| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include "UiCanvasComponent.h"
- #include "UiElementComponent.h"
- #include "UiTransform2dComponent.h"
- #include "UiSerialize.h"
- #include "UiCanvasFileObject.h"
- #include "UiGameEntityContext.h"
- #include "UiNavigationHelpers.h"
- #include "UiRenderer.h"
- #include "LyShine.h"
- #include <Random.h>
- #include <CryFile.h>
- #include <LyShine/Bus/UiInteractableBus.h>
- #include <LyShine/Bus/UiInitializationBus.h>
- #include <LyShine/Bus/UiNavigationBus.h>
- #include <LyShine/Bus/UiTooltipDisplayBus.h>
- #include <LyShine/Bus/UiEntityContextBus.h>
- #include <LyShine/Bus/UiCanvasUpdateNotificationBus.h>
- #include <LyShine/UiSerializeHelpers.h>
- #include <LyShine/IDraw2d.h>
- #include <AzCore/Math/Crc.h>
- #include <AzCore/Memory/Memory.h>
- #include <AzCore/Component/ComponentApplicationBus.h>
- #include <AzCore/Component/EntityUtils.h>
- #include <AzCore/Serialization/SerializeContext.h>
- #include <AzCore/Serialization/EditContext.h>
- #include <AzCore/Serialization/Utils.h>
- #include <AzCore/Asset/AssetSerializer.h>
- #include <AzCore/RTTI/BehaviorContext.h>
- #include <AzCore/IO/SystemFile.h>
- #include <AzCore/std/time.h>
- #include <AzCore/std/string/conversions.h>
- #include <AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h>
- #include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
- #include <AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.h>
- #include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
- #include <AzFramework/Input/Devices/Touch/InputDeviceTouch.h>
- #include <Atom/RPI.Public/Image/ImageSystemInterface.h>
- #include <Atom/RPI.Public/Image/AttachmentImagePool.h>
- #include "Animation/UiAnimationSystem.h"
- #ifndef _RELEASE
- #include <LyShine/Bus/UiRenderBus.h>
- #include <LyShine/Bus/UiRenderControlBus.h>
- #include <LyShine/Bus/UiImageBus.h>
- #include <LyShine/Bus/UiTextBus.h>
- #include <LyShine/Bus/UiMaskBus.h>
- #include <LyShine/Bus/UiFaderBus.h>
- #endif
- #include "LyShinePassDataBus.h"
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- //! UiCanvasNotificationBus Behavior context handler class
- class UiCanvasNotificationBusBehaviorHandler
- : public UiCanvasNotificationBus::Handler
- , public AZ::BehaviorEBusHandler
- {
- public:
- AZ_EBUS_BEHAVIOR_BINDER(UiCanvasNotificationBusBehaviorHandler, "{64014B4F-E12F-4839-99B0-426B5717DB44}", AZ::SystemAllocator,
- OnAction,
- OnActionMultitouch,
- OnEnableStateChanged);
- void OnAction(AZ::EntityId entityId, const LyShine::ActionName& actionName) override
- {
- Call(FN_OnAction, entityId, actionName);
- }
-
- void OnActionMultitouch(AZ::EntityId entityId, const LyShine::ActionName& actionName, const AZ::Vector2& position, int multitouchIndex) override
- {
- Call(FN_OnActionMultitouch, entityId, actionName, position, multitouchIndex);
- }
- void OnEnableStateChanged(AZ::EntityId canvasEntityId, bool enabled) override
- {
- Call(FN_OnEnableStateChanged, canvasEntityId, enabled);
- }
- };
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- //! UiCanvasInputNotificationBus Behavior context handler class
- class UiCanvasInputNotificationBusBehaviorHandler
- : public UiCanvasInputNotificationBus::Handler
- , public AZ::BehaviorEBusHandler
- {
- public:
- AZ_EBUS_BEHAVIOR_BINDER(UiCanvasInputNotificationBusBehaviorHandler, "{76042EFA-0B61-4E7A-ACC8-296382D46881}", AZ::SystemAllocator,
- OnCanvasPrimaryPressed, OnCanvasPrimaryReleased, OnCanvasMultiTouchPressed, OnCanvasMultiTouchReleased, OnCanvasHoverStart, OnCanvasHoverEnd, OnCanvasEnterPressed, OnCanvasEnterReleased);
- void OnCanvasPrimaryPressed(AZ::EntityId entityId) override
- {
- Call(FN_OnCanvasPrimaryPressed, entityId);
- }
- void OnCanvasPrimaryReleased(AZ::EntityId entityId) override
- {
- Call(FN_OnCanvasPrimaryReleased, entityId);
- }
- void OnCanvasMultiTouchPressed(AZ::EntityId entityId, int multiTouchIndex) override
- {
- Call(FN_OnCanvasMultiTouchPressed, entityId, multiTouchIndex);
- }
- void OnCanvasMultiTouchReleased(AZ::EntityId entityId, int multiTouchIndex) override
- {
- Call(FN_OnCanvasMultiTouchReleased, entityId, multiTouchIndex);
- }
- void OnCanvasHoverStart(AZ::EntityId entityId) override
- {
- Call(FN_OnCanvasHoverStart, entityId);
- }
- void OnCanvasHoverEnd(AZ::EntityId entityId) override
- {
- Call(FN_OnCanvasHoverEnd, entityId);
- }
- void OnCanvasEnterPressed(AZ::EntityId entityId) override
- {
- Call(FN_OnCanvasEnterPressed, entityId);
- }
- void OnCanvasEnterReleased(AZ::EntityId entityId) override
- {
- Call(FN_OnCanvasEnterReleased, entityId);
- }
- };
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- //! UiAnimationNotificationBus Behavior context handler class
- class UiAnimationNotificationBusBehaviorHandler
- : public UiAnimationNotificationBus::Handler
- , public AZ::BehaviorEBusHandler
- {
- public:
- AZ_EBUS_BEHAVIOR_BINDER(UiAnimationNotificationBusBehaviorHandler, "{35D19FE8-5F31-426E-877A-8EEF3A42F99F}", AZ::SystemAllocator,
- OnUiAnimationEvent, OnUiTrackEvent);
- void OnUiAnimationEvent(IUiAnimationListener::EUiAnimationEvent uiAnimationEvent, AZStd::string animSequenceName) override
- {
- Call(FN_OnUiAnimationEvent, uiAnimationEvent, animSequenceName);
- }
- void OnUiTrackEvent(AZStd::string eventName, AZStd::string valueName, AZStd::string animSequenceName) override
- {
- Call(FN_OnUiTrackEvent, eventName, valueName, animSequenceName);
- }
- };
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- //! UiInitializationBus Behavior context handler class
- class UiInitializationBusBehaviorHandler
- : public UiInitializationBus::Handler
- , public AZ::BehaviorEBusHandler
- {
- public:
- AZ_EBUS_BEHAVIOR_BINDER(UiInitializationBusBehaviorHandler, "{2978A8A2-1A88-40C2-A299-ECA68AD1C519}", AZ::SystemAllocator,
- InGamePostActivate);
- void InGamePostActivate() override
- {
- Call(FN_InGamePostActivate);
- }
- };
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- //! UiCanvasEnabledStateNotificationBus Behavior context handler class
- class UiCanvasEnabledStateNotificationBusBehaviorHandler
- : public UiCanvasEnabledStateNotificationBus::Handler
- , public AZ::BehaviorEBusHandler
- {
- public:
- AZ_EBUS_BEHAVIOR_BINDER(UiCanvasEnabledStateNotificationBusBehaviorHandler, "{CF010D8C-AC69-4F2A-BE87-03432E68A304}", AZ::SystemAllocator,
- OnCanvasEnabledStateChanged);
- void OnCanvasEnabledStateChanged(AZ::EntityId canvasEntityId, bool enabled) override
- {
- Call(FN_OnCanvasEnabledStateChanged, canvasEntityId, enabled);
- }
- };
- // Anonymous namespace
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- namespace
- {
- LyShine::CanvasId s_lastCanvasId = 0;
- ////////////////////////////////////////////////////////////////////////////////////////////////
- // test if the given text file starts with the given text string
- bool TestFileStartString(const AZStd::string& pathname, const char* expectedStart)
- {
- // Open the file using CCryFile, this supports it being in the pak file or a standalone file
- CCryFile file;
- if (!file.Open(pathname.c_str(), "r"))
- {
- return false;
- }
- // get the size of the file and the length of the expected start string
- size_t fileSize = file.GetLength();
- size_t expectedStartLen = strlen(expectedStart);
- // if the file is smaller than the expected start string then it is not a valid file
- if (fileSize < expectedStartLen)
- {
- return false;
- }
- // read in the length of the expected start string
- char* buffer = new char[expectedStartLen];
- file.ReadRaw(buffer, expectedStartLen);
- // match is true if the string read from the file matches the expected start string
- bool match = strncmp(expectedStart, buffer, expectedStartLen) == 0;
- delete [] buffer;
- return match;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////
- // Check if the given file was saved using AZ serialization
- bool IsValidAzSerializedFile(const AZStd::string& pathname)
- {
- return TestFileStartString(pathname, "<ObjectStream");
- }
- template<class T, class MapType>
- void ReuseOrGenerateNewIdsAndFixRefs(T* object, MapType& newIdMap, AZ::SerializeContext* context)
- {
- AZ::EntityUtils::ReplaceEntityIds(
- object,
- [&newIdMap](const AZ::EntityId& originalId, bool /*isEntityId*/) -> AZ::EntityId
- {
- auto findIt = newIdMap.find(originalId);
- if (findIt == newIdMap.end())
- {
- AZ::EntityId newId = AZ::Entity::MakeId();
- newIdMap.insert(AZStd::make_pair(originalId, newId));
- return newId;
- }
- else
- {
- return findIt->second; // return the previously remapped id
- }
- }, context);
- AZ::EntityUtils::ReplaceEntityRefs(
- object,
- [&newIdMap](const AZ::EntityId& originalId, bool /*isEntityId*/) -> AZ::EntityId
- {
- auto findIt = newIdMap.find(originalId);
- if (findIt == newIdMap.end())
- {
- return originalId; // entityId is not being remapped
- }
- else
- {
- return findIt->second; // return the remapped id
- }
- }, context);
- }
- UiRenderer* GetUiRendererForGame()
- {
- if (AZ::Interface<ILyShine>::Get())
- {
- CLyShine* lyShine = static_cast<CLyShine*>(AZ::Interface<ILyShine>::Get());
- return lyShine->GetUiRenderer();
- }
- return nullptr;
- }
- UiRenderer* GetUiRendererForEditor()
- {
- if (AZ::Interface<ILyShine>::Get())
- {
- CLyShine* lyShine = static_cast<CLyShine*>(AZ::Interface<ILyShine>::Get());
- return lyShine->GetUiRendererForEditor();
- }
- return nullptr;
- }
- bool IsValidInteractable(const AZ::EntityId& entityId)
- {
- if (!entityId.IsValid())
- {
- return false;
- }
- // Check if element is enabled
- bool isEnabled = false;
- UiElementBus::EventResult(isEnabled, entityId, &UiElementBus::Events::IsEnabled);
- if (!isEnabled)
- {
- return false;
- }
- // Check if element is handling events and therefore also an interactable
- bool canHandleEvents = false;
- UiInteractableBus::EventResult(canHandleEvents, entityId, &UiInteractableBus::Events::IsHandlingEvents);
- return canHandleEvents;
- }
- bool IsAttachmentImageAssetCompatible(const AZ::Data::Asset<AZ::RPI::AttachmentImageAsset>& attachmentImageAsset)
- {
- // Check that the attachment image asset's bind flags are compatible
- // - Supports use as a color attachment on a scope
- // - Supports read access through a ShaderResourceGroup
- const AZ::RHI::ImageDescriptor& imageDescriptor = attachmentImageAsset->GetImageDescriptor();
- bool isCompatible = AZ::RHI::CheckBitsAll(imageDescriptor.m_bindFlags,
- AZ::RHI::ImageBindFlags::Color | AZ::RHI::ImageBindFlags::ShaderRead);
- if (!isCompatible)
- {
- AZ_Error("UI", false, "Attachment image asset: %s has invalid bindings for rendering a UI Canvas. Please "
- "ensure that its BindFlags property includes Color and ShaderRead.", attachmentImageAsset.GetHint().c_str());
- return false;
- }
- // Check that the attachment image asset's pixel format is compatible
- isCompatible = (imageDescriptor.m_format == AZ::RHI::Format::R8G8B8A8_UNORM)
- || (imageDescriptor.m_format == AZ::RHI::Format::R8G8B8A8_UNORM_SRGB);
- if (!isCompatible)
- {
- AZ_Error("UI", false, "Attachment image asset: %s has an invalid pixel format for rendering a "
- "UI Canvas. Please ensure that it contains a Format property with a valid value of "
- "19 (R8G8B8A8_UNORM) or 20 (R8G8B8A8_UNORM_SRGB).", attachmentImageAsset.GetHint().c_str());
- return false;
- }
- return true;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- // STATIC MEMBER DATA
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- const AZ::Vector2 UiCanvasComponent::s_defaultCanvasSize(1280.0f, 720.0f);
- const AZ::Color UiCanvasComponent::s_defaultGuideColor(0.25f,1.0f,0.25f,1.0f);
- bool UiCanvasComponent::s_handleHoverInputEvents = true;
- bool UiCanvasComponent::s_allowClearingHoverInteractableOnHoverInput = true;
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- // PUBLIC MEMBER FUNCTIONS
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- UiCanvasComponent::UiCanvasComponent()
- : m_uniqueId(0)
- , m_rootElement()
- , m_lastElementId(0)
- , m_canvasToViewportMatrix(AZ::Matrix4x4::CreateIdentity())
- , m_viewportToCanvasMatrix(AZ::Matrix4x4::CreateIdentity())
- , m_activeInteractableShouldStayActive(false)
- , m_isActiveInteractablePressed(false)
- , m_lastMousePosition(-1.0f, -1.0f)
- , m_id(++s_lastCanvasId)
- , m_drawOrder(0)
- , m_canvasSize(UiCanvasComponent::s_defaultCanvasSize)
- , m_targetCanvasSize(m_canvasSize)
- , m_deviceScale(1.0f, 1.0f)
- , m_isLoadedInGame(false)
- , m_keepLoadedOnLevelUnload(false)
- , m_enabled(true)
- , m_renderToTexture(false)
- , m_isSnapEnabled(false)
- , m_snapDistance(10.0f)
- , m_snapRotationDegrees(10.0f)
- , m_guideColor(s_defaultGuideColor)
- , m_guidesAreLocked(false)
- , m_entityContext(nullptr)
- {
- m_navCommandStatus[UiNavigationHelpers::Command::Up] = {0, 0, true};
- m_navCommandStatus[UiNavigationHelpers::Command::Down] = {0, 0, true};
- m_navCommandStatus[UiNavigationHelpers::Command::Left] = {0, 0, true};
- m_navCommandStatus[UiNavigationHelpers::Command::Right] = {0, 0, true};
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- UiCanvasComponent::~UiCanvasComponent()
- {
- DestroyScheduledElements();
- m_uiAnimationSystem.RemoveAllSequences();
- // remove all entries from m_elementsNeedingTransformRecompute list, can't use clear since that doesn't set the
- // m_next pointers to null except in a debug build.
- while (!m_elementsNeedingTransformRecompute.empty())
- {
- UiElementComponent& elementComponent = m_elementsNeedingTransformRecompute.front();
- m_elementsNeedingTransformRecompute.pop_front();
- elementComponent.m_next = nullptr; // needed in order to be able to test if an element is in the list.
- }
- if (m_entityContext)
- {
- // deactivate all UI elements, this is so that we can detect improper deletion of UI elements by users
- // during game play
- DeactivateElements();
- // Destroy the entity context, this will delete all the UI elements
- m_entityContext->DestroyUiContext();
- }
- if (m_isLoadedInGame)
- {
- delete m_entityContext;
- }
- // Unload any active texture atlases
- UnloadAtlases();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- const AZStd::string& UiCanvasComponent::GetPathname()
- {
- return m_pathname;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- LyShine::CanvasId UiCanvasComponent::GetCanvasId()
- {
- return m_id;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::u64 UiCanvasComponent::GetUniqueCanvasId()
- {
- return m_uniqueId;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- int UiCanvasComponent::GetDrawOrder()
- {
- return m_drawOrder;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetDrawOrder(int drawOrder)
- {
- m_drawOrder = drawOrder;
- UiCanvasOrderNotificationBus::Broadcast(&UiCanvasOrderNotificationBus::Events::OnCanvasDrawOrderChanged, GetEntityId());
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::GetKeepLoadedOnLevelUnload()
- {
- return m_keepLoadedOnLevelUnload;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetKeepLoadedOnLevelUnload(bool keepLoaded)
- {
- m_keepLoadedOnLevelUnload = keepLoaded;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::RecomputeChangedLayouts()
- {
- SendRectChangeNotificationsAndRecomputeLayouts();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- int UiCanvasComponent::GetNumChildElements()
- {
- int numChildElements = 0;
- UiElementBus::EventResult(numChildElements, m_rootElement, &UiElementBus::Events::GetNumChildElements);
- return numChildElements;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::Entity* UiCanvasComponent::GetChildElement(int index)
- {
- AZ::Entity* child = nullptr;
- UiElementBus::EventResult(child, m_rootElement, &UiElementBus::Events::GetChildElement, index);
- return child;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::EntityId UiCanvasComponent::GetChildElementEntityId(int index)
- {
- AZ::EntityId childEntityId;
- UiElementBus::EventResult(childEntityId, m_rootElement, &UiElementBus::Events::GetChildEntityId, index);
- return childEntityId;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- LyShine::EntityArray UiCanvasComponent::GetChildElements()
- {
- LyShine::EntityArray childElements;
- UiElementBus::EventResult(childElements, m_rootElement, &UiElementBus::Events::GetChildElements);
- return childElements;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZStd::vector<AZ::EntityId> UiCanvasComponent::GetChildElementEntityIds()
- {
- AZStd::vector<AZ::EntityId> childElementEntityIds;
- UiElementBus::EventResult(childElementEntityIds, m_rootElement, &UiElementBus::Events::GetChildEntityIds);
- return childElementEntityIds;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::Entity* UiCanvasComponent::CreateChildElement(const LyShine::NameType& name)
- {
- AZ::Entity* child = nullptr;
- UiElementBus::EventResult(child, m_rootElement, &UiElementBus::Events::CreateChildElement, name);
- return child;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::Entity* UiCanvasComponent::FindElementById(LyShine::ElementId id)
- {
- AZ::Entity* element = nullptr;
- UiElementBus::EventResult(element, m_rootElement, &UiElementBus::Events::FindDescendantById, id);
- return element;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::Entity* UiCanvasComponent::FindElementByName(const LyShine::NameType& name)
- {
- AZ::Entity* entity = nullptr;
- UiElementBus::EventResult(entity, m_rootElement, &UiElementBus::Events::FindDescendantByName, name);
- return entity;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::EntityId UiCanvasComponent::FindElementEntityIdByName(const LyShine::NameType& name)
- {
- AZ::EntityId entityId;
- UiElementBus::EventResult(entityId, m_rootElement, &UiElementBus::Events::FindDescendantEntityIdByName, name);
- return entityId;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::FindElementsByName(const LyShine::NameType& name, LyShine::EntityArray& result)
- {
- // find all elements with the given name
- UiElementBus::Event(
- m_rootElement,
- &UiElementBus::Events::FindDescendantElements,
- [&name](const AZ::Entity* entity)
- {
- return name == entity->GetName();
- },
- result);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::Entity* UiCanvasComponent::FindElementByHierarchicalName(const LyShine::NameType& name)
- {
- // start at the root
- AZ::Entity* currentEntity = GetRootElement();
- bool found = false;
- std::size_t lastPos = 0;
- while (currentEntity)
- {
- std::size_t pos = name.find('/', lastPos);
- if (pos == lastPos)
- {
- // skip over any double '/' characters or '/' characters at the start
- lastPos++;
- }
- else if (pos == LyShine::NameType::npos)
- {
- // '/' not found, use whole remaining string
- AZ::Entity* entity = nullptr;
- UiElementBus::EventResult(entity, currentEntity->GetId(), &UiElementBus::Events::FindChildByName, name.substr(lastPos));
- currentEntity = entity;
- if (currentEntity)
- {
- found = true;
- }
- break;
- }
- else
- {
- // use the part of the string between lastPos and pos (between the '/' characters)
- AZ::Entity* entity = nullptr;
- UiElementBus::EventResult(
- entity, currentEntity->GetId(), &UiElementBus::Events::FindChildByName, name.substr(lastPos, pos - lastPos));
- currentEntity = entity;
- lastPos = pos + 1;
- }
- }
- return (found) ? currentEntity : nullptr;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::FindElements(AZStd::function<bool(const AZ::Entity*)> predicate, LyShine::EntityArray& result)
- {
- // find all matching elements
- UiElementBus::Event(m_rootElement, &UiElementBus::Events::FindDescendantElements, predicate, result);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::Entity* UiCanvasComponent::PickElement(AZ::Vector2 point)
- {
- AZ::Entity* element = nullptr;
- UiElementBus::EventResult(element, m_rootElement, &UiElementBus::Events::FindFrontmostChildContainingPoint, point, m_isLoadedInGame);
- return element;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- LyShine::EntityArray UiCanvasComponent::PickElements(const AZ::Vector2& bound0, const AZ::Vector2& bound1)
- {
- LyShine::EntityArray elements;
- UiElementBus::EventResult(
- elements, m_rootElement, &UiElementBus::Events::FindAllChildrenIntersectingRect, bound0, bound1, m_isLoadedInGame);
- return elements;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::EntityId UiCanvasComponent::FindInteractableToHandleEvent(AZ::Vector2 point)
- {
- AZ::EntityId interactable;
- UiElementBus::EventResult(interactable, m_rootElement, &UiElementBus::Events::FindInteractableToHandleEvent, point);
- return interactable;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::SaveToXml(const AZStd::string& assetIdPathname, const AZStd::string& sourceAssetPathname)
- {
- PrepareAnimationSystemForCanvasSave();
- // We are saving to the dev assets (source) not the cache so we use the sourceAssetPathname to save
- // the file
- bool result = SaveCanvasToFile(sourceAssetPathname, AZ::ObjectStream::ST_XML);
- if (result)
- {
- // We store the asset ID so that we can tell if the same file is being loaded from the game
- m_pathname = assetIdPathname;
- }
- return result;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::FixupCreatedEntities(LyShine::EntityArray topLevelEntities, bool makeUniqueNamesAndIds, AZ::Entity* optionalInsertionPoint)
- {
- if (makeUniqueNamesAndIds)
- {
- AZ::EntityId parentEntityId;
- if (optionalInsertionPoint)
- {
- parentEntityId = optionalInsertionPoint->GetId();
- }
- LyShine::EntityArray namedChildren;
- for (auto entity : topLevelEntities)
- {
- AZStd::string uniqueName = GetUniqueChildName(parentEntityId, entity->GetName(), &namedChildren);
- entity->SetName(uniqueName);
- namedChildren.push_back(entity);
- }
- }
- AZ::Entity* parent = (optionalInsertionPoint) ? optionalInsertionPoint : GetRootElement();
- for (auto entity : topLevelEntities)
- {
- UiElementComponent* elementComponent = entity->FindComponent<UiElementComponent>();
- AZ_Assert(elementComponent, "No element component found on prefab entity");
- // recursively visit all the elements and set their canvas and parent pointers
- elementComponent->FixupPostLoad(entity, this, parent, makeUniqueNamesAndIds);
- }
- if (m_isLoadedInGame)
- {
- // Call InGamePostActivate on all the created entities
- for (auto entity : topLevelEntities)
- {
- InGamePostActivateBottomUp(entity);
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::AddElement(AZ::Entity* element, AZ::Entity* parent, AZ::Entity* insertBefore)
- {
- if (!parent)
- {
- parent = GetRootElement();
- }
- // add this new entity as a child of the parent (insertionPoint or root)
- UiElementComponent* parentElementComponent = parent->FindComponent<UiElementComponent>();
- AZ_Assert(parentElementComponent, "No element component found on parent entity");
- parentElementComponent->AddChild(element, insertBefore);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::ReinitializeElements()
- {
- // This gets called when a canvas or a slice in the canvas is reloaded. So, for example,
- // a Push to Slice in the editor causes a reload of that slice. It is only used in the editor.
- AZ::Entity* rootElement = GetRootElement();
- UiElementComponent* elementComponent = rootElement->FindComponent<UiElementComponent>();
- AZ_Assert(elementComponent, "No element component found on root element entity");
- elementComponent->FixupPostLoad(rootElement, this, nullptr, false);
- // All or some elements in the UI canvas have been recreated when ReinitializeElements is called.
- // This likely requires recompute of the transforms (in particular UiTextComponent requires this
- // if text is being wrapped, due to its delayed initialization that relies on OnCanvasSpaceRectChanged
- // being called).
- UiTransformBus::Event(m_rootElement, &UiTransformBus::Events::SetRecomputeFlags, UiTransformInterface::Recompute::RectAndTransform);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZStd::string UiCanvasComponent::SaveToXmlString()
- {
- PrepareAnimationSystemForCanvasSave();
- AZStd::string charBuffer;
- AZ::IO::ByteContainerStream<AZStd::string > charStream(&charBuffer);
- [[maybe_unused]] bool success = SaveCanvasToStream(charStream, AZ::ObjectStream::ST_XML);
- AZ_Assert(success, "Failed to serialize canvas entity to XML");
- return charBuffer;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZStd::string UiCanvasComponent::GetUniqueChildName(AZ::EntityId parentEntityId, AZStd::string baseName, const LyShine::EntityArray* includeChildren)
- {
- // Get a list of children that the name needs to be unique to
- LyShine::EntityArray children;
- if (parentEntityId.IsValid())
- {
- UiElementBus::EventResult(children, parentEntityId, &UiElementBus::Events::GetChildElements);
- }
- else
- {
- children = GetChildElements();
- }
- if (includeChildren)
- {
- children.insert(children.end(),includeChildren->begin(),includeChildren->end());
- }
- // First, check if base name is unique
- if (IsElementNameUnique(baseName, children))
- {
- return baseName;
- }
- // Count trailing digits in base name
- int i;
- for (i = static_cast<int>(baseName.length() - 1); i >= 0; i--)
- {
- if (!isdigit(baseName[i]))
- {
- break;
- }
- }
- int startDigitIndex = i + 1;
- int numDigits = static_cast<int>(baseName.length() - startDigitIndex);
- int suffix = 1;
- if (numDigits > 0)
- {
- // Set starting suffix
- suffix = AZStd::stoi(baseName.substr(startDigitIndex, numDigits));
- // Trim the digits from the base name
- baseName.erase(startDigitIndex, numDigits);
- }
- // Keep incrementing suffix until a unique name is found
- // NOTE: This could cause a performance issue when large copies are being made in a large canvas
- AZStd::string proposedChildName;
- do
- {
- ++suffix;
- proposedChildName = baseName;
- AZStd::string suffixString = AZStd::string::format("%d", suffix);
- // Append leading zeros
- int numLeadingZeros = static_cast<int>((suffixString.length() < numDigits) ? numDigits - suffixString.length() : 0);
- for (int zeroes = 0; zeroes < numLeadingZeros; zeroes++)
- {
- proposedChildName.push_back('0');
- }
- // Append suffix
- proposedChildName.append(suffixString);
- } while (!IsElementNameUnique(proposedChildName, children));
- return proposedChildName;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::Entity* UiCanvasComponent::CloneElement(AZ::Entity* sourceEntity, AZ::Entity* parentEntity)
- {
- return CloneAndAddElementInternal(sourceEntity, parentEntity, nullptr);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::EntityId UiCanvasComponent::CloneElementEntityId(AZ::EntityId sourceEntityId, AZ::EntityId parentEntityId, AZ::EntityId insertBeforeId)
- {
- AZ::EntityId result;
- AZ::Entity* sourceEntity = nullptr;
- AZ::ComponentApplicationBus::BroadcastResult(sourceEntity, &AZ::ComponentApplicationBus::Events::FindEntity, sourceEntityId);
- if (!sourceEntity)
- {
- AZ_Warning("UI", false, "CloneElementEntityId: Cannot find entity to clone.");
- return result;
- }
- AZ::Entity* parentEntity = nullptr;
- if (parentEntityId.IsValid())
- {
- AZ::ComponentApplicationBus::BroadcastResult(parentEntity, &AZ::ComponentApplicationBus::Events::FindEntity, parentEntityId);
- if (!parentEntity)
- {
- AZ_Warning("UI", false, "CloneElementEntityId: Cannot find parent entity.");
- return result;
- }
- }
- else
- {
- parentEntity = GetRootElement();
- }
- AZ::Entity* insertBeforeEntity = nullptr;
- if (insertBeforeId.IsValid())
- {
- AZ::ComponentApplicationBus::BroadcastResult(insertBeforeEntity, &AZ::ComponentApplicationBus::Events::FindEntity, insertBeforeId);
- if (!insertBeforeEntity)
- {
- AZ_Warning("UI", false, "CloneElementEntityId: Cannot find insertBefore entity.");
- return result;
- }
- }
- AZ::Entity* clonedEntity = CloneAndAddElementInternal(sourceEntity, parentEntity, insertBeforeEntity);
- if (clonedEntity)
- {
- result = clonedEntity->GetId();
- }
- return result;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::Entity* UiCanvasComponent::CloneCanvas(const AZ::Vector2& canvasSize)
- {
- UiGameEntityContext* entityContext = new UiGameEntityContext();
- UiCanvasComponent* canvasComponent = CloneAndInitializeCanvas(entityContext, m_pathname, &canvasSize);
- AZ::Entity* newCanvasEntity = nullptr;
- if (canvasComponent)
- {
- newCanvasEntity = canvasComponent->GetEntity();
- canvasComponent->m_isLoadedInGame = true;
- // The game entity context needs to know its corresponding canvas entity for instantiating dynamic slices
- entityContext->SetCanvasEntity(newCanvasEntity->GetId());
- }
- else
- {
- delete entityContext;
- }
- return newCanvasEntity;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetCanvasToViewportMatrix(const AZ::Matrix4x4& matrix)
- {
- if (!m_canvasToViewportMatrix.IsClose(matrix))
- {
- m_canvasToViewportMatrix = matrix;
- m_viewportToCanvasMatrix = m_canvasToViewportMatrix.GetInverseTransform();
- UiTransformBus::Event(
- GetRootElement()->GetId(), &UiTransformBus::Events::SetRecomputeFlags, UiTransformInterface::Recompute::ViewportTransformOnly);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- const AZ::Matrix4x4& UiCanvasComponent::GetCanvasToViewportMatrix()
- {
- return m_canvasToViewportMatrix;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::GetViewportToCanvasMatrix(AZ::Matrix4x4& matrix)
- {
- matrix = m_viewportToCanvasMatrix;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::Vector2 UiCanvasComponent::GetCanvasSize()
- {
- return m_targetCanvasSize;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::Vector2 UiCanvasComponent::GetAuthoredCanvasSize()
- {
- return m_canvasSize;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetCanvasSize(const AZ::Vector2& canvasSize)
- {
- m_canvasSize = canvasSize;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetTargetCanvasSize(bool isInGame, const AZ::Vector2& targetCanvasSize)
- {
- if (m_renderToTexture)
- {
- // When a canvas is set to render to texture the target canvas size is retrieved from the asset.
- // This is typically the same size as the authored canvas size
- AZ::Vector2 targetSize(m_canvasSize);
- if (m_attachmentImageAsset)
- {
- AZ::RHI::Size imageSize = m_attachmentImageAsset->GetImageDescriptor().m_size;
- targetSize = AZ::Vector2(aznumeric_cast<float>(imageSize.m_width), aznumeric_cast<float>(imageSize.m_height));
- }
- SetTargetCanvasSizeAndUniformScale(isInGame, targetSize);
- }
- else
- {
- SetTargetCanvasSizeAndUniformScale(isInGame, targetCanvasSize);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::Vector2 UiCanvasComponent::GetDeviceScale()
- {
- return m_deviceScale;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::GetIsPixelAligned()
- {
- return m_isPixelAligned;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetIsPixelAligned(bool isPixelAligned)
- {
- m_isPixelAligned = isPixelAligned;
- UiCanvasPixelAlignmentNotificationBus::Event(
- GetEntityId(), &UiCanvasPixelAlignmentNotificationBus::Events::OnCanvasPixelAlignmentChange);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::GetIsTextPixelAligned()
- {
- return m_isTextPixelAligned;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetIsTextPixelAligned(bool isTextPixelAligned)
- {
- m_isTextPixelAligned = isTextPixelAligned;
- UiCanvasPixelAlignmentNotificationBus::Event(
- GetEntityId(), &UiCanvasPixelAlignmentNotificationBus::Events::OnCanvasTextPixelAlignmentChange);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- IUiAnimationSystem* UiCanvasComponent::GetAnimationSystem()
- {
- return &m_uiAnimationSystem;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::GetEnabled()
- {
- return m_enabled;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetEnabled(bool enabled)
- {
- if (m_enabled != enabled)
- {
- m_enabled = enabled;
- MarkRenderGraphDirty();
- EBUS_EVENT(UiCanvasNotificationBus, OnEnableStateChanged, GetEntityId(), m_enabled);
- UiCanvasEnabledStateNotificationBus::Broadcast(
- &UiCanvasEnabledStateNotificationBus::Events::OnCanvasEnabledStateChanged, GetEntityId(), m_enabled);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::GetIsRenderToTexture()
- {
- return m_renderToTexture;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetIsRenderToTexture(bool isRenderToTexture)
- {
- m_renderToTexture = isRenderToTexture;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- const AZ::Data::Asset<AZ::RPI::AttachmentImageAsset>& UiCanvasComponent::GetAttachmentImageAsset()
- {
- return m_attachmentImageAsset;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetAttachmentImageAsset(const AZ::Data::Asset<AZ::RPI::AttachmentImageAsset>& attachmentImageAsset)
- {
- if (m_attachmentImageAsset != attachmentImageAsset)
- {
- DestroyRenderTarget();
- m_attachmentImageAsset = attachmentImageAsset;
- MarkRenderGraphDirty();
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::GetIsPositionalInputSupported()
- {
- return m_isPositionalInputSupported;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetIsPositionalInputSupported(bool isSupported)
- {
- m_isPositionalInputSupported = isSupported;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::GetIsConsumingAllInputEvents()
- {
- return m_isConsumingAllInputEvents;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetIsConsumingAllInputEvents(bool isConsuming)
- {
- m_isConsumingAllInputEvents = isConsuming;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::GetIsMultiTouchSupported()
- {
- return m_isMultiTouchSupported;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetIsMultiTouchSupported(bool isSupported)
- {
- m_isMultiTouchSupported = isSupported;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::GetIsNavigationSupported()
- {
- return m_isNavigationSupported;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetIsNavigationSupported(bool isSupported)
- {
- m_isNavigationSupported = isSupported;
- SetFirstHoverInteractable();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- float UiCanvasComponent::GetNavigationThreshold()
- {
- return m_navigationThreshold;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetNavigationThreshold(float navigationThreshold)
- {
- m_navigationThreshold = navigationThreshold;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::u64 UiCanvasComponent::GetNavigationRepeatDelay()
- {
- return m_navigationRepeatDelay;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetNavigationRepeatDelay(AZ::u64 navigationRepeatDelay)
- {
- m_navigationRepeatDelay = navigationRepeatDelay;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::u64 UiCanvasComponent::GetNavigationRepeatPeriod()
- {
- return m_navigationRepeatPeriod;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetNavigationRepeatPeriod(AZ::u64 navigationRepeatPeriod)
- {
- m_navigationRepeatPeriod = navigationRepeatPeriod;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AzFramework::LocalUserId UiCanvasComponent::GetLocalUserIdInputFilter()
- {
- return m_localUserIdInputFilter;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetLocalUserIdInputFilter(AzFramework::LocalUserId localUserId)
- {
- m_localUserIdInputFilter = localUserId;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::HandleInputEvent(const AzFramework::InputChannel::Snapshot& inputSnapshot,
- const AZ::Vector2* viewportPos,
- AzFramework::ModifierKeyMask activeModifierKeys)
- {
- // Ignore input events if we're not enabled
- if (!m_enabled)
- {
- return false;
- }
- if (m_localUserIdInputFilter != AzFramework::LocalUserIdAny &&
- m_localUserIdInputFilter != inputSnapshot.m_localUserId)
- {
- // Ignore input events if they were not generated by the desired local user id
- return false;
- }
- if (inputSnapshot.m_channelId == AzFramework::InputDeviceMouse::Movement::X ||
- inputSnapshot.m_channelId == AzFramework::InputDeviceMouse::Movement::Y ||
- inputSnapshot.m_channelId == AzFramework::InputDeviceMouse::Movement::Z)
- {
- // Ignore the individual mouse movement input channels.
- // X, Y are handled through the SystemCursorPosition input channel.
- // Z (scroll wheel) functionality is not currently supported on the canvas level
- return m_isConsumingAllInputEvents;
- }
- if (AzFramework::InputDeviceKeyboard::IsKeyboardDevice(inputSnapshot.m_deviceId) ||
- AzFramework::InputDeviceVirtualKeyboard::IsVirtualKeyboardDevice(inputSnapshot.m_deviceId) ||
- AzFramework::InputDeviceGamepad::IsGamepadDevice(inputSnapshot.m_deviceId))
- {
- return HandleKeyInputEvent(inputSnapshot, activeModifierKeys) || m_isConsumingAllInputEvents;
- }
- else if (viewportPos)
- {
- if (!m_renderToTexture && m_isPositionalInputSupported)
- {
- if (HandleInputPositionalEvent(inputSnapshot, *viewportPos))
- {
- return true;
- }
- }
- }
- return !m_renderToTexture && m_isConsumingAllInputEvents;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::HandleTextEvent(const AZStd::string& textUTF8)
- {
- // Ignore input events if we're not enabled
- if (!m_enabled)
- {
- return false;
- }
- if (m_activeInteractable.IsValid())
- {
- UiInteractableBus::Event(m_activeInteractable, &UiInteractableBus::Events::HandleTextInput, textUTF8);
- return true;
- }
- return false;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::HandleInputPositionalEvent(const AzFramework::InputChannel::Snapshot& inputSnapshot,
- AZ::Vector2 viewportPos)
- {
- if (AzFramework::InputDeviceMouse::IsMouseDevice(inputSnapshot.m_deviceId))
- {
- if (m_lastMousePosition != viewportPos)
- {
- // Check if the mouse position has been initialized
- if (m_lastMousePosition.GetX() >= 0.0f && m_lastMousePosition.GetY() >= 0.0f)
- {
- // Mouse moved, resume handling hover input events if there is no active interactable
- if (!m_activeInteractable.IsValid())
- {
- s_handleHoverInputEvents = true;
- }
- }
- m_lastMousePosition = viewportPos;
- }
- }
- //Well treat the first finger input as the 'mouse position'
- if (inputSnapshot.m_channelId == AzFramework::InputDeviceTouch::Touch::Index0)
- {
- if (m_lastMousePosition != viewportPos)
- {
- m_lastMousePosition = viewportPos;
- }
- }
- // Currently we are just interested in mouse events and the primary touch for hover events
- if (AzFramework::InputDeviceMouse::IsMouseDevice(inputSnapshot.m_deviceId) ||
- inputSnapshot.m_channelId == AzFramework::InputDeviceTouch::Touch::Index0)
- {
- if (s_handleHoverInputEvents)
- {
- HandleHoverInputEvent(viewportPos);
- }
- }
- // Currently we are just interested in mouse button 1 events and UI events here
- if (inputSnapshot.m_channelId == AzFramework::InputDeviceMouse::Button::Left ||
- inputSnapshot.m_channelId == AzFramework::InputDeviceTouch::Touch::Index0)
- {
- if (inputSnapshot.m_state == AzFramework::InputChannel::State::Began)
- {
- return HandlePrimaryPress(viewportPos);
- }
- else if (inputSnapshot.m_state == AzFramework::InputChannel::State::Ended)
- {
- if (inputSnapshot.m_channelId == AzFramework::InputDeviceTouch::Touch::Index0)
- {
- ClearHoverInteractable();
- }
- return HandlePrimaryRelease(viewportPos);
- }
- else if (inputSnapshot.m_state == AzFramework::InputChannel::State::Updated)
- {
- return HandlePrimaryUpdate(viewportPos);
- }
- }
- // ...while all other events from touch devices should be treated as multi-touch
- else if (AzFramework::InputDeviceTouch::IsTouchDevice(inputSnapshot.m_deviceId))
- {
- const auto& touchIndexIt = AZStd::find(AzFramework::InputDeviceTouch::Touch::All.cbegin(),
- AzFramework::InputDeviceTouch::Touch::All.cend(),
- inputSnapshot.m_channelId);
- if (touchIndexIt != AzFramework::InputDeviceTouch::Touch::All.cend())
- {
- const int touchindex = static_cast<int>(touchIndexIt - AzFramework::InputDeviceTouch::Touch::All.cbegin());
- if (inputSnapshot.m_state == AzFramework::InputChannel::State::Began)
- {
- return HandleMultiTouchPress(viewportPos, touchindex);
- }
- else if (inputSnapshot.m_state == AzFramework::InputChannel::State::Ended)
- {
- return HandleMultiTouchRelease(viewportPos, touchindex);
- }
- else if (inputSnapshot.m_state == AzFramework::InputChannel::State::Updated)
- {
- return HandleMultiTouchUpdated(viewportPos, touchindex);
- }
- }
- }
- return false;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::Vector2 UiCanvasComponent::GetMousePosition()
- {
- return m_lastMousePosition;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::EntityId UiCanvasComponent::GetTooltipDisplayElement()
- {
- return m_tooltipDisplayElement;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetTooltipDisplayElement(AZ::EntityId entityId)
- {
- m_tooltipDisplayElement = entityId;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::ForceFocusInteractable(AZ::EntityId interactableId)
- {
- if (interactableId.IsValid())
- {
- AZ::EntityId lastHoverInteractable = m_hoverInteractable;
- // Force the interactable to have the hover. Will also auto activate the
- // interactable if the flag is set
- ForceHoverInteractable(interactableId);
- // Will also set as active interactable
- CheckHoverInteractableAndAutoActivate(lastHoverInteractable, UiNavigationHelpers::Command::Unknown, true);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::ForceActiveInteractable(AZ::EntityId interactableId, bool shouldStayActive, AZ::Vector2 point)
- {
- SetHoverInteractable(interactableId);
- SetActiveInteractable(interactableId, shouldStayActive);
- m_lastMousePosition = point;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::EntityId UiCanvasComponent::GetHoverInteractable()
- {
- return m_hoverInteractable;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::ForceHoverInteractable(AZ::EntityId newHoverInteractable)
- {
- if (!m_isNavigationSupported)
- {
- AZ_Warning("UI", false, "This UI canvas does not support keyboard/gamepad input events");
- return;
- }
- if (newHoverInteractable.IsValid())
- {
- // Make sure the element is an interactable that is handling events
- if (!IsValidInteractable(newHoverInteractable))
- {
- AZ_Warning("UI", false, "Entity is either not an interactable, not enabled or is not accepting events");
- return;
- }
- // Make sure the active interactable and the hover interactible are the same
- if (m_activeInteractable.IsValid() && (m_activeInteractable != newHoverInteractable))
- {
- ClearActiveInteractable();
- }
- }
- SetHoverInteractable(newHoverInteractable);
- if (m_hoverInteractable.IsValid())
- {
- s_handleHoverInputEvents = false;
- s_allowClearingHoverInteractableOnHoverInput = false;
- AZ::EntityId ancestorInteractable = FindAncestorInteractable(m_hoverInteractable);
- if (ancestorInteractable.IsValid())
- {
- // Send an event that the descendant interactable became the hover interactable via navigation
- UiInteractableBus::Event(
- ancestorInteractable, &UiInteractableBus::Events::HandleDescendantReceivedHoverByNavigation, m_hoverInteractable);
- }
- CheckHoverInteractableAndAutoActivate();
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::ClearAllInteractables()
- {
- m_multiTouchInteractablesByTouchIndex.clear();
- ClearActiveInteractable();
- // Clear hover interactable if last input was positional (mouse/touch)
- if (s_handleHoverInputEvents)
- {
- ClearHoverInteractable();
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::ForceEnterInputEventOnInteractable(AZ::EntityId interactableId)
- {
- if (!m_isNavigationSupported)
- {
- AZ_Warning("UI", false, "This UI canvas does not support keyboard/gamepad input events");
- return;
- }
- if (!interactableId.IsValid())
- {
- AZ_Warning("UI", false, "EntityId is not valid");
- return;
- }
- // Make sure the element is an interactable that is handling events
- if (!IsValidInteractable(interactableId))
- {
- AZ_Warning("UI", false, "Entity is either not an interactable, not enabled or is not accepting events");
- return;
- }
- // Set the hover interactable to accept the events
- if (m_hoverInteractable != interactableId)
- {
- ForceHoverInteractable(interactableId);
- }
- // Generate Enter key pressed input event
- AzFramework::InputChannel::Snapshot snapshot(AzFramework::InputDeviceKeyboard::Key::EditEnter,
- AzFramework::InputDeviceKeyboard::Id,
- AzFramework::InputChannel::State::Began);
- HandleEnterInputEvent(UiNavigationHelpers::Command::Enter, snapshot);
- // Generate Enter key released input event
- snapshot.m_state = AzFramework::InputChannel::State::Ended;
- HandleEnterInputEvent(UiNavigationHelpers::Command::Enter, snapshot);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::LoadAtlases()
- {
- if (m_atlases.size() > 0)
- {
- // Atlases already loaded
- return;
- }
- for (int i = 0; i < m_atlasPathNames.size(); ++i)
- {
- const AZStd::string& atlasAssetPath = m_atlasPathNames[i].GetAssetPath();
- if (!atlasAssetPath.empty()) // no need to print an error message for empty strings
- {
- TextureAtlasNamespace::TextureAtlas* atlas = nullptr;
- TextureAtlasNamespace::TextureAtlasRequestBus::BroadcastResult(atlas, &TextureAtlasNamespace::TextureAtlasRequests::LoadAtlas, atlasAssetPath);
- if (atlas != nullptr)
- {
- m_atlases.push_back(atlas);
- }
- else
- {
- AZ_Error("UI", false, "UI canvas: %s failed to load texture atlas: %s", m_pathname.c_str(), atlasAssetPath.c_str());
- }
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::UnloadAtlases()
- {
- while (m_atlases.size() > 0)
- {
- TextureAtlasNamespace::TextureAtlasRequestBus::Broadcast(&TextureAtlasNamespace::TextureAtlasRequests::UnloadAtlas, m_atlases.back());
- m_atlases.pop_back();
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::ReloadAtlases()
- {
- UnloadAtlases();
- LoadAtlases();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::OnEntityDeactivated(const AZ::EntityId& entityId)
- {
- AZ::EntityBus::Handler::BusDisconnect(entityId);
- if (entityId == m_hoverInteractable)
- {
- ClearHoverInteractable();
- // If we are using keyboard/gamepad navigation we should set a new hover interactable
- SetFirstHoverInteractable();
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::StartSequence(const AZStd::string& sequenceName)
- {
- IUiAnimSequence* sequence = m_uiAnimationSystem.FindSequence(sequenceName.c_str());
- if (sequence)
- {
- m_uiAnimationSystem.AddUiAnimationListener(sequence, this);
- m_uiAnimationSystem.PlaySequence(sequence, nullptr, false, false);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::PlaySequenceRange(const AZStd::string& sequenceName, float startTime, float endTime)
- {
- IUiAnimSequence* sequence = m_uiAnimationSystem.FindSequence(sequenceName.c_str());
- if (sequence)
- {
- m_uiAnimationSystem.AddUiAnimationListener(sequence, this);
- m_uiAnimationSystem.PlaySequence(sequence, nullptr, false, false, startTime, endTime);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::StopSequence(const AZStd::string& sequenceName)
- {
- IUiAnimSequence* sequence = m_uiAnimationSystem.FindSequence(sequenceName.c_str());
- if (sequence)
- {
- m_uiAnimationSystem.StopSequence(sequence);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::AbortSequence(const AZStd::string& sequenceName)
- {
- IUiAnimSequence* sequence = m_uiAnimationSystem.FindSequence(sequenceName.c_str());
- if (sequence)
- {
- m_uiAnimationSystem.AbortSequence(sequence);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::PauseSequence(const AZStd::string& sequenceName)
- {
- IUiAnimSequence* sequence = m_uiAnimationSystem.FindSequence(sequenceName.c_str());
- if (sequence)
- {
- sequence->Pause();
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::ResumeSequence(const AZStd::string& sequenceName)
- {
- IUiAnimSequence* sequence = m_uiAnimationSystem.FindSequence(sequenceName.c_str());
- if (sequence)
- {
- sequence->Resume();
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::ResetSequence(const AZStd::string& sequenceName)
- {
- IUiAnimSequence* sequence = m_uiAnimationSystem.FindSequence(sequenceName.c_str());
- if (sequence)
- {
- sequence->Reset(true);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- float UiCanvasComponent::GetSequencePlayingSpeed(const AZStd::string& sequenceName)
- {
- IUiAnimSequence* sequence = m_uiAnimationSystem.FindSequence(sequenceName.c_str());
- return m_uiAnimationSystem.GetPlayingSpeed(sequence);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetSequencePlayingSpeed(const AZStd::string& sequenceName, float speed)
- {
- IUiAnimSequence* sequence = m_uiAnimationSystem.FindSequence(sequenceName.c_str());
- m_uiAnimationSystem.SetPlayingSpeed(sequence, speed);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- float UiCanvasComponent::GetSequencePlayingTime(const AZStd::string& sequenceName)
- {
- IUiAnimSequence* sequence = m_uiAnimationSystem.FindSequence(sequenceName.c_str());
- return m_uiAnimationSystem.GetPlayingTime(sequence);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::IsSequencePlaying(const AZStd::string& sequenceName)
- {
- IUiAnimSequence* sequence = m_uiAnimationSystem.FindSequence(sequenceName.c_str());
- if (sequence)
- {
- return m_uiAnimationSystem.IsPlaying(sequence);
- }
- return false;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- float UiCanvasComponent::GetSequenceLength(const AZStd::string& sequenceName)
- {
- float length = 0.f;
- IUiAnimSequence* sequence = m_uiAnimationSystem.FindSequence(sequenceName.c_str());
- if (sequence)
- {
- auto range = sequence->GetTimeRange();
- length = range.Length();
- }
- return length;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetSequenceStopBehavior(IUiAnimationSystem::ESequenceStopBehavior stopBehavior)
- {
- m_uiAnimationSystem.SetSequenceStopBehavior(stopBehavior);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::ActiveCancelled()
- {
- // currently we are only connected to one UiInteractableActiveNotificationBus so we know it is the
- // pressed interactable. If we could be connected to several we would need to change
- // the ActiveCancelled method to pass the EntityId.
- if (m_activeInteractable.IsValid())
- {
- UiInteractableActiveNotificationBus::Handler::BusDisconnect(m_activeInteractable);
- m_activeInteractable.SetInvalid();
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- // change the active interactable to the given one
- void UiCanvasComponent::ActiveChanged(AZ::EntityId m_newActiveInteractable, bool shouldStayActive)
- {
- // There should always be an active interactable at this point, disconnect from it
- if (m_activeInteractable.IsValid())
- {
- UiInteractableActiveNotificationBus::Handler::BusDisconnect(m_activeInteractable);
- m_activeInteractable.SetInvalid();
- }
- // The m_newActiveInteractable should always be valid but check anyway
- if (m_newActiveInteractable.IsValid())
- {
- m_activeInteractable = m_newActiveInteractable;
- UiInteractableActiveNotificationBus::Handler::BusConnect(m_activeInteractable);
- m_activeInteractableShouldStayActive = shouldStayActive;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::OnUiAnimationEvent(EUiAnimationEvent uiAnimationEvent, IUiAnimSequence* pAnimSequence)
- {
- // Queue the event to prevent deletions during the canvas update
- UiAnimationNotificationBus::QueueEvent(
- GetEntityId(), &UiAnimationNotificationBus::Events::OnUiAnimationEvent, uiAnimationEvent, pAnimSequence->GetName());
- // Stop listening to events
- if ((uiAnimationEvent == EUiAnimationEvent::eUiAnimationEvent_Stopped) || (uiAnimationEvent == EUiAnimationEvent::eUiAnimationEvent_Aborted))
- {
- m_uiAnimationSystem.RemoveUiAnimationListener(pAnimSequence, this);
- }
- }
- void UiCanvasComponent::OnUiTrackEvent(AZStd::string eventName, AZStd::string valueName, IUiAnimSequence* pAnimSequence)
- {
- // Queue the event to prevent deletions during the canvas update
- UiAnimationNotificationBus::QueueEvent(
- GetEntityId(), &UiAnimationNotificationBus::Events::OnUiTrackEvent, eventName, valueName, pAnimSequence->GetName());
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::GetIsSnapEnabled()
- {
- return m_isSnapEnabled;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetIsSnapEnabled(bool enabled)
- {
- m_isSnapEnabled = enabled;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- float UiCanvasComponent::GetSnapDistance()
- {
- return m_snapDistance;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetSnapDistance(float distance)
- {
- m_snapDistance = distance;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- float UiCanvasComponent::GetSnapRotationDegrees()
- {
- return m_snapRotationDegrees;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetSnapRotationDegrees(float degrees)
- {
- m_snapRotationDegrees = degrees;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZStd::vector<float> UiCanvasComponent::GetHorizontalGuidePositions()
- {
- return m_horizontalGuidePositions;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::AddHorizontalGuide(float position)
- {
- m_horizontalGuidePositions.push_back(position);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::RemoveHorizontalGuide(int index)
- {
- if (index < m_horizontalGuidePositions.size())
- {
- m_horizontalGuidePositions.erase(m_horizontalGuidePositions.begin() + index);
- }
- else
- {
- AZ_Warning("UI", false, "Index out of range in RemoveHorizontalGuide");
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetHorizontalGuidePosition(int index, float position)
- {
- if (index < m_horizontalGuidePositions.size())
- {
- m_horizontalGuidePositions[index] = position;
- }
- else
- {
- AZ_Warning("UI", false, "Index out of range in SetHorizontalGuidePosition");
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZStd::vector<float> UiCanvasComponent::GetVerticalGuidePositions()
- {
- return m_verticalGuidePositions;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::AddVerticalGuide(float position)
- {
- m_verticalGuidePositions.push_back(position);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::RemoveVerticalGuide(int index)
- {
- if (index < m_verticalGuidePositions.size())
- {
- m_verticalGuidePositions.erase(m_verticalGuidePositions.begin() + index);
- }
- else
- {
- AZ_Warning("UI", false, "Index out of range in RemoveVerticalGuide");
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetVerticalGuidePosition(int index, float position)
- {
- if (index < m_verticalGuidePositions.size())
- {
- m_verticalGuidePositions[index] = position;
- }
- else
- {
- AZ_Warning("UI", false, "Index out of range in SetVerticalGuidePosition");
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::RemoveAllGuides()
- {
- m_horizontalGuidePositions.clear();
- m_verticalGuidePositions.clear();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::Color UiCanvasComponent::GetGuideColor()
- {
- return m_guideColor;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetGuideColor(const AZ::Color& color)
- {
- m_guideColor = color;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::GetGuidesAreLocked()
- {
- return m_guidesAreLocked;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetGuidesAreLocked(bool areLocked)
- {
- m_guidesAreLocked = areLocked;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::CheckForOrphanedElements()
- {
- AZ::SliceComponent::EntityList orphanedEntities;
- GetOrphanedElements(orphanedEntities);
- return !orphanedEntities.empty();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::RecoverOrphanedElements()
- {
- AZ::SliceComponent::EntityList orphanedEntities;
- GetOrphanedElements(orphanedEntities);
- // we will put the orphaned elements under a top-level element called this:
- const char* recoveredOrphansName = "RecoveredOrphans";
- // If the recovered orphans element does not already at the top-level of the canvas exist then create it
- AZ::Entity* recoveredOrphansElement = FindElementByHierarchicalName(recoveredOrphansName);
- if (!recoveredOrphansElement)
- {
- recoveredOrphansElement = CreateChildElement(recoveredOrphansName);
- recoveredOrphansElement->Deactivate();
- recoveredOrphansElement->CreateComponent(LyShine::UiTransform2dComponentUuid);
- recoveredOrphansElement->Activate();
- }
- // We have to find the top-level elements within the orphans and add them as children of recoveredOrphansElement.
- // First we make a set of all the orphans that are referenced as children of other orphans.
- AZStd::unordered_set<AZ::EntityId> referencedChildren;
- for (AZ::Entity* orphan : orphanedEntities)
- {
- UiElementComponent* orphanElementComponent = orphan->FindComponent<UiElementComponent>();
- int numChildren = orphanElementComponent->GetNumChildElements();
- for (int i = 0; i < numChildren; ++i)
- {
- AZ::EntityId childId = orphanElementComponent->GetChildEntityId(i);
- referencedChildren.insert(childId);
- }
- }
- // Any orphans that are not in the set are top-level orphans and should be added.
- UiElementComponent* recoveredOrphansElementComponent = recoveredOrphansElement->FindComponent<UiElementComponent>();
- for (AZ::Entity* orphan : orphanedEntities)
- {
- if (referencedChildren.find(orphan->GetId()) == referencedChildren.end())
- {
- // First add the orphan as a child of the recoveredOrphansElement
- recoveredOrphansElementComponent->AddChild(orphan);
- // Then fixup all the parent, canvas, child pointers in the orphan and its children
- UiElementComponent* orphanElementComponent = orphan->FindComponent<UiElementComponent>();
- orphanElementComponent->FixupPostLoad(orphan, this, recoveredOrphansElement, false);
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::RemoveOrphanedElements()
- {
- // get the orphaned entities
- AZ::SliceComponent::EntityList orphanedEntities;
- GetOrphanedElements(orphanedEntities);
- // remove the entities from the entity context, this will remove any slice instances and references that become empty
- for (AZ::Entity* orphan : orphanedEntities)
- {
- m_entityContext->DestroyEntity(orphan);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::UpdateCanvasInEditorViewport(float deltaTime, bool isInGame)
- {
- UpdateCanvas(deltaTime, isInGame);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::RenderCanvasInEditorViewport(bool isInGame, AZ::Vector2 viewportSize)
- {
- // When isInGame is true we're rendering the canvas in UI Editor's Preview Mode
- UiRenderer* uiRenderer = GetUiRendererForEditor();
- AZ_Assert(uiRenderer, "Trying to render a canvas in the UI Editor before its UIRenderer has been initialized");
- uiRenderer->BeginUiFrameRender();
- RenderCanvas(isInGame, viewportSize, uiRenderer);
- uiRenderer->EndUiFrameRender();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::MarkRenderGraphDirty()
- {
- // It is possible that the loading screen can result in this being called while we are already
- // rendering this canvas. We never want to set the dirty flag while rendering, if the dirty
- // flag is not already set it could result in an incomplete renderGraph being created since
- // the render graph will be cleared.
- if (!m_isRendering)
- {
- m_renderGraph.SetDirtyFlag(true);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::RHI::AttachmentId UiCanvasComponent::UseRenderTarget(const AZ::Name& renderTargetName, AZ::RHI::Size size)
- {
- // Create a render target that UI elements will render to
- AZ::RPI::CreateAttachmentImageRequest createImageRequest;
- AZ::RHI::ImageDescriptor& imageDesc = createImageRequest.m_imageDescriptor;
- imageDesc.m_bindFlags = AZ::RHI::ImageBindFlags::Color | AZ::RHI::ImageBindFlags::ShaderReadWrite;
- imageDesc.m_size = size;
- imageDesc.m_format = AZ::RHI::Format::R8G8B8A8_UNORM;
- createImageRequest.m_imagePool = AZ::RPI::ImageSystemInterface::Get()->GetSystemAttachmentPool().get();
- createImageRequest.m_imageName = renderTargetName;
- createImageRequest.m_isUniqueName = false; // Can't use an unique name since the previous render target's release is delayed
- auto attachmentImage = AZ::RPI::AttachmentImage::Create(createImageRequest);
- if (!attachmentImage)
- {
- AZ_Warning("UI", false, "Failed to create render target");
- return AZ::RHI::AttachmentId();
- }
- m_attachmentImageMap[attachmentImage->GetAttachmentId()] = attachmentImage;
- // Notify LyShine render pass that it needs to rebuild
- QueueRttPassRebuild();
- return attachmentImage->GetAttachmentId();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::RHI::AttachmentId UiCanvasComponent::UseRenderTargetAsset(const AZ::Data::Asset<AZ::RPI::AttachmentImageAsset>& attachmentImageAsset)
- {
- AZ::RHI::AttachmentId attachmentId = AZ::RHI::AttachmentId{};
- if (!attachmentImageAsset.GetId().IsValid())
- {
- AZ_Error("UI", false, "UI Canvas (%s) using invalid attachment image! ", m_pathname.c_str());
- return attachmentId;
- }
- // Check that the attachment image asset's properties are compatible for rendering a UI canvas
- if (IsAttachmentImageAssetCompatible(attachmentImageAsset))
- {
- // Instantiate or get an existing image instance for the specified asset
- if (auto attachmentImage = AZ::RPI::AttachmentImage::FindOrCreate(attachmentImageAsset))
- {
- attachmentId = attachmentImage->GetAttachmentId();
- m_attachmentImageMap[attachmentId] = attachmentImage;
- // Notify LyShine render pass that it needs to rebuild
- QueueRttPassRebuild();
- }
- else
- {
- AZ_Error("UI", false, "Failed to find or create render target from asset: %s",
- attachmentImageAsset.GetHint().c_str());
- }
- }
- return attachmentId;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::ReleaseRenderTarget(const AZ::RHI::AttachmentId& attachmentId)
- {
- m_attachmentImageMap.erase(attachmentId);
- // Notify LyShine render pass that it needs to rebuild
- QueueRttPassRebuild();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::Data::Instance<AZ::RPI::AttachmentImage> UiCanvasComponent::GetRenderTarget(const AZ::RHI::AttachmentId& attachmentId)
- {
- return m_attachmentImageMap[attachmentId];
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::UpdateCanvas(float deltaTime, bool isInGame)
- {
- // Ignore update if we're not enabled
- if (!m_enabled)
- {
- return;
- }
- if (isInGame)
- {
- UiCanvasUpdateNotificationBus::Event(GetEntityId(), &UiCanvasUpdateNotificationBus::Events::Update, deltaTime);
- // update the animation system
- m_uiAnimationSystem.PreUpdate(deltaTime);
- m_uiAnimationSystem.PostUpdate(deltaTime);
- }
- else
- {
- UiCanvasUpdateNotificationBus::Event(GetEntityId(), &UiCanvasUpdateNotificationBus::Events::UpdateInEditor, deltaTime);
- }
- DestroyScheduledElements();
- SendRectChangeNotificationsAndRecomputeLayouts();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::RenderCanvas(bool isInGame, AZ::Vector2 viewportSize, UiRenderer* uiRenderer)
- {
- // Ignore render ops if we're not enabled
- if (!m_enabled)
- {
- return;
- }
- m_renderInEditor = uiRenderer ? true : false;
- if (!uiRenderer)
- {
- uiRenderer = GetUiRendererForGame();
- }
- // It is possible, due to the LoadScreenComponent, for this canvas to have Render called while it is rendering.
- // This is rare but can happen because rendering of text can call FontCreateTexture which results in CreateTextureObject
- // being called, which has a load scren update in it. Rendering the canvas to the render graph while already in the
- // process of doing so can corrupt the render graph by adding an element to an intrusive list that is already in the
- // list.
- // We could prevent this at the CLyShine::Render level. But doing it here with an m_isRendering flag also allows us to
- // check for the error condition where MarkRenderGraphDirty (which clears the render graph) is called during rendering.
- if (m_isRendering)
- {
- return;
- }
- m_isRendering = true;
- if (m_renderGraph.GetDirtyFlag())
- {
- m_renderGraph.ResetGraph();
- bool renderToTexture = !m_renderInEditor && GetIsRenderToTexture();
- if (renderToTexture)
- {
- if (m_attachmentImageId.IsEmpty())
- {
- CreateRenderTarget();
- }
- // we always clear to transparent black - the accumulation of alpha in the render target requires it
- AZ::Color clearColor = AZ::Color::CreateZero();
- // Start building the render to texture node in the render graph
- AZ::Vector2 viewportTopLeft = AZ::Vector2::CreateZero();
- AZ::Data::Instance<AZ::RPI::AttachmentImage> attachmentImage;
- LyShine::RenderToTextureRequestBus::EventResult(attachmentImage, GetEntityId(),
- &LyShine::RenderToTextureRequestBus::Events::GetRenderTarget, m_attachmentImageId);
- m_renderGraph.BeginRenderToTexture(attachmentImage, viewportTopLeft, viewportSize, clearColor);
- }
- else
- {
- if (!m_attachmentImageId.IsEmpty())
- {
- DestroyRenderTarget();
- }
- }
- UiElementBus::Event(m_rootElement, &UiElementBus::Events::RenderElement, &m_renderGraph, isInGame);
- if (renderToTexture)
- {
- // finish building the render to texture node in the render graph
- m_renderGraph.EndRenderToTexture();
- }
- m_renderGraph.SetDirtyFlag(false);
- m_renderGraph.FinalizeGraph();
- }
- if (!m_renderGraph.IsEmpty())
- {
- if (!m_attachmentImageMap.empty())
- {
- // Process any queued pass changes to get rtt draw list tags in sync prior to adding draw calls
- AZ::RPI::PassSystemInterface* passSystem = AZ::RPI::PassSystemInterface::Get();
- passSystem->ProcessQueuedChanges();
- }
- uiRenderer->BeginCanvasRender();
- m_renderGraph.Render(uiRenderer, viewportSize);
- uiRenderer->EndCanvasRender();
- }
- m_isRendering = false;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::Entity* UiCanvasComponent::GetRootElement() const
- {
- AZ::Entity* rootEntity = nullptr;
- AZ::ComponentApplicationBus::BroadcastResult(rootEntity, &AZ::ComponentApplicationBus::Events::FindEntity, m_rootElement);
- return rootEntity;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- LyShine::ElementId UiCanvasComponent::GenerateId()
- {
- return ++m_lastElementId;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::Vector2 UiCanvasComponent::GetTargetCanvasSize()
- {
- return m_targetCanvasSize;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::ScheduleElementForTransformRecompute(UiElementComponent* elementComponent)
- {
- // Do not add if already in the list
- if (!elementComponent->m_next)
- {
- m_elementsNeedingTransformRecompute.push_back(*elementComponent);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::UnscheduleElementForTransformRecompute(UiElementComponent* elementComponent)
- {
- // Do not erase if not in list
- if (elementComponent->m_next)
- {
- m_elementsNeedingTransformRecompute.erase(ElementComponentSlist::const_iterator_impl(elementComponent));
- elementComponent->m_next = nullptr;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::ScheduleElementDestroy(AZ::EntityId entityId)
- {
- m_elementsScheduledForDestroy.push_back(entityId);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::GetRenderTargets(LyShine::AttachmentImagesAndDependencies& attachmentImagesAndDependencies)
- {
- m_renderGraph.GetRenderTargetsAndDependencies(attachmentImagesAndDependencies);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::DestroyScheduledElements()
- {
- for (auto entityId : m_elementsScheduledForDestroy)
- {
- UiElementComponent::DestroyElementEntity(entityId);
- }
- m_elementsScheduledForDestroy.clear();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::QueueRttPassRebuild()
- {
- UiRenderer* uiRenderer = m_renderInEditor ? GetUiRendererForEditor() : GetUiRendererForGame();
- if (uiRenderer && uiRenderer->GetViewportContext()) // can be null in automated testing
- {
- if (const AZ::RPI::ScenePtr& scene = uiRenderer->GetViewportContext()->GetRenderScene())
- {
- AZ::RPI::SceneId sceneId = scene->GetId();
- LyShinePassRequestBus::Event(sceneId, &LyShinePassRequestBus::Events::RebuildRttChildren);
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- #ifndef _RELEASE
- void UiCanvasComponent::GetDebugInfoInteractables(AZ::EntityId& activeInteractable, AZ::EntityId& hoverInteractable) const
- {
- activeInteractable = m_activeInteractable;
- hoverInteractable = m_hoverInteractable;
- }
- void UiCanvasComponent::GetDebugInfoNumElements(DebugInfoNumElements& info) const
- {
- info.m_numElements = 0;
- info.m_numEnabledElements = 0;
- info.m_numRenderElements = 0;
- info.m_numRenderControlElements = 0;
- info.m_numImageElements = 0;
- info.m_numTextElements = 0;
- info.m_numMaskElements = 0;
- info.m_numFaderElements = 0;
- info.m_numInteractableElements = 0;
- info.m_numUpdateElements = static_cast<int>(UiCanvasUpdateNotificationBus::GetNumOfEventHandlers(GetEntityId()));
- DebugInfoCountChildren(m_rootElement, true, info);
- }
- void UiCanvasComponent::GetDebugInfoRenderGraph(LyShineDebug::DebugInfoRenderGraph& info) const
- {
- m_renderGraph.GetDebugInfoRenderGraph(info);
- }
- void UiCanvasComponent::DebugInfoCountChildren(const AZ::EntityId entity, bool parentEnabled, DebugInfoNumElements& info) const
- {
- int numChildElements = 0;
- UiElementBus::EventResult(numChildElements, entity, &UiElementBus::Events::GetNumChildElements);
- info.m_numElements += numChildElements;
- for (int i = 0; i < numChildElements; ++i)
- {
- AZ::EntityId child;
- UiElementBus::EventResult(child, entity, &UiElementBus::Events::GetChildEntityId, i);
- bool isEnabled = false;
- UiElementBus::EventResult(isEnabled, child, &UiElementBus::Events::IsEnabled);
- if (isEnabled && parentEnabled)
- {
- ++info.m_numEnabledElements;
- if (UiRenderBus::FindFirstHandler(child))
- {
- ++info.m_numRenderElements;
- }
- if (UiRenderControlBus::FindFirstHandler(child))
- {
- ++info.m_numRenderControlElements;
- }
- if (UiImageBus::FindFirstHandler(child))
- {
- ++info.m_numImageElements;
- }
- if (UiTextBus::FindFirstHandler(child))
- {
- ++info.m_numTextElements;
- }
- if (UiMaskBus::FindFirstHandler(child))
- {
- ++info.m_numMaskElements;
- }
- if (UiFaderBus::FindFirstHandler(child))
- {
- ++info.m_numFaderElements;
- }
- if (UiInteractableBus::FindFirstHandler(child))
- {
- ++info.m_numInteractableElements;
- }
- }
- DebugInfoCountChildren(child, isEnabled && parentEnabled, info);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::DebugReportDrawCalls(AZ::IO::HandleType fileHandle, LyShineDebug::DebugInfoDrawCallReport& reportInfo, void* context) const
- {
- m_renderGraph.DebugReportDrawCalls(fileHandle, reportInfo, context);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::DebugDisplayElemBounds(IDraw2d* draw2d) const
- {
- DebugDisplayChildElemBounds(draw2d, m_rootElement);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::DebugDisplayChildElemBounds(IDraw2d* draw2d, const AZ::EntityId entity) const
- {
- AZ::u64 time = AZStd::GetTimeUTCMilliSecond();
- uint32 fractionsOfOneSecond = time % 1000;
- uint32 fractionsOfHalfSecond = (fractionsOfOneSecond > 500) ? 1000 - fractionsOfOneSecond : fractionsOfOneSecond;
- float brightness = fractionsOfHalfSecond / 500.0f;
- UiTransformInterface::RectPoints points;
- int numChildElements = 0;
- UiElementBus::EventResult(numChildElements, entity, &UiElementBus::Events::GetNumChildElements);
- for (int i = 0; i < numChildElements; ++i)
- {
- AZ::EntityId child;
- UiElementBus::EventResult(child, entity, &UiElementBus::Events::GetChildEntityId, i);
- bool isEnabled = false;
- UiElementBus::EventResult(isEnabled, child, &UiElementBus::Events::IsEnabled);
- if (isEnabled)
- {
- UiTransformBus::Event(entity, &UiTransformBus::Events::GetViewportSpacePoints, points);
- AZ::Color color(brightness, brightness, brightness, 1.0f);
- draw2d->DrawLine(points.TopLeft(), points.TopRight(), color);
- draw2d->DrawLine(points.TopRight(), points.BottomRight(), color);
- draw2d->DrawLine(points.BottomRight(), points.BottomLeft(), color);
- draw2d->DrawLine(points.BottomLeft(), points.TopLeft(), color);
- DebugDisplayChildElemBounds(draw2d, child);
- }
- }
- }
- #endif
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- // PUBLIC STATIC MEMBER FUNCTIONS
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::Reflect(AZ::ReflectContext* context)
- {
- AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
- if (serializeContext)
- {
- UiAnimationSystem::Reflect(serializeContext);
- serializeContext->Class<UiCanvasComponent, AZ::Component>()
- ->Version(4, &VersionConverter)
- // Not in properties pane
- ->Field("UniqueId", &UiCanvasComponent::m_uniqueId)
- ->Field("RootElement", &UiCanvasComponent::m_rootElement)
- ->Field("LastElement", &UiCanvasComponent::m_lastElementId)
- ->Field("CanvasSize", &UiCanvasComponent::m_canvasSize)
- ->Field("IsSnapEnabled", &UiCanvasComponent::m_isSnapEnabled)
- // Rendering group
- ->Field("DrawOrder", &UiCanvasComponent::m_drawOrder)
- ->Field("IsPixelAligned", &UiCanvasComponent::m_isPixelAligned)
- ->Field("IsTextPixelAligned", &UiCanvasComponent::m_isTextPixelAligned)
- ->Field("RenderToTexture", &UiCanvasComponent::m_renderToTexture)
- ->Field("AttachmentImageAsset", &UiCanvasComponent::m_attachmentImageAsset)
- // Input group
- ->Field("IsPosInputSupported", &UiCanvasComponent::m_isPositionalInputSupported)
- ->Field("IsConsumingAllInput", &UiCanvasComponent::m_isConsumingAllInputEvents)
- ->Field("IsMultiTouchSupported", &UiCanvasComponent::m_isMultiTouchSupported)
- ->Field("IsNavigationSupported", &UiCanvasComponent::m_isNavigationSupported)
- ->Field("NavigationThreshold", &UiCanvasComponent::m_navigationThreshold)
- ->Field("NavigationRepeatDelay", &UiCanvasComponent::m_navigationRepeatDelay)
- ->Field("NavigationRepeatPeriod", &UiCanvasComponent::m_navigationRepeatPeriod)
- ->Field("FirstHoverElement", &UiCanvasComponent::m_firstHoverInteractable)
- ->Field("AnimSystem", &UiCanvasComponent::m_uiAnimationSystem)
- ->Field("AnimationData", &UiCanvasComponent::m_serializedAnimationData)
- // Tooltips group
- ->Field("TooltipDisplayElement", &UiCanvasComponent::m_tooltipDisplayElement)
- // Editor settings
- ->Field("SnapDistance", &UiCanvasComponent::m_snapDistance)
- ->Field("SnapRotationDegrees", &UiCanvasComponent::m_snapRotationDegrees)
- ->Field("HorizontalGuides", &UiCanvasComponent::m_horizontalGuidePositions)
- ->Field("VerticalGuides", &UiCanvasComponent::m_verticalGuidePositions)
- ->Field("GuideColor", &UiCanvasComponent::m_guideColor)
- ->Field("GuidesLocked", &UiCanvasComponent::m_guidesAreLocked)
- // Texture Atlases
- ->Field("TextureAtlases", &UiCanvasComponent::m_atlasPathNames)
- ;
- // Old SimpleAssetReference<TextureAtlasAsset> TypeId = {6F612FE6-A054-4E49-830C-0288F3C79A52}
- // Performs a sha1 calculation of the following typeids AzFramework::SimpleAssetReference<TextureAtlasAsset> + AZStd::allocator + AZStd::vector
- AZ::TypeId deprecatedTypeId = AZ::TypeId("{6F612FE6-A054-4E49-830C-0288F3C79A52}") + AZ::AzTypeInfo<AZStd::allocator>::Uuid()
- + AZ::TypeId("{A60E3E61-1FF6-4982-B6B8-9E4350C4C679}");
- serializeContext->ClassDeprecate("AZStd::vector<SimpleAssetReference_TextureAtlasAsset>", deprecatedTypeId,
- [](AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& rootElement)
- {
- AZStd::vector<AZ::SerializeContext::DataElementNode> childNodeElements;
- for (int index = 0; index < rootElement.GetNumSubElements(); ++index)
- {
- childNodeElements.push_back(rootElement.GetSubElement(index));
- }
- rootElement.Convert<AZStd::vector<AzFramework::SimpleAssetReference<TextureAtlasNamespace::TextureAtlasAsset>>>(context);
- for (AZ::SerializeContext::DataElementNode& childNodeElement : childNodeElements)
- {
- rootElement.AddElement(AZStd::move(childNodeElement));
- }
- return true;
- });
- AZ::EditContext* ec = serializeContext->GetEditContext();
- if (ec)
- {
- auto editInfo = ec->Class<UiCanvasComponent>("UI Canvas", "These are the properties of the UI canvas.");
- editInfo->ClassElement(AZ::Edit::ClassElements::EditorData, "")
- ->Attribute(AZ::Edit::Attributes::AddableByUser, false)
- ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/UiCanvas.png")
- ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/UiCanvas.png")
- ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
- editInfo->ClassElement(AZ::Edit::ClassElements::Group, "Rendering")
- ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
- editInfo->DataElement(AZ::Edit::UIHandlers::Default, &UiCanvasComponent::m_drawOrder, "Draw order", "The order, relative to other canvases, in which this canvas will draw (higher numbers on top).");
- editInfo->DataElement(AZ::Edit::UIHandlers::CheckBox, &UiCanvasComponent::m_isPixelAligned, "Is pixel aligned", "When checked, all corners of all elements will be rounded to the nearest pixel.")
- ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiCanvasComponent::OnPixelAlignmentChange);
- editInfo->DataElement(AZ::Edit::UIHandlers::CheckBox, &UiCanvasComponent::m_isTextPixelAligned, "Is text pixel aligned", "When checked, all text will be rounded to the nearest pixel.")
- ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiCanvasComponent::OnTextPixelAlignmentChange);
- editInfo->DataElement(AZ::Edit::UIHandlers::CheckBox, &UiCanvasComponent::m_renderToTexture, "Render to texture", "When checked, the canvas is rendered to a texture instead of the full screen.")
- ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshEntireTree", 0xefbc823c));
- editInfo->DataElement(AZ::Edit::UIHandlers::Default, &UiCanvasComponent::m_attachmentImageAsset, "Render Target", "The asset for the attachment image that this canvas renders to.")
- ->Attribute(AZ::Edit::Attributes::Visibility, &UiCanvasComponent::m_renderToTexture);
- editInfo->ClassElement(AZ::Edit::ClassElements::Group, "Input")
- ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
- editInfo->DataElement(AZ::Edit::UIHandlers::CheckBox, &UiCanvasComponent::m_isPositionalInputSupported, "Handle positional", "When checked, positional input (mouse/touch) will automatically be handled.")
- ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshEntireTree", 0xefbc823c));
- editInfo->DataElement(AZ::Edit::UIHandlers::CheckBox, &UiCanvasComponent::m_isConsumingAllInputEvents, "Consume all input", "When checked, all input events will be consumed by this canvas while it is enabled.")
- ->Attribute(AZ::Edit::Attributes::Visibility, &UiCanvasComponent::GetIsPositionalInputSupported);
- editInfo->DataElement(AZ::Edit::UIHandlers::CheckBox, &UiCanvasComponent::m_isMultiTouchSupported, "Handle multi-touch", "When checked, multi-touch input will automatically be handled.")
- ->Attribute(AZ::Edit::Attributes::Visibility, &UiCanvasComponent::GetIsPositionalInputSupported);
- editInfo->DataElement(AZ::Edit::UIHandlers::CheckBox, &UiCanvasComponent::m_isNavigationSupported, "Handle navigation", "When checked, keyboard/gamepad events will automatically be used for navigation.");
- editInfo->DataElement(AZ::Edit::UIHandlers::Default, &UiCanvasComponent::m_navigationThreshold, "Navigation threshold", "The analog (eg. thumb-stick) input value that must be exceeded before a navigation command will be processed.")
- ->Attribute(AZ::Edit::Attributes::Min, 0.0f)
- ->Attribute(AZ::Edit::Attributes::Max, 1.0f);
- editInfo->DataElement(AZ::Edit::UIHandlers::Default, &UiCanvasComponent::m_navigationRepeatDelay, "Navigation repeat delay", "The delay (milliseconds) before a held navigation command will begin repeating.");
- editInfo->DataElement(AZ::Edit::UIHandlers::Default, &UiCanvasComponent::m_navigationRepeatPeriod, "Navigation repeat period", "The delay (milliseconds) before a held navigation command will continue repeating.");
- editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiCanvasComponent::m_firstHoverInteractable, "First focus elem", "The element to receive focus when the canvas loads.")
- ->Attribute("EnumValues", &UiCanvasComponent::PopulateNavigableEntityList);
- editInfo->ClassElement(AZ::Edit::ClassElements::Group, "Tooltips")
- ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
- editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiCanvasComponent::m_tooltipDisplayElement, "Tooltip display elem", "The element to be displayed when hovering over an interactable.")
- ->Attribute("EnumValues", &UiCanvasComponent::PopulateTooltipDisplayEntityList);
- editInfo->ClassElement(AZ::Edit::ClassElements::Group, "Editor settings")
- ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
- editInfo->DataElement(AZ::Edit::UIHandlers::Default, &UiCanvasComponent::m_snapDistance, "Snap distance", "The snap grid spacing.")
- ->Attribute(AZ::Edit::Attributes::Min, 1.0f);
- editInfo->DataElement(AZ::Edit::UIHandlers::Default, &UiCanvasComponent::m_snapRotationDegrees, "Snap rotation", "The degrees of rotation to snap to.")
- ->Attribute(AZ::Edit::Attributes::Min, 1.0f)
- ->Attribute(AZ::Edit::Attributes::Max, 359.0f)
- ->Attribute(AZ::Edit::Attributes::Suffix, " degrees");
- editInfo->DataElement(AZ::Edit::UIHandlers::Default, &UiCanvasComponent::m_guideColor, "Guide color", "The color to draw the guide lines on this canvas.");
- editInfo->DataElement("SimpleAssetRef", &UiCanvasComponent::m_atlasPathNames, "Texture atlases", "The texture atlases that this canvas loads.")
- ->Attribute("ChangeNotify", &UiCanvasComponent::ReloadAtlases);
- }
- }
- AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
- if (behaviorContext)
- {
- behaviorContext->EBus<UiCanvasBus>("UiCanvasBus")
- ->Event("GetDrawOrder", &UiCanvasBus::Events::GetDrawOrder)
- ->Event("SetDrawOrder", &UiCanvasBus::Events::SetDrawOrder)
- ->Event("GetKeepLoadedOnLevelUnload", &UiCanvasBus::Events::GetKeepLoadedOnLevelUnload)
- ->Event("SetKeepLoadedOnLevelUnload", &UiCanvasBus::Events::SetKeepLoadedOnLevelUnload)
- ->Event("RecomputeChangedLayouts", &UiCanvasBus::Events::RecomputeChangedLayouts)
- ->Event("GetNumChildElements", &UiCanvasBus::Events::GetNumChildElements)
- ->Event("GetChildElement", &UiCanvasBus::Events::GetChildElementEntityId)
- ->Event("GetChildElements", &UiCanvasBus::Events::GetChildElementEntityIds)
- ->Event("FindElementByName", &UiCanvasBus::Events::FindElementEntityIdByName)
- ->Event("CloneElement", &UiCanvasBus::Events::CloneElementEntityId)
- ->Event("GetIsPixelAligned", &UiCanvasBus::Events::GetIsPixelAligned)
- ->Event("SetIsPixelAligned", &UiCanvasBus::Events::SetIsPixelAligned)
- ->Event("GetIsTextPixelAligned", &UiCanvasBus::Events::GetIsTextPixelAligned)
- ->Event("SetIsTextPixelAligned", &UiCanvasBus::Events::SetIsTextPixelAligned)
- ->Event("GetEnabled", &UiCanvasBus::Events::GetEnabled)
- ->Event("SetEnabled", &UiCanvasBus::Events::SetEnabled)
- ->Event("GetIsRenderToTexture", &UiCanvasBus::Events::GetIsRenderToTexture)
- ->Event("SetIsRenderToTexture", &UiCanvasBus::Events::SetIsRenderToTexture)
- ->Event("GetAttachmentImageAsset", &UiCanvasBus::Events::GetAttachmentImageAsset)
- ->Event("SetAttachmentImageAsset", &UiCanvasBus::Events::SetAttachmentImageAsset)
- ->Event("GetIsPositionalInputSupported", &UiCanvasBus::Events::GetIsPositionalInputSupported)
- ->Event("SetIsPositionalInputSupported", &UiCanvasBus::Events::SetIsPositionalInputSupported)
- ->Event("GetIsConsumingAllInputEvents", &UiCanvasBus::Events::GetIsConsumingAllInputEvents)
- ->Event("SetIsConsumingAllInputEvents", &UiCanvasBus::Events::SetIsConsumingAllInputEvents)
- ->Event("GetIsMultiTouchSupported", &UiCanvasBus::Events::GetIsMultiTouchSupported)
- ->Event("SetIsMultiTouchSupported", &UiCanvasBus::Events::SetIsMultiTouchSupported)
- ->Event("GetIsNavigationSupported", &UiCanvasBus::Events::GetIsNavigationSupported)
- ->Event("SetIsNavigationSupported", &UiCanvasBus::Events::SetIsNavigationSupported)
- ->Event("GetNavigationThreshold", &UiCanvasBus::Events::GetNavigationThreshold)
- ->Event("SetNavigationThreshold", &UiCanvasBus::Events::SetNavigationThreshold)
- ->Event("GetNavigationRepeatDelay", &UiCanvasBus::Events::GetNavigationRepeatDelay)
- ->Event("SetNavigationRepeatDelay", &UiCanvasBus::Events::SetNavigationRepeatDelay)
- ->Event("GetNavigationRepeatPeriod", &UiCanvasBus::Events::GetNavigationRepeatPeriod)
- ->Event("SetNavigationRepeatPeriod", &UiCanvasBus::Events::SetNavigationRepeatPeriod)
- ->Event("GetTooltipDisplayElement", &UiCanvasBus::Events::GetTooltipDisplayElement)
- ->Event("SetTooltipDisplayElement", &UiCanvasBus::Events::SetTooltipDisplayElement)
- ->Event("GetHoverInteractable", &UiCanvasBus::Events::GetHoverInteractable)
- ->Event("ForceHoverInteractable", &UiCanvasBus::Events::ForceHoverInteractable)
- ->Event("GetMousePosition", &UiCanvasBus::Events::GetMousePosition)
- ->Event("GetCanvasSize", &UiCanvasBus::Events::GetCanvasSize)
- ->Event("GetAuthoredCanvasSize", &UiCanvasBus::Events::GetAuthoredCanvasSize)
- ->Event("ForceEnterInputEventOnInteractable", &UiCanvasBus::Events::ForceEnterInputEventOnInteractable);
- behaviorContext->EBus<UiCanvasNotificationBus>("UiCanvasNotificationBus")
- ->Handler<UiCanvasNotificationBusBehaviorHandler>();
- behaviorContext->EBus<UiAnimationBus>("UiAnimationBus")
- ->Event("StartSequence", &UiAnimationBus::Events::StartSequence)
- ->Event("PlaySequenceRange", &UiAnimationBus::Events::PlaySequenceRange)
- ->Event("StopSequence", &UiAnimationBus::Events::StopSequence)
- ->Event("AbortSequence", &UiAnimationBus::Events::AbortSequence)
- ->Event("PauseSequence", &UiAnimationBus::Events::PauseSequence)
- ->Event("ResumeSequence", &UiAnimationBus::Events::ResumeSequence)
- ->Event("ResetSequence", &UiAnimationBus::Events::ResetSequence)
- ->Event("GetSequencePlayingSpeed", &UiAnimationBus::Events::GetSequencePlayingSpeed)
- ->Event("SetSequencePlayingSpeed", &UiAnimationBus::Events::SetSequencePlayingSpeed)
- ->Event("GetSequencePlayingTime", &UiAnimationBus::Events::GetSequencePlayingTime)
- ->Event("IsSequencePlaying", &UiAnimationBus::Events::IsSequencePlaying)
- ->Event("GetSequenceLength", &UiAnimationBus::Events::GetSequenceLength)
- ->Event("SetSequenceStopBehavior", &UiAnimationBus::Events::SetSequenceStopBehavior);
- behaviorContext->Enum<(int)IUiAnimationListener::EUiAnimationEvent::eUiAnimationEvent_Started>("eUiAnimationEvent_Started")
- ->Enum<(int)IUiAnimationListener::EUiAnimationEvent::eUiAnimationEvent_Stopped>("eUiAnimationEvent_Stopped")
- ->Enum<(int)IUiAnimationListener::EUiAnimationEvent::eUiAnimationEvent_Aborted>("eUiAnimationEvent_Aborted")
- ->Enum<(int)IUiAnimationListener::EUiAnimationEvent::eUiAnimationEvent_Updated>("eUiAnimationEvent_Updated");
- behaviorContext->Enum<(int)IUiAnimationSystem::ESequenceStopBehavior::eSSB_LeaveTime>("eSSB_LeaveTime")
- ->Enum<(int)IUiAnimationSystem::ESequenceStopBehavior::eSSB_GotoEndTime>("eSSB_GotoEndTime")
- ->Enum<(int)IUiAnimationSystem::ESequenceStopBehavior::eSSB_GotoStartTime>("eSSB_GotoStartTime");
- behaviorContext->EBus<UiAnimationNotificationBus>("UiAnimationNotificationBus")
- ->Handler<UiAnimationNotificationBusBehaviorHandler>();
- behaviorContext->EBus<UiInitializationBus>("UiInitializationBus")
- ->Handler<UiInitializationBusBehaviorHandler>();
- behaviorContext->EBus<UiCanvasInputNotificationBus>("UiCanvasInputNotificationBus")
- ->Handler<UiCanvasInputNotificationBusBehaviorHandler>();
- behaviorContext->EBus<UiCanvasEnabledStateNotificationBus>("UiCanvasEnabledStateNotificationBus")
- ->Handler<UiCanvasEnabledStateNotificationBusBehaviorHandler>();
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::Initialize()
- {
- s_handleHoverInputEvents = true;
- s_allowClearingHoverInteractableOnHoverInput = true;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::Shutdown()
- {
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- // PROTECTED MEMBER FUNCTIONS
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::Init()
- {
- // We don't know whether we're in editor or game yet, but if we're in the editor
- // we need to know the authored canvas size to ensure certain properties are displayed
- // correctly in the editor window. If we're in game, the target canvas size will be
- // initialized to the viewport on the first render loop.
- m_targetCanvasSize = m_canvasSize;
- if (m_uniqueId == 0)
- {
- // Initialize unique Id
- m_uniqueId = CreateUniqueId();
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::Activate()
- {
- UiCanvasBus::Handler::BusConnect(GetEntityId());
- UiCanvasComponentImplementationBus::Handler::BusConnect(GetEntityId());
- UiEditorCanvasBus::Handler::BusConnect(GetEntityId());
- UiAnimationBus::Handler::BusConnect(GetEntityId());
- LyShine::RenderToTextureRequestBus::Handler::BusConnect(GetEntityId());
- // Reconnect to buses that we connect to intermittently
- // This will only happen if we have been deactivated and reactivated at runtime
- if (m_hoverInteractable.IsValid())
- {
- AZ::EntityBus::Handler::BusConnect(m_hoverInteractable);
- }
- if (m_activeInteractable.IsValid())
- {
- UiInteractableActiveNotificationBus::Handler::BusConnect(m_activeInteractable);
- }
- LoadAtlases();
- m_layoutManager = new UiLayoutManager(GetEntityId());
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::Deactivate()
- {
- UiCanvasBus::Handler::BusDisconnect();
- UiCanvasComponentImplementationBus::Handler::BusDisconnect();
- UiEditorCanvasBus::Handler::BusDisconnect();
- UiAnimationBus::Handler::BusDisconnect();
- LyShine::RenderToTextureRequestBus::Handler::BusDisconnect();
- // disconnect from any other buses we could be connected to
- if (m_hoverInteractable.IsValid() && AZ::EntityBus::Handler::BusIsConnectedId(m_hoverInteractable))
- {
- AZ::EntityBus::Handler::BusDisconnect(m_hoverInteractable);
- }
- if (m_activeInteractable.IsValid() && UiInteractableActiveNotificationBus::Handler::BusIsConnectedId(m_activeInteractable))
- {
- UiInteractableActiveNotificationBus::Handler::BusDisconnect(m_activeInteractable);
- }
- m_multiTouchInteractablesByTouchIndex.clear();
- if (m_renderToTexture)
- {
- DestroyRenderTarget();
- }
- // Destroy owned render targets
- m_attachmentImageMap.clear();
- //! Notify LyShine pass that it needs to rebuild
- QueueRttPassRebuild();
- delete m_layoutManager;
- m_layoutManager = nullptr;
- m_renderGraph.ResetGraph();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- // PRIVATE MEMBER FUNCTIONS
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::HandleHoverInputEvent(AZ::Vector2 point)
- {
- bool result = false;
- // We don't change the active interactable here. Some interactables may want to still be considered
- // pressed if the mouse moves outside their bounds while they are pressed.
- // However, the active interactable does influence how hover works, if there is an active
- // interactable then that is the only one that can be the hoverInteractable
- AZ::EntityId latestHoverInteractable;
- if (m_activeInteractable.IsValid())
- {
- // check if the mouse is hovering over the active interactable
- bool hoveringOnActive = false;
- UiTransformBus::EventResult(hoveringOnActive, m_activeInteractable, &UiTransformBus::Events::IsPointInRect, point);
- if (hoveringOnActive)
- {
- latestHoverInteractable = m_activeInteractable;
- }
- }
- else
- {
- // there is no active interactable
- // find the interactable that the mouse is hovering over (if any)
- UiElementBus::EventResult(latestHoverInteractable, m_rootElement, &UiElementBus::Events::FindInteractableToHandleEvent, point);
- }
- if (latestHoverInteractable.IsValid())
- {
- s_allowClearingHoverInteractableOnHoverInput = true;
- }
- if (m_hoverInteractable.IsValid() && m_hoverInteractable != latestHoverInteractable)
- {
- // we were hovering over an interactable but now we are hovering over nothing or a different interactable
- if (s_allowClearingHoverInteractableOnHoverInput)
- {
- ClearHoverInteractable();
- }
- }
- if (latestHoverInteractable.IsValid() && !m_hoverInteractable.IsValid())
- {
- // we are now hovering over something and we aren't tracking that yet
- SetHoverInteractable(latestHoverInteractable);
- UiInteractableBus::EventResult(result, m_hoverInteractable, &UiInteractableBus::Events::IsHandlingEvents);
- }
- // if there is an active interactable then we send mouse position updates to that interactable
- if (m_activeInteractable.IsValid())
- {
- UiInteractableBus::Event(m_activeInteractable, &UiInteractableBus::Events::InputPositionUpdate, point);
- }
- return result;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::HandleKeyInputEvent(const AzFramework::InputChannel::Snapshot& inputSnapshot, AzFramework::ModifierKeyMask activeModifierKeys)
- {
- bool result = false;
- // Allow the active interactable to handle the key input first
- if (m_activeInteractable.IsValid())
- {
- if (inputSnapshot.m_state == AzFramework::InputChannel::State::Began ||
- AzFramework::InputDeviceVirtualKeyboard::IsVirtualKeyboardDevice(inputSnapshot.m_deviceId)) // Virtual keyboard events don't have state
- {
- UiInteractableBus::EventResult(
- result, m_activeInteractable, &UiInteractableBus::Events::HandleKeyInputBegan, inputSnapshot, activeModifierKeys);
- }
- }
- if (!result && m_isNavigationSupported)
- {
- const UiNavigationHelpers::Command command = UiNavigationHelpers::MapInputChannelIdToUiNavigationCommand(inputSnapshot.m_channelId, activeModifierKeys);
- if (command != UiNavigationHelpers::Command::Unknown)
- {
- // Handle directional navigation input. Navigation is performed if there is no active interactable, or if the
- // active interactable is not pressed and is set to auto-activate
- bool handleDirectionalNavigation = false;
- if (!m_activeInteractable.IsValid())
- {
- handleDirectionalNavigation = true;
- }
- else if (!m_isActiveInteractablePressed)
- {
- // Check if the active interactable automatically goes to an active state
- UiInteractableBus::EventResult(
- handleDirectionalNavigation, m_activeInteractable, &UiInteractableBus::Events::GetIsAutoActivationEnabled);
- }
- if (handleDirectionalNavigation)
- {
- AZ::EntityId oldHoverInteractable = m_hoverInteractable;
- result = HandleNavigationInputEvent(command, inputSnapshot);
- if (m_hoverInteractable != oldHoverInteractable)
- {
- s_handleHoverInputEvents = false;
- s_allowClearingHoverInteractableOnHoverInput = false;
- AZ::EntityId ancestorInteractable = FindAncestorInteractable(m_hoverInteractable);
- if (ancestorInteractable.IsValid())
- {
- // Send an event that the descendant interactable became the hover interactable via navigation
- UiInteractableBus::Event(
- ancestorInteractable,
- &UiInteractableBus::Events::HandleDescendantReceivedHoverByNavigation,
- m_hoverInteractable);
- }
- ClearActiveInteractable();
- // Check if this hover interactable should automatically go to an active state
- CheckHoverInteractableAndAutoActivate(oldHoverInteractable, command);
- }
- }
- if (!result)
- {
- // Handle enter input
- result = HandleEnterInputEvent(command, inputSnapshot);
- }
- if (!result)
- {
- // Handle back input
- result = HandleBackInputEvent(command, inputSnapshot);
- }
- if (!result)
- {
- // If there is any active or hover interactable then we consider this event handled.
- // Otherwise we can end up sending events to underlying canvases even though there
- // is an interactable in this canvas that should block the events
- if (m_activeInteractable.IsValid() || m_hoverInteractable.IsValid())
- {
- result = true;
- }
- }
- }
- }
- return result;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::HandleEnterInputEvent(UiNavigationHelpers::Command command, const AzFramework::InputChannel::Snapshot& inputSnapshot)
- {
- bool result = false;
- if (command == UiNavigationHelpers::Command::Enter)
- {
- // The key is the Enter key. If there is any active or hover interactable then we consider this event handled.
- // Otherwise we can end up sending Enter events to underlying canvases even though there is an interactable
- // in this canvas that should block the events
- if (m_activeInteractable.IsValid() || m_hoverInteractable.IsValid())
- {
- result = true;
- }
- if (inputSnapshot.m_state == AzFramework::InputChannel::State::Began)
- {
- AZ::EntityId prevHoverInteractable = m_hoverInteractable;
- // Enter key was pressed. The press can activate an interactable and also deactivate an interactable
- // Check if there's an interactable to deactivate
- if (m_activeInteractable.IsValid() && m_activeInteractableShouldStayActive)
- {
- DeactivateInteractableByKeyInput(inputSnapshot);
- }
- else
- {
- // Check if there's a hover interactable to make active
- if (m_hoverInteractable.IsValid())
- {
- // clear any active interactable
- ClearActiveInteractable();
- // if the hover interactable can handle enter pressed events then
- // it becomes the currently pressed interactable for the canvas
- bool handled = false;
- bool shouldStayActive = false;
- UiInteractableBus::EventResult(
- handled, m_hoverInteractable, &UiInteractableBus::Events::HandleEnterPressed, shouldStayActive);
- if (handled)
- {
- SetActiveInteractable(m_hoverInteractable, shouldStayActive);
- s_handleHoverInputEvents = false;
- s_allowClearingHoverInteractableOnHoverInput = false;
- m_isActiveInteractablePressed = true;
- }
- }
- }
- // Send a notification to listeners telling them who was just pressed (can be noone)
- UiCanvasInputNotificationBus::Event(
- GetEntityId(), &UiCanvasInputNotificationBus::Events::OnCanvasEnterPressed, prevHoverInteractable);
- }
- else if (inputSnapshot.m_state == AzFramework::InputChannel::State::Ended)
- {
- AZ::EntityId prevActiveInteractable = m_activeInteractable;
- // Enter key has been released. Check if the active interactable should stay active
- if (m_activeInteractable.IsValid() && (m_activeInteractable == m_hoverInteractable))
- {
- UiInteractableBus::Event(m_activeInteractable, &UiInteractableBus::Events::HandleEnterReleased);
- if (!m_activeInteractableShouldStayActive)
- {
- ClearActiveInteractable();
- }
- else
- {
- // Interactable should stay active, so check if if it has a descendant interactable
- // that it should pass the hover to
- CheckActiveInteractableAndPassHoverToDescendant();
- }
- m_isActiveInteractablePressed = false;
- }
- // Send a notification to listeners telling them who was just released (can be noone)
- UiCanvasInputNotificationBus::Event(
- GetEntityId(), &UiCanvasInputNotificationBus::Events::OnCanvasEnterReleased, prevActiveInteractable);
- }
- }
- return result;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::HandleBackInputEvent(UiNavigationHelpers::Command command, const AzFramework::InputChannel::Snapshot& inputSnapshot)
- {
- bool result = false;
- if (command == UiNavigationHelpers::Command::Back)
- {
- if (inputSnapshot.m_state == AzFramework::InputChannel::State::Began)
- {
- // Back has two purposes:
- // 1. If there is an active interactable, and it's not set to auto-activate, pressing back deactivates the interactable
- // 2. If there is a hover interactable, and it's a child of another interactable, then pressing back moves focus from the child
- // to the parent
- // First check if there is an active interactable to deactivate
- if (m_activeInteractable.IsValid())
- {
- // Deactivate this interactable
- result = DeactivateInteractableByKeyInput(inputSnapshot);
- }
- else if (m_hoverInteractable.IsValid())
- {
- result = PassHoverToAncestorByKeyInput(inputSnapshot);
- }
- }
- }
- return result;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::HandleNavigationInputEvent(UiNavigationHelpers::Command command, const AzFramework::InputChannel::Snapshot& inputSnapshot)
- {
- bool result = false;
- if (command == UiNavigationHelpers::Command::Up ||
- command == UiNavigationHelpers::Command::Down ||
- command == UiNavigationHelpers::Command::Left ||
- command == UiNavigationHelpers::Command::Right)
- {
- // If the stick is no longer pushed, we allow navigating in that direction again
- auto navCommandStatus = m_navCommandStatus.find(command);
- if (inputSnapshot.m_state == AzFramework::InputChannel::State::Ended)
- {
- navCommandStatus->second.navigationCount = 0;
- navCommandStatus->second.allowNavigation = true;
- }
- // Prevent navigation in this direction for the specified period of time
- const AZ::u64 time = AZStd::GetTimeUTCMilliSecond();
- if (!navCommandStatus->second.allowNavigation)
- {
- // The 'navigation repeat delay' is different to the 'navigation repeat period' so that we
- // can have a longer delay before the first repeated navigation command vs all subsequent
- // navigation command repeats. For example, the default values result in a delay of 300ms
- // before a held navigation command will begin repeated, but then while it remains held it
- // will continue to repeat every 150ms
- const AZ::u64 timeSinceLastNavigation = time - navCommandStatus->second.lastNavigationTime;
- if ((navCommandStatus->second.navigationCount > 1 && timeSinceLastNavigation >= m_navigationRepeatPeriod) ||
- (timeSinceLastNavigation >= m_navigationRepeatDelay))
- {
- navCommandStatus->second.allowNavigation = true;
- }
- else
- {
- return false;
- }
- }
- // Check if the thumb-stick was pushed far enough
- if (inputSnapshot.m_value >= m_navigationThreshold)
- {
- // Don't allow navigating in this direction again until the stick is released or enough time has elapsed.
- navCommandStatus->second.lastNavigationTime = time;
- navCommandStatus->second.allowNavigation = false;
- ++navCommandStatus->second.navigationCount;
- AZ::EntityId firstHoverInteractable = GetFirstHoverInteractable();
- // Find the interactable to navigate to
- if (!m_hoverInteractable.IsValid())
- {
- SetHoverInteractable(firstHoverInteractable);
- }
- else
- {
- AZ::EntityId curInteractable = m_hoverInteractable;
- while (curInteractable.IsValid())
- {
- AZ::EntityId ancestorInteractable = UiNavigationHelpers::FindAncestorNavigableInteractable(curInteractable);
- LyShine::EntityArray navigableElements;
- UiNavigationHelpers::FindNavigableInteractables(ancestorInteractable.IsValid() ? ancestorInteractable : m_rootElement, curInteractable, navigableElements);
- AZ::EntityId nextEntityId = UiNavigationHelpers::GetNextElement(curInteractable, command,
- navigableElements, firstHoverInteractable, IsValidInteractable, ancestorInteractable);
- if (nextEntityId.IsValid())
- {
- SetHoverInteractable(nextEntityId);
- break;
- }
- else
- {
- // Check if parent interactable was auto-activated
- bool autoActivated = false;
- UiInteractableBus::EventResult(
- autoActivated, ancestorInteractable, &UiInteractableBus::Events::GetIsAutoActivationEnabled);
- if (autoActivated)
- {
- curInteractable = ancestorInteractable;
- }
- else
- {
- break;
- }
- }
- }
- }
- result = m_hoverInteractable.IsValid();
- }
- }
- return result;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::DeactivateInteractableByKeyInput(const AzFramework::InputChannel::Snapshot& inputSnapshot)
- {
- // Check if the active interactable automatically went to an active state. If it did
- // not automatically go into its active state, then we deactivate the active interactable.
- // Otherwise, the only way to deactivate the interactable is by navigating away from it
- // using the directional keys
- bool autoActivated = false;
- UiInteractableBus::EventResult(autoActivated, m_activeInteractable, &UiInteractableBus::Events::GetIsAutoActivationEnabled);
- if (!autoActivated)
- {
- // Clear the active interactable
- AZ::EntityId prevActiveInteractable = m_activeInteractable;
- ClearActiveInteractable();
- if (AzFramework::InputDeviceGamepad::IsGamepadDevice(inputSnapshot.m_deviceId))
- {
- SetHoverInteractable(prevActiveInteractable);
- s_handleHoverInputEvents = false;
- s_allowClearingHoverInteractableOnHoverInput = false;
- }
- return true;
- }
- return false;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::PassHoverToAncestorByKeyInput([[maybe_unused]] const AzFramework::InputChannel::Snapshot& inputSnapshot)
- {
- bool result = false;
- // Check if the hover interactable has an ancestor that's also an interactable
- AZ::EntityId ancestorInteractable = UiNavigationHelpers::FindAncestorNavigableInteractable(m_hoverInteractable, true);
- if (ancestorInteractable.IsValid())
- {
- AZ::EntityId descendantInteractable = m_hoverInteractable;
- SetHoverInteractable(ancestorInteractable);
- UiInteractableBus::Event(
- ancestorInteractable, &UiInteractableBus::Events::HandleReceivedHoverByNavigatingFromDescendant, descendantInteractable);
- result = true;
- }
- return result;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::HandlePrimaryPress(AZ::Vector2 point)
- {
- bool result = false;
- // use the pressed position to select the interactable being pressed
- AZ::EntityId interactableEntity;
- UiElementBus::EventResult(interactableEntity, m_rootElement, &UiElementBus::Events::FindInteractableToHandleEvent, point);
- // Clear the previous active interactable if it's different from the new active interactable
- if (!interactableEntity.IsValid() || (interactableEntity != m_activeInteractable))
- {
- if (m_activeInteractable.IsValid())
- {
- ClearActiveInteractable();
- }
- }
- if (interactableEntity.IsValid())
- {
- // if there is an interactable at that point and it can handle pressed events then
- // it becomes the currently pressed interactable for the canvas
- bool handled = false;
- bool shouldStayActive = false;
- UiInteractableBus::EventResult(handled, interactableEntity, &UiInteractableBus::Events::HandlePressed, point, shouldStayActive);
- if (handled)
- {
- SetActiveInteractable(interactableEntity, shouldStayActive);
- m_isActiveInteractablePressed = true;
- result = true;
- }
- }
- // Resume handling hover input events
- s_handleHoverInputEvents = true;
- s_allowClearingHoverInteractableOnHoverInput = true;
- // Send a notification to listeners telling them who was just pressed (can be noone)
- UiCanvasInputNotificationBus::Event(GetEntityId(), &UiCanvasInputNotificationBus::Events::OnCanvasPrimaryPressed, interactableEntity);
- return result;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::HandlePrimaryUpdate([[maybe_unused]] AZ::Vector2 point)
- {
- bool result = false;
- if (m_activeInteractable.IsValid())
- {
- result = true;
- }
- return result;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::HandlePrimaryRelease(AZ::Vector2 point)
- {
- bool result = false;
- AZ::EntityId prevActiveInteractable = m_activeInteractable;
- // touch was released, if there is a currently pressed interactable let it handle the release
- if (m_activeInteractable.IsValid())
- {
- UiInteractableBus::Event(m_activeInteractable, &UiInteractableBus::Events::HandleReleased, point);
- if (!m_activeInteractableShouldStayActive)
- {
- UiInteractableActiveNotificationBus::Handler::BusDisconnect(m_activeInteractable);
- m_activeInteractable.SetInvalid();
- }
- m_isActiveInteractablePressed = false;
- result = true;
- }
- // Send a notification to listeners telling them who was just released
- UiCanvasInputNotificationBus::Event(
- GetEntityId(), &UiCanvasInputNotificationBus::Events::OnCanvasPrimaryReleased, prevActiveInteractable);
- return result;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::HandleMultiTouchPress(AZ::Vector2 point, int multiTouchIndex)
- {
- bool result = false;
- if (m_isMultiTouchSupported)
- {
- AZ::EntityId interactableEntity;
- UiElementBus::EventResult(interactableEntity, m_rootElement, &UiElementBus::Events::FindInteractableToHandleEvent, point);
- if (interactableEntity.IsValid() && !IsInteractableActiveOrPressed(interactableEntity))
- {
- UiInteractableBus::EventResult(
- result, interactableEntity, &UiInteractableBus::Events::HandleMultiTouchPressed, point, multiTouchIndex);
- if (result)
- {
- m_multiTouchInteractablesByTouchIndex[multiTouchIndex] = interactableEntity;
- }
- }
- // Send a notification to listeners telling them who was just pressed (can be noone)
- UiCanvasInputNotificationBus::Event(
- GetEntityId(), &UiCanvasInputNotificationBus::Events::OnCanvasMultiTouchPressed, interactableEntity, multiTouchIndex);
- }
- return result;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::HandleMultiTouchRelease(AZ::Vector2 point, int multiTouchIndex)
- {
- bool result = false;
- if (m_isMultiTouchSupported)
- {
- // Get the corresponding interactable from the map before removing it. It should always already
- // exist in the map, but if not this will just insert an invalid entity id then remove it again
- AZ::EntityId multiTouchInteractable = m_multiTouchInteractablesByTouchIndex[multiTouchIndex];
- m_multiTouchInteractablesByTouchIndex.erase(multiTouchIndex);
- if (multiTouchInteractable.IsValid())
- {
- UiInteractableBus::Event(multiTouchInteractable, &UiInteractableBus::Events::HandleMultiTouchReleased, point, multiTouchIndex);
- result = true;
- }
- // Send a notification to listeners telling them who was just released
- UiCanvasInputNotificationBus::Event(
- GetEntityId(), &UiCanvasInputNotificationBus::Events::OnCanvasMultiTouchReleased, multiTouchInteractable, multiTouchIndex);
- }
- return result;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::HandleMultiTouchUpdated(AZ::Vector2 point, int multiTouchIndex)
- {
- bool result = false;
- if (m_isMultiTouchSupported)
- {
- auto it = m_multiTouchInteractablesByTouchIndex.find(multiTouchIndex);
- if (it != m_multiTouchInteractablesByTouchIndex.end() && it->second.IsValid())
- {
- UiInteractableBus::Event(it->second, &UiInteractableBus::Events::MultiTouchPositionUpdate, point, multiTouchIndex);
- result = true;
- }
- }
- return result;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::IsInteractableActiveOrPressed(AZ::EntityId interactableId) const
- {
- if (interactableId == m_activeInteractable)
- {
- return true;
- }
- for (const auto& multiTouchInteractableByTouchIndex : m_multiTouchInteractablesByTouchIndex)
- {
- if (interactableId == multiTouchInteractableByTouchIndex.second)
- {
- return true;
- }
- }
- return false;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetHoverInteractable(AZ::EntityId newHoverInteractable)
- {
- if (m_hoverInteractable != newHoverInteractable)
- {
- ClearHoverInteractable();
- m_hoverInteractable = newHoverInteractable;
- if (m_hoverInteractable.IsValid())
- {
- UiInteractableBus::Event(m_hoverInteractable, &UiInteractableBus::Events::HandleHoverStart);
- UiCanvasInputNotificationBus::Event(
- GetEntityId(), &UiCanvasInputNotificationBus::Events::OnCanvasHoverStart, m_hoverInteractable);
- // we want to know if this entity is deactivated or destroyed
- // (unlikely: while hovered over we can't be in edit mode, could happen from C++ interface though)
- AZ::EntityBus::Handler::BusConnect(m_hoverInteractable);
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::ClearHoverInteractable()
- {
- if (m_hoverInteractable.IsValid())
- {
- UiInteractableBus::Event(m_hoverInteractable, &UiInteractableBus::Events::HandleHoverEnd);
- UiCanvasInputNotificationBus::Event(GetEntityId(), &UiCanvasInputNotificationBus::Events::OnCanvasHoverEnd, m_hoverInteractable);
- AZ::EntityBus::Handler::BusDisconnect(m_hoverInteractable);
- m_hoverInteractable.SetInvalid();
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetActiveInteractable(AZ::EntityId newActiveInteractable, bool shouldStayActive)
- {
- if (m_activeInteractable != newActiveInteractable)
- {
- ClearActiveInteractable();
- m_activeInteractable = newActiveInteractable;
- if (m_activeInteractable.IsValid())
- {
- UiInteractableActiveNotificationBus::Handler::BusConnect(m_activeInteractable);
- m_activeInteractableShouldStayActive = shouldStayActive;
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::ClearActiveInteractable()
- {
- if (m_activeInteractable.IsValid())
- {
- UiInteractableBus::Event(m_activeInteractable, &UiInteractableBus::Events::LostActiveStatus);
- UiInteractableActiveNotificationBus::Handler::BusDisconnect(m_activeInteractable);
- m_activeInteractable.SetInvalid();
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::CheckHoverInteractableAndAutoActivate(AZ::EntityId prevHoverInteractable, UiNavigationHelpers::Command command, bool forceAutoActivate)
- {
- // Check if this hover interactable should automatically go to an active state
- bool autoActivate = false;
- UiInteractableBus::EventResult(autoActivate, m_hoverInteractable, &UiInteractableBus::Events::GetIsAutoActivationEnabled);
- if (autoActivate || forceAutoActivate)
- {
- bool handled = false;
- UiInteractableBus::EventResult(handled, m_hoverInteractable, &UiInteractableBus::Events::HandleAutoActivation);
- if (handled)
- {
- SetActiveInteractable(m_hoverInteractable, true);
- CheckActiveInteractableAndPassHoverToDescendant(prevHoverInteractable, command);
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::CheckActiveInteractableAndPassHoverToDescendant(AZ::EntityId prevHoverInteractable, UiNavigationHelpers::Command command)
- {
- AZ::EntityId hoverInteractable;
- if (prevHoverInteractable.IsValid())
- {
- LyShine::EntityArray navigableElements;
- UiNavigationHelpers::FindNavigableInteractables(m_activeInteractable, AZ::EntityId(), navigableElements);
- if (!navigableElements.empty())
- {
- hoverInteractable = UiNavigationHelpers::SearchForNextElement(prevHoverInteractable, command, navigableElements, m_activeInteractable);
- }
- }
- if (!hoverInteractable.IsValid())
- {
- hoverInteractable = FindFirstHoverInteractable(m_activeInteractable);
- }
- if (hoverInteractable.IsValid())
- {
- // Send an event that the descendant interactable became the hover interactable via navigation
- UiInteractableBus::Event(
- m_activeInteractable, &UiInteractableBus::Events::HandleDescendantReceivedHoverByNavigation, hoverInteractable);
- ClearActiveInteractable();
- SetHoverInteractable(hoverInteractable);
- CheckHoverInteractableAndAutoActivate(prevHoverInteractable, command);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::EntityId UiCanvasComponent::FindAncestorInteractable(AZ::EntityId entityId)
- {
- AZ::EntityId parent;
- UiElementBus::EventResult(parent, entityId, &UiElementBus::Events::GetParentEntityId);
- while (parent.IsValid())
- {
- if (UiInteractableBus::FindFirstHandler(parent))
- {
- break;
- }
- AZ::EntityId newParent = parent;
- parent.SetInvalid();
- UiElementBus::EventResult(parent, newParent, &UiElementBus::Events::GetParentEntityId);
- }
- return parent;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::EntityId UiCanvasComponent::GetFirstHoverInteractable()
- {
- AZ::EntityId hoverInteractable;
- if (m_firstHoverInteractable.IsValid())
- {
- // Make sure that this interactable exists
- AZ::Entity* hoverEntity = nullptr;
- AZ::ComponentApplicationBus::BroadcastResult(
- hoverEntity, &AZ::ComponentApplicationBus::Events::FindEntity, m_firstHoverInteractable);
- if (hoverEntity)
- {
- if (UiNavigationHelpers::IsElementInteractableAndNavigable(m_firstHoverInteractable))
- {
- hoverInteractable = m_firstHoverInteractable;
- }
- }
- }
- if (!hoverInteractable.IsValid())
- {
- hoverInteractable = FindFirstHoverInteractable();
- }
- return hoverInteractable;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::EntityId UiCanvasComponent::FindFirstHoverInteractable(AZ::EntityId parentElement)
- {
- if (!parentElement.IsValid())
- {
- parentElement = m_rootElement;
- }
- LyShine::EntityArray navigableElements;
- UiNavigationHelpers::FindNavigableInteractables(parentElement, AZ::EntityId(), navigableElements);
- UiTransformInterface::Rect parentRect;
- AZ::Matrix4x4 transformFromViewport;
- if (parentElement != m_rootElement)
- {
- UiTransformBus::Event(parentElement, &UiTransformBus::Events::GetCanvasSpaceRectNoScaleRotate, parentRect);
- UiTransformBus::Event(parentElement, &UiTransformBus::Events::GetTransformFromViewport, transformFromViewport);
- }
- else
- {
- transformFromViewport = AZ::Matrix4x4::CreateIdentity();
- parentRect.Set(0.0f, m_targetCanvasSize.GetX(), 0.0f, m_targetCanvasSize.GetY());
- }
- // Go through the navigable elements and find the closest element to the top left of its parent
- float shortestDist = FLT_MAX;
- float shortestOutsideDist = FLT_MAX;
- AZ::EntityId closestElement;
- AZ::EntityId closestOutsideElement;
- for (auto navigableElement : navigableElements)
- {
- UiTransformInterface::RectPoints points;
- UiTransformBus::Event(navigableElement->GetId(), &UiTransformBus::Events::GetViewportSpacePoints, points);
- points = points.Transform(transformFromViewport);
- AZ::Vector2 topLeft = points.GetAxisAlignedTopLeft() - AZ::Vector2(parentRect.left, parentRect.top);
- AZ::Vector2 center = points.GetCenter();
- float dist = topLeft.GetLength();
- bool inside = (center.GetX() >= parentRect.left &&
- center.GetX() <= parentRect.right &&
- center.GetY() >= parentRect.top &&
- center.GetY() <= parentRect.bottom);
- if (inside)
- {
- // Calculate a value from 0 to 1 representing how close the element is to the top of the screen
- float yDistValue = topLeft.GetY() / parentRect.GetHeight();
- // Calculate final distance value biased by y distance value
- const float distMultConstant = 1.0f;
- dist += dist * distMultConstant * yDistValue;
- if (dist < shortestDist)
- {
- shortestDist = dist;
- closestElement = navigableElement->GetId();
- }
- }
- else
- {
- if (dist < shortestOutsideDist)
- {
- shortestOutsideDist = dist;
- closestOutsideElement = navigableElement->GetId();
- }
- }
- }
- if (!closestElement.IsValid())
- {
- closestElement = closestOutsideElement;
- }
- return closestElement;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetFirstHoverInteractable()
- {
- bool setHoverInteractable = false;
- if (s_handleHoverInputEvents)
- {
- // Check if there is a mouse or touch input device
- const AzFramework::InputDevice* mouseDevice = AzFramework::InputDeviceRequests::FindInputDevice(AzFramework::InputDeviceMouse::Id);
- const AzFramework::InputDevice* touchDevice = AzFramework::InputDeviceRequests::FindInputDevice(AzFramework::InputDeviceTouch::Id);
- if ((!mouseDevice || !mouseDevice->IsConnected()) &&
- (!touchDevice || !touchDevice->IsConnected()))
- {
- // No mouse or touch input device available so set a hover interactable
- setHoverInteractable = true;
- }
- }
- else
- {
- // Not handling hover input events so set a hover interactable
- setHoverInteractable = true;
- }
- if (setHoverInteractable)
- {
- AZ::EntityId hoverInteractable = GetFirstHoverInteractable();
- if (hoverInteractable.IsValid())
- {
- SetHoverInteractable(hoverInteractable);
- s_handleHoverInputEvents = false;
- s_allowClearingHoverInteractableOnHoverInput = false;
- CheckHoverInteractableAndAutoActivate();
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::PrepareAnimationSystemForCanvasSave()
- {
- m_serializedAnimationData.m_serializeData.clear();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::RestoreAnimationSystemAfterCanvasLoad(bool remapIds, UiElementComponent::EntityIdMap* entityIdMap)
- {
- // NOTE: this is legacy code for loading old format animation data. The latest canvas format
- // uses the AZ serialization system for animation data.
- const char* buffer = m_serializedAnimationData.m_serializeData.c_str();
- size_t size = m_serializedAnimationData.m_serializeData.length();
- if (size > 0)
- {
- // found old format animation data
- // serialize back from loaded string and then clear string
- XmlNodeRef xmlNode = gEnv->pSystem->LoadXmlFromBuffer(buffer, size);
- m_uiAnimationSystem.Serialize(xmlNode, true);
- m_serializedAnimationData.m_serializeData.clear();
- }
- // go through the sequences and fixup the entity Ids
- // NOTE: for a latest format canvas these have probably already been remapped by ReplaceEntityRefs
- // This function will leave them alone if that are not in the remap table
- m_uiAnimationSystem.InitPostLoad(remapIds, entityIdMap);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- UiCanvasComponent* UiCanvasComponent::CloneAndInitializeCanvas(UiEntityContext* entityContext, const AZStd::string& assetIdPathname, const AZ::Vector2* canvasSize)
- {
- UiCanvasComponent* canvasComponent = nullptr;
- // Clone the root slice entity
- // Do this in a way that handles this canvas being a Editor canvas.
- // If it is an editor canvas then slices will be flattened and Editor components will be
- // replaced with runtime components.
- AZ::Entity* clonedRootSliceEntity = nullptr;
- AZStd::string rootSliceBuffer;
- AZ::IO::ByteContainerStream<AZStd::string > rootSliceStream(&rootSliceBuffer);
- if (m_entityContext->SaveToStreamForGame(rootSliceStream, AZ::ObjectStream::ST_XML))
- {
- rootSliceStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
- clonedRootSliceEntity = AZ::Utils::LoadObjectFromStream<AZ::Entity>(rootSliceStream);
- }
- // Clone the canvas entity
- AZ::Entity* sourceCanvasEntity = GetEntity();
- AZ::Entity* clonedCanvasEntity = nullptr;
- AZStd::string canvasBuffer;
- AZ::IO::ByteContainerStream<AZStd::string > canvasStream(&canvasBuffer);
- if (m_entityContext->SaveCanvasEntityToStreamForGame(sourceCanvasEntity, canvasStream, AZ::ObjectStream::ST_XML))
- {
- canvasStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
- clonedCanvasEntity = AZ::Utils::LoadObjectFromStream<AZ::Entity>(canvasStream);
- }
- if (clonedCanvasEntity && clonedRootSliceEntity)
- {
- // complete initialization of cloned entities, we assume this is NOT for editor
- // since we only do this when using canvas in game that is already loaded in editor
- canvasComponent = FixupPostLoad(clonedCanvasEntity, clonedRootSliceEntity, false, entityContext, canvasSize);
- }
- if (canvasComponent)
- {
- canvasComponent->m_pathname = assetIdPathname;
- canvasComponent->m_isLoadedInGame = true;
- }
- return canvasComponent;
- }
- void UiCanvasComponent::DeactivateElements()
- {
- AZ::SliceComponent::EntityIdSet entities;
- AZ::SliceComponent* rootSlice;
- AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(rootSlice, m_entityContext->GetContextId(),
- &AzFramework::SliceEntityOwnershipServiceRequests::GetRootSlice);
- bool result = rootSlice->GetEntityIds(entities);
- if (result)
- {
- for (AZ::EntityId& entityId : entities)
- {
- // Look up the entity by ID, as sometimes one of the entities owns others
- // that will be destroyed when it's destroyed. Since we store pointers,
- // those will point to freed memory.
- AZ::Entity* entity = nullptr;
- AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityId);
- if (entity && entity->GetState() == AZ::Entity::State::Active)
- {
- entity->Deactivate();
- }
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZStd::vector<AZ::EntityId> UiCanvasComponent::GetEntityIdsOfElementAndDescendants(AZ::Entity* entity)
- {
- AZStd::vector<AZ::EntityId> entitiesInPrefab;
- entitiesInPrefab.push_back(entity->GetId());
- LyShine::EntityArray descendantEntities;
- UiElementBus::Event(
- entity->GetId(),
- &UiElementBus::Events::FindDescendantElements,
- [](const AZ::Entity*)
- {
- return true;
- },
- descendantEntities);
- for (auto descendant : descendantEntities)
- {
- entitiesInPrefab.push_back(descendant->GetId());
- }
- return entitiesInPrefab;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SetTargetCanvasSizeAndUniformScale(bool isInGame, AZ::Vector2 canvasSize)
- {
- AZ::Vector2 oldTargetCanvasSize = m_targetCanvasSize;
- AZ::Vector2 oldDeviceScale = m_deviceScale;
- if (isInGame)
- {
- // Set the target canvas size to the canvas size specified by the caller
- m_targetCanvasSize = canvasSize;
- // set the device scale
- m_deviceScale.SetX(m_targetCanvasSize.GetX() / m_canvasSize.GetX());
- m_deviceScale.SetY(m_targetCanvasSize.GetY() / m_canvasSize.GetY());
- }
- else
- {
- // While in the editor, the only resolution we care about is the canvas' authored
- // size, so we set that as our target size for display purposes.
- m_targetCanvasSize = m_canvasSize;
- }
- // if the target canvas size or the uniform device scale changed then this will affect the
- // element transforms so force them to recompute
- if (oldTargetCanvasSize != m_targetCanvasSize || oldDeviceScale != m_deviceScale)
- {
- UiTransformInterface::Recompute recompute;
- if (oldTargetCanvasSize != m_targetCanvasSize)
- {
- recompute = (oldDeviceScale != m_deviceScale) ? UiTransformInterface::Recompute::RectAndTransform : UiTransformInterface::Recompute::RectOnly;
- }
- else
- {
- recompute = UiTransformInterface::Recompute::TransformOnly;
- }
- UiTransformBus::Event(GetRootElement()->GetId(), &UiTransformBus::Events::SetRecomputeFlags, recompute);
- UiCanvasSizeNotificationBus::Broadcast(&UiCanvasSizeNotificationBus::Events::OnCanvasSizeOrScaleChange, GetEntityId());
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::IsElementNameUnique(const AZStd::string& elementName, const LyShine::EntityArray& elements)
- {
- for (auto element : elements)
- {
- if (element->GetName() == elementName)
- {
- return false;
- }
- }
- return true;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- UiCanvasComponent::EntityComboBoxVec UiCanvasComponent::PopulateNavigableEntityList()
- {
- EntityComboBoxVec result;
- // Add a first entry for "None"
- result.push_back(AZStd::make_pair(AZ::EntityId(), "<None>"));
- // Get a list of all navigable elements
- LyShine::EntityArray navigableElements;
- auto checkNavigable = [](const AZ::Entity* entity)
- {
- UiNavigationInterface::NavigationMode navigationMode = UiNavigationInterface::NavigationMode::None;
- UiNavigationBus::EventResult(navigationMode, entity->GetId(), &UiNavigationBus::Events::GetNavigationMode);
- return (navigationMode != UiNavigationInterface::NavigationMode::None);
- };
- FindElements(checkNavigable, navigableElements);
- // Sort the elements by name
- AZStd::sort(navigableElements.begin(), navigableElements.end(),
- [](const AZ::Entity* e1, const AZ::Entity* e2) { return e1->GetName() < e2->GetName(); });
- // Add their names to the StringList and their IDs to the id list
- for (auto navigableEntity : navigableElements)
- {
- result.push_back(AZStd::make_pair(navigableEntity->GetId(), navigableEntity->GetName()));
- }
- return result;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- UiCanvasComponent::EntityComboBoxVec UiCanvasComponent::PopulateTooltipDisplayEntityList()
- {
- EntityComboBoxVec result;
- // Add a first entry for "None"
- result.push_back(AZStd::make_pair(AZ::EntityId(), "<None>"));
- // Get a list of all tooltip display elements
- LyShine::EntityArray tooltipDisplayElements;
- auto checkTooltipDisplay = [](const AZ::Entity* entity)
- {
- // Check for component on entity
- return UiTooltipDisplayBus::FindFirstHandler(entity->GetId()) ? true : false;
- };
- FindElements(checkTooltipDisplay, tooltipDisplayElements);
- // Sort the elements by name
- AZStd::sort(tooltipDisplayElements.begin(), tooltipDisplayElements.end(),
- [](const AZ::Entity* e1, const AZ::Entity* e2) { return e1->GetName() < e2->GetName(); });
- // Add their names to the StringList and their IDs to the id list
- for (auto tooltipDisplayEntity : tooltipDisplayElements)
- {
- result.push_back(AZStd::make_pair(tooltipDisplayEntity->GetId(), tooltipDisplayEntity->GetName()));
- }
- return result;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::OnPixelAlignmentChange()
- {
- UiCanvasPixelAlignmentNotificationBus::Event(
- GetEntityId(), &UiCanvasPixelAlignmentNotificationBus::Events::OnCanvasPixelAlignmentChange);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::OnTextPixelAlignmentChange()
- {
- UiCanvasPixelAlignmentNotificationBus::Event(
- GetEntityId(), &UiCanvasPixelAlignmentNotificationBus::Events::OnCanvasTextPixelAlignmentChange);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::CreateRenderTarget()
- {
- if (m_canvasSize.GetX() <= 0 || m_canvasSize.GetY() <= 0)
- {
- gEnv->pSystem->Warning(VALIDATOR_MODULE_SHINE, VALIDATOR_WARNING, VALIDATOR_FLAG_FILE,
- m_pathname.c_str(),
- "Invalid render target width/height for UI canvas: %s",
- m_pathname.c_str());
- return;
- }
- // Create a render target for the canvas
- m_attachmentImageId = UseRenderTargetAsset(m_attachmentImageAsset);
- AZ_Error("UI", !m_attachmentImageId.IsEmpty(), "Failed to acquire a render target for UI canvas: %s", m_pathname.c_str());
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::DestroyRenderTarget()
- {
- if (!m_attachmentImageId.IsEmpty())
- {
- LyShine::RenderToTextureRequestBus::Event(GetEntityId(),
- &LyShine::RenderToTextureRequestBus::Events::ReleaseRenderTarget, m_attachmentImageId);
- m_attachmentImageId = AZ::RHI::AttachmentId{};
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::SaveCanvasToFile(const AZStd::string& pathname, AZ::DataStream::StreamType streamType)
- {
- // Note: This is ok for saving in tools, but we should use the streamer to write objects directly (no memory store)
- AZStd::vector<AZ::u8> dstData;
- AZ::IO::ByteContainerStream<AZStd::vector<AZ::u8> > dstByteStream(&dstData);
- if (!SaveCanvasToStream(dstByteStream, streamType))
- {
- return false;
- }
- AZ::IO::SystemFile file;
- file.Open(pathname.c_str(), AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY);
- if (!file.IsOpen())
- {
- file.Close();
- return false;
- }
- file.Write(dstData.data(), dstData.size());
- file.Close();
- return true;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::SaveCanvasToStream(AZ::IO::GenericStream& stream, AZ::DataStream::StreamType streamType)
- {
- UiCanvasFileObject fileObject;
- fileObject.m_canvasEntity = this->GetEntity();
- AzFramework::RootSliceAsset rootSliceAsset;
- AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(rootSliceAsset, m_entityContext->GetContextId(),
- &AzFramework::SliceEntityOwnershipServiceRequestBus::Events::GetRootAsset);
- fileObject.m_rootSliceEntity = rootSliceAsset->GetEntity();
- if (!AZ::Utils::SaveObjectToStream<UiCanvasFileObject>(stream, streamType, &fileObject))
- {
- return false;
- }
- return true;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SendRectChangeNotificationsAndRecomputeLayouts()
- {
- // Send canvas space rect change notifications. Handlers may mark
- // layouts for a recompute
- SendRectChangeNotifications();
- // Recompute invalid layouts
- if (m_layoutManager->HasMarkedLayouts())
- {
- m_layoutManager->RecomputeMarkedLayouts();
- // The layout recompute may have caused child size changes, so
- // send canvas space rect change notifications again
- // NOTE: this is expensive so we don't do it unless there were marked layouts
- SendRectChangeNotifications();
- // Remove the newly marked layouts since they have been marked due
- // to their parents recomputing them
- m_layoutManager->UnmarkAllLayouts();
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::SendRectChangeNotifications()
- {
- // While we know there are transforms that need re-computing
- while (!m_elementsNeedingTransformRecompute.empty())
- {
- // get the front element from the list and remove it from the list
- UiElementComponent& elementComponent = m_elementsNeedingTransformRecompute.front();
- m_elementsNeedingTransformRecompute.pop_front();
- elementComponent.m_next = nullptr; // needed in order to be able to test if an element is in the list.
- // Get the transform component from the element and (if valid) recompute its transforms
- UiTransform2dComponent* transformComponent = elementComponent.GetTransform2dComponent();
- if (transformComponent)
- {
- // tell this transform to recompute (this can result in other elements being added to m_elementsNeedingTransformRecompute)
- transformComponent->RecomputeTransformsAndSendNotifications();
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::InitializeLayouts()
- {
- m_layoutManager->ComputeLayoutForElementAndDescendants(GetRootElement()->GetId());
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::InGamePostActivateBottomUp(AZ::Entity* entity)
- {
- if (!entity)
- {
- return;
- }
- LyShine::EntityArray childElements;
- UiElementBus::EventResult(childElements, entity->GetId(), &UiElementBus::Events::GetChildElements);
- for (auto child : childElements)
- {
- InGamePostActivateBottomUp(child);
- }
- UiInitializationBus::Event(entity->GetId(), &UiInitializationBus::Events::InGamePostActivate);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::Entity* UiCanvasComponent::CloneAndAddElementInternal(AZ::Entity* sourceEntity, AZ::Entity* parentEntity, AZ::Entity* insertBeforeEntity)
- {
- // first check that the given entity really is a UI element - i.e. it has a UiElementComponent
- UiElementComponent* sourceElementComponent = sourceEntity->FindComponent<UiElementComponent>();
- if (!sourceElementComponent)
- {
- AZ_Warning("UI", false, "CloneElement: The entity to be cloned must have an element component");
- return nullptr;
- }
- // also check that the given parent entity is part of this canvas (if one is specified)
- if (parentEntity)
- {
- AZ::EntityId parentCanvasId;
- UiElementBus::EventResult(parentCanvasId, parentEntity->GetId(), &UiElementBus::Events::GetCanvasEntityId);
- if (parentCanvasId != GetEntityId())
- {
- AZ_Warning("UI", false, "CloneElement: The parent entity must belong to this canvas");
- return nullptr;
- }
- }
- // If no parent entity specified then the parent is the root element
- AZ::Entity* parent = (parentEntity) ? parentEntity : GetRootElement();
- // also check that the given InsertBefore entity is a child of the parent
- if (insertBeforeEntity)
- {
- AZ::Entity* insertBeforeParent;
- UiElementBus::EventResult(insertBeforeParent, insertBeforeEntity->GetId(), &UiElementBus::Events::GetParent);
- if (insertBeforeParent != parent)
- {
- AZ_Warning("UI", false, "CloneElement: The insertBefore entity must be a child of the parent");
- return nullptr;
- }
- }
- AZ::SerializeContext* context = nullptr;
- AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
- AZ_Assert(context, "No serialization context found");
- AZStd::vector<AZ::EntityId> entitiesToClone = GetEntityIdsOfElementAndDescendants(sourceEntity);
- AzFramework::EntityList clonedEntities;
- m_entityContext->CloneUiEntities(entitiesToClone, clonedEntities);
- AZ::Entity* clonedRootEntity = clonedEntities[0];
- UiElementComponent* elementComponent = clonedRootEntity->FindComponent<UiElementComponent>();
- AZ_Assert(elementComponent, "The cloned entity must have an element component");
- // recursively set the canvas and parent pointers
- elementComponent->FixupPostLoad(clonedRootEntity, this, parent, true);
- // add this new entity as a child of the parent (parentEntity or root)
- UiElementComponent* parentElementComponent = parent->FindComponent<UiElementComponent>();
- AZ_Assert(parentElementComponent, "No element component found on parent entity");
- parentElementComponent->AddChild(clonedRootEntity, insertBeforeEntity);
- if (m_isLoadedInGame)
- {
- // Call InGamePostActivate on all the created entities
- InGamePostActivateBottomUp(clonedRootEntity);
- }
- return clonedRootEntity;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiCanvasComponent::GetOrphanedElements(AZ::SliceComponent::EntityList& orphanedEntities)
- {
- AZ::SliceComponent::EntityList entities;
- AZ::SliceComponent* rootSlice;
- AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(rootSlice, m_entityContext->GetContextId(),
- &AzFramework::SliceEntityOwnershipServiceRequests::GetRootSlice);
- rootSlice->GetEntities(entities);
- // We want to quickly check that every UiElement entity is referenced from the canvas.
- // We know that at this point all referenced elements have had FixupPostLoad called but
- // and orphans would not have had it called.
- // This means that referenced children have a non-null parent (except the root element).
- // We can use this data to make a list of all orphaned children.
- for (AZ::Entity* entity : entities)
- {
- AZ::Entity* parent = nullptr;
- UiElementBus::EventResult(parent, entity->GetId(), &UiElementBus::Events::GetParent);
- if (!parent)
- {
- if (m_rootElement != entity->GetId())
- {
- // This is an entity that is not referenced by the canvas.
- // If it has a UiElementComponent on it then it is definitely an orphan
- UiElementComponent* elementComponent = entity->FindComponent<UiElementComponent>();
- if (elementComponent)
- {
- // Add to ths list of orphans
- orphanedEntities.push_back(entity);
- }
- else
- {
- // This is some unknown entity. In theory the slice system could create such things but it does
- // not appear to.
- AZ_Warning("UI", false, "Found entity with no UiElementComponent in a UI canvas root slice.");
- }
- }
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- // PRIVATE STATIC MEMBER FUNCTIONS
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::u64 UiCanvasComponent::CreateUniqueId()
- {
- AZ::u64 utcTime = AZStd::GetTimeUTCMilliSecond();
- uint32 r = cry_random_uint32();
- AZ::u64 id = utcTime << 32 | r;
- return id;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- UiCanvasComponent* UiCanvasComponent::CreateCanvasInternal(UiEntityContext* entityContext, bool forEditor)
- {
- // create a new empty canvas, give it a name to avoid serialization generating one based on
- // the ID (which in some cases caused diffs to fail in the editor)
- AZ::Entity* canvasEntity = aznew AZ::Entity("UiCanvasEntity");
- UiCanvasComponent* canvasComponent = canvasEntity->CreateComponent<UiCanvasComponent>();
- // Initialize the UiEntityContext
- canvasComponent->m_entityContext = entityContext;
- canvasComponent->m_entityContext->InitUiContext();
- // Give the canvas a unique identifier. Used for canvas metrics
- canvasComponent->m_uniqueId = CreateUniqueId();
- // This is the dummy root node of the canvas.
- // It needs an element component and a transform component.
- AZ::Entity* rootEntity = canvasComponent->m_entityContext->CreateEntity("_root");
- canvasComponent->m_rootElement = rootEntity->GetId();
- AZ_Assert(rootEntity, "Failed to create root element entity");
- rootEntity->Deactivate(); // so we can add components
- UiElementComponent* elementComponent = rootEntity->CreateComponent<UiElementComponent>();
- AZ_Assert(elementComponent, "Failed to add UiElementComponent to entity");
- elementComponent->SetCanvas(canvasComponent, canvasComponent->GenerateId());
- [[maybe_unused]] AZ::Component* transformComponent = rootEntity->CreateComponent<UiTransform2dComponent>();
- AZ_Assert(transformComponent, "Failed to add transform2d component to entity");
- rootEntity->Activate(); // re-activate
- // init the canvas entity (the canvas entity is not part of the EntityContext so is not automatically initialized)
- canvasEntity->Init();
- canvasEntity->Activate();
- canvasComponent->m_isLoadedInGame = !forEditor;
- return canvasComponent;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- UiCanvasComponent* UiCanvasComponent::LoadCanvasInternal(const AZStd::string& pathnameToOpen, bool forEditor, const AZStd::string& assetIdPathname, UiEntityContext* entityContext,
- const AZ::SliceComponent::EntityIdToEntityIdMap* previousRemapTable, AZ::EntityId previousCanvasId)
- {
- UiCanvasComponent* canvasComponent = nullptr;
- // Currently LoadObjectFromFile will hang if the file cannot be parsed
- // (LMBR-10078). So first check that it is in the right format
- if (IsValidAzSerializedFile(pathnameToOpen))
- {
- // Open a stream on the input path
- AZ::IO::FileIOStream stream(pathnameToOpen.c_str(), AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeBinary);
- if (!stream.IsOpen())
- {
- AZ_Warning("UI", false, "Cannot open UI canvas file \"%s\".", pathnameToOpen.c_str());
- }
- else
- {
- // Read in the canvas from the stream
- UiCanvasFileObject* canvasFileObject = UiCanvasFileObject::LoadCanvasFromStream(stream);
- AZ_Assert(canvasFileObject, "Failed to load canvas");
- if (canvasFileObject)
- {
- AZ::Entity* canvasEntity = canvasFileObject->m_canvasEntity;
- AZ::Entity* rootSliceEntity = canvasFileObject->m_rootSliceEntity;
- AZ_Assert(canvasEntity && rootSliceEntity, "Failed to load canvas");
- if (canvasEntity && rootSliceEntity)
- {
- // file loaded OK
- // no need to check if a canvas with this EntityId is already loaded since we are going
- // to generate new entity IDs for all entities loaded from the file.
- // complete initialization of loaded entities
- canvasComponent = FixupPostLoad(canvasEntity, rootSliceEntity, forEditor, entityContext, nullptr, previousRemapTable, previousCanvasId);
- if (canvasComponent)
- {
- // The canvas size may get reset on the first call to RenderCanvas to set the size to
- // viewport size. So we'll recompute again on first render.
- UiTransformBus::Event(
- canvasComponent->GetRootElement()->GetId(),
- &UiTransformBus::Events::SetRecomputeFlags,
- UiTransformInterface::Recompute::RectAndTransform);
- canvasComponent->m_pathname = assetIdPathname;
- canvasComponent->m_isLoadedInGame = !forEditor;
- }
- else
- {
- // cleanup, don't delete rootSliceEntity, deleting the canvasEntity cleans up the EntityContext and root slice
- delete canvasEntity;
- }
- }
- // UiCanvasFileObject is a simple container for the canvas pointers, its destructor
- // doesn't destroy the canvas, but we need to delete it nonetheless to avoid leaking.
- delete canvasFileObject;
- }
- }
- }
- else
- {
- // this file is not a valid canvas file
- gEnv->pSystem->Warning(VALIDATOR_MODULE_SHINE, VALIDATOR_WARNING, VALIDATOR_FLAG_FILE,
- pathnameToOpen.c_str(),
- "Invalid XML format or couldn't load file for UI canvas file: %s",
- pathnameToOpen.c_str());
- }
- return canvasComponent;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- UiCanvasComponent* UiCanvasComponent::FixupReloadedCanvasForEditorInternal(AZ::Entity* newCanvasEntity,
- AZ::Entity* rootSliceEntity, UiEntityContext* entityContext,
- LyShine::CanvasId existingId, const AZStd::string& existingPathname)
- {
- UiCanvasComponent* newCanvasComponent = FixupPostLoad(newCanvasEntity, rootSliceEntity, true, entityContext);
- if (newCanvasComponent)
- {
- newCanvasComponent->m_id = existingId;
- newCanvasComponent->m_pathname = existingPathname;
- }
- return newCanvasComponent;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- UiCanvasComponent* UiCanvasComponent::FixupPostLoad(AZ::Entity* canvasEntity, AZ::Entity* rootSliceEntity, bool forEditor, UiEntityContext* entityContext, const AZ::Vector2* canvasSize,
- const AZ::SliceComponent::EntityIdToEntityIdMap* previousRemapTable, AZ::EntityId previousCanvasId)
- {
- // When we load in game we always generate new entity IDs.
- bool makeNewEntityIds = (forEditor) ? false : true;
- // When we load in the editor, check if there is another canvas open that has the same entityId
- if (forEditor)
- {
- AZ::Entity* foundEntity = nullptr;
- AZ::ComponentApplicationBus::BroadcastResult(foundEntity, &AZ::ComponentApplicationBus::Events::FindEntity, canvasEntity->GetId());
- if (foundEntity)
- {
- makeNewEntityIds = true;
- }
- }
- UiCanvasComponent* canvasComponent = canvasEntity->FindComponent<UiCanvasComponent>();
- AZ_Assert(canvasComponent, "No canvas component found on loaded entity");
- if (!canvasComponent)
- {
- return nullptr; // unlikely to happen but perhaps possible if a non-canvas file was opened
- }
- // Initialize the entity context for the new canvas and init and activate all the entities
- // in the root slice
- canvasComponent->m_entityContext = entityContext;
- canvasComponent->m_entityContext->InitUiContext();
- // Load atlases before initializing child components
- canvasComponent->LoadAtlases();
- bool isLoadingRootEntitySuccessful = false;
- if (previousRemapTable)
- {
- // We are reloading a canvas and we want to use the same entity ID mapping (from editor entity to game entity) as in the previously
- // loaded canvas. The code below is similar to what HandleLoadedRootSliceEntity does for remapping except that if the existing
- // mapping table already contains an entry for an editor entity ID it will use the existing mapping
- AZ::SliceComponent* newRootSlice = rootSliceEntity->FindComponent<AZ::SliceComponent>();
- AZ::SerializeContext* context = nullptr;
- AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
- AZ_Assert(context, "No serialization context found");
- AZ::SliceComponent::InstantiatedContainer entityContainer(false);
- newRootSlice->GetEntities(entityContainer.m_entities);
- canvasComponent->m_editorToGameEntityIdMap = *previousRemapTable;
- ReuseOrGenerateNewIdsAndFixRefs(&entityContainer, canvasComponent->m_editorToGameEntityIdMap, context);
- AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(isLoadingRootEntitySuccessful,
- canvasComponent->m_entityContext->GetContextId(),
- &AzFramework::SliceEntityOwnershipServiceRequestBus::Events::HandleRootEntityReloadedFromStream, rootSliceEntity, false, nullptr);
- if (!isLoadingRootEntitySuccessful)
- {
- return nullptr;
- }
- }
- else
- {
- // we are not reloading a canvas so we let the EntityContext HandleLoadedRootSliceEntity do the entity ID remapping as usual
- AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(isLoadingRootEntitySuccessful,
- canvasComponent->m_entityContext->GetContextId(),
- &AzFramework::SliceEntityOwnershipServiceRequestBus::Events::HandleRootEntityReloadedFromStream, rootSliceEntity,
- makeNewEntityIds, &canvasComponent->m_editorToGameEntityIdMap);
- if (!isLoadingRootEntitySuccessful)
- {
- return nullptr;
- }
- }
- // For the canvas entity itself, handle ID mapping and initialization
- {
- if (previousCanvasId.IsValid())
- {
- canvasEntity->SetId(previousCanvasId);
- }
- else if (makeNewEntityIds)
- {
- AZ::EntityId newId = AZ::Entity::MakeId();
- canvasEntity->SetId(newId);
- }
- // remap entity IDs such as m_rootElement and any entity IDs in the animation data
- if (makeNewEntityIds)
- {
- // new IDs were generated so we should fix up any internal EntityRefs
- AZ::SerializeContext* context = nullptr;
- AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
- AZ_Assert(context, "No serialization context found");
- ReuseOrGenerateNewIdsAndFixRefs(canvasEntity, canvasComponent->m_editorToGameEntityIdMap, context);
- }
- canvasEntity->Init();
- canvasEntity->Activate();
- }
- AZ::Entity* rootElement = canvasComponent->GetRootElement();
- UiElementComponent* elementComponent = rootElement->FindComponent<UiElementComponent>();
- AZ_Assert(elementComponent, "No element component found on root element entity");
- // Need to remapIds too (actually I don't think this needs to remap anymore)
- canvasComponent->RestoreAnimationSystemAfterCanvasLoad(makeNewEntityIds, &canvasComponent->m_editorToGameEntityIdMap);
- bool fixupSuccess = elementComponent->FixupPostLoad(rootElement, canvasComponent, nullptr, false);
- if (!fixupSuccess)
- {
- return nullptr;
- }
- // Initialize the target canvas size and uniform scale
- // This should be done before calling InGamePostActivate so that the
- // canvas space rects of the elements are accurate
- UiRenderer* uiRenderer = forEditor ? GetUiRendererForEditor() : GetUiRendererForGame();
- if (uiRenderer) // can be null in automated testing
- {
- AZ::Vector2 targetCanvasSize;
- if (canvasSize)
- {
- targetCanvasSize = *canvasSize;
- }
- else
- {
- targetCanvasSize = uiRenderer->GetViewportSize();
- }
- canvasComponent->SetTargetCanvasSizeAndUniformScale(!forEditor, targetCanvasSize);
- }
- // Set this before calling InGamePostActivate on the created entities. InGamePostActivate could
- // call CloneElement which checks this flag
- canvasComponent->m_isLoadedInGame = !forEditor;
- // Initialize transform properties of children of layout elements
- canvasComponent->InitializeLayouts();
- if (!forEditor)
- {
- // Call InGamePostActivate on all the created entities when loading in game
- canvasComponent->InGamePostActivateBottomUp(rootElement);
- }
- // Set the first hover interactable
- if (canvasComponent->m_isNavigationSupported)
- {
- canvasComponent->SetFirstHoverInteractable();
- }
- return canvasComponent;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiCanvasComponent::VersionConverter(AZ::SerializeContext& context,
- AZ::SerializeContext::DataElementNode& classElement)
- {
- // conversion from version 1 to 2:
- if (classElement.GetVersion() < 2)
- {
- // No need to actually convert anything because the CanvasFileObject takes care of it
- // But it makes sense to bump the version number because the m_rootElement is now an EntityId
- // rather than an Entity*
- }
- // conversion from version 2 to 3:
- // - Need to convert Vec2 to AZ::Vector2
- if (classElement.GetVersion() < 3)
- {
- if (!LyShine::ConvertSubElementFromVec2ToVector2(context, classElement, "CanvasSize"))
- {
- return false;
- }
- }
- // conversion from version 3 to 4:
- // - Need to remove render target name as it was replaced with attachment image asset
- if (classElement.GetVersion() < 4)
- {
- if (!LyShine::RemoveRenderTargetAsString(context, classElement, "RenderTargetName"))
- {
- return false;
- }
- }
- return true;
- }
|