UiInteractableState.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "UiInteractableState.h"
  9. #include <AzCore/Math/Crc.h>
  10. #include <AzCore/Serialization/SerializeContext.h>
  11. #include <AzCore/Serialization/EditContext.h>
  12. #include <AzCore/std/sort.h>
  13. #include <AzFramework/API/ApplicationAPI.h>
  14. #include <LyShine/ISprite.h>
  15. #include <LyShine/UiSerializeHelpers.h>
  16. #include <LyShine/Bus/UiCanvasBus.h>
  17. #include <LyShine/Bus/UiElementBus.h>
  18. #include <LyShine/Bus/UiVisualBus.h>
  19. #include <LyShine/Bus/UiIndexableImageBus.h>
  20. #include <LyShine/ILyShine.h>
  21. #include "EditorPropertyTypes.h"
  22. #include "Sprite.h"
  23. ////////////////////////////////////////////////////////////////////////////////////////////////////
  24. // UiInteractableStateAction class
  25. ////////////////////////////////////////////////////////////////////////////////////////////////////
  26. ////////////////////////////////////////////////////////////////////////////////////////////////////
  27. void UiInteractableStateAction::SetInteractableEntity(AZ::EntityId interactableEntityId)
  28. {
  29. m_interactableEntity = interactableEntityId;
  30. }
  31. ////////////////////////////////////////////////////////////////////////////////////////////////////
  32. void UiInteractableStateAction::Reflect(AZ::ReflectContext* context)
  33. {
  34. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  35. {
  36. serializeContext->Class<UiInteractableStateAction>();
  37. }
  38. }
  39. ////////////////////////////////////////////////////////////////////////////////////////////////////
  40. void UiInteractableStateAction::Init(AZ::EntityId interactableEntityId)
  41. {
  42. m_interactableEntity = interactableEntityId;
  43. }
  44. ////////////////////////////////////////////////////////////////////////////////////////////////////
  45. UiInteractableStateAction::EntityComboBoxVec UiInteractableStateAction::PopulateTargetEntityList()
  46. {
  47. EntityComboBoxVec result;
  48. // add a first entry for "None"
  49. result.emplace_back(m_interactableEntity, "<This element>");
  50. // Get a list of all child elements
  51. LyShine::EntityArray matchingElements;
  52. UiElementBus::Event(
  53. m_interactableEntity,
  54. &UiElementBus::Events::FindDescendantElements,
  55. []([[maybe_unused]] const AZ::Entity* entity)
  56. {
  57. return true;
  58. },
  59. matchingElements);
  60. // add their names to the StringList and their IDs to the id list
  61. for (auto childEntity : matchingElements)
  62. {
  63. result.push_back(AZStd::make_pair(childEntity->GetId(), childEntity->GetName()));
  64. }
  65. return result;
  66. }
  67. ////////////////////////////////////////////////////////////////////////////////////////////////////
  68. // UiInteractableStateColor class
  69. ////////////////////////////////////////////////////////////////////////////////////////////////////
  70. ////////////////////////////////////////////////////////////////////////////////////////////////////
  71. UiInteractableStateColor::UiInteractableStateColor()
  72. : m_color(1.0f, 1.0f, 1.0f, 1.0f)
  73. {
  74. }
  75. ////////////////////////////////////////////////////////////////////////////////////////////////////
  76. UiInteractableStateColor::UiInteractableStateColor(AZ::EntityId target, AZ::Color color)
  77. : m_targetEntity(target)
  78. , m_color(color)
  79. {
  80. }
  81. ////////////////////////////////////////////////////////////////////////////////////////////////////
  82. void UiInteractableStateColor::Init(AZ::EntityId interactableEntityId)
  83. {
  84. UiInteractableStateAction::Init(interactableEntityId);
  85. if (!m_targetEntity.IsValid())
  86. {
  87. m_targetEntity = interactableEntityId;
  88. }
  89. }
  90. ////////////////////////////////////////////////////////////////////////////////////////////////////
  91. void UiInteractableStateColor::ApplyState()
  92. {
  93. UiVisualBus::Event(m_targetEntity, &UiVisualBus::Events::SetOverrideColor, m_color);
  94. }
  95. ////////////////////////////////////////////////////////////////////////////////////////////////////
  96. void UiInteractableStateColor::SetInteractableEntity(AZ::EntityId interactableEntityId)
  97. {
  98. m_interactableEntity = interactableEntityId;
  99. if (!m_targetEntity.IsValid())
  100. {
  101. m_targetEntity = m_interactableEntity;
  102. }
  103. }
  104. ////////////////////////////////////////////////////////////////////////////////////////////////////
  105. UiInteractableStateAction::EntityComboBoxVec UiInteractableStateColor::PopulateTargetEntityList()
  106. {
  107. return UiInteractableStateAction::PopulateTargetEntityList();
  108. }
  109. ////////////////////////////////////////////////////////////////////////////////////////////////////
  110. void UiInteractableStateColor::Reflect(AZ::ReflectContext* context)
  111. {
  112. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  113. if (serializeContext)
  114. {
  115. serializeContext->Class<UiInteractableStateColor, UiInteractableStateAction>()
  116. ->Version(3, &VersionConverter)
  117. ->Field("TargetEntity", &UiInteractableStateColor::m_targetEntity)
  118. ->Field("Color", &UiInteractableStateColor::m_color);
  119. AZ::EditContext* ec = serializeContext->GetEditContext();
  120. if (ec)
  121. {
  122. auto editInfo = ec->Class<UiInteractableStateColor>("Color", "Overrides the color tint on the target element.");
  123. editInfo->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  124. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  125. editInfo->DataElement("ComboBox", &UiInteractableStateColor::m_targetEntity, "Target", "The target element.")
  126. ->Attribute("EnumValues", &UiInteractableStateColor::PopulateTargetEntityList);
  127. editInfo->DataElement("Color", &UiInteractableStateColor::m_color, "Color", "The color tint.");
  128. }
  129. }
  130. }
  131. ////////////////////////////////////////////////////////////////////////////////////////////////////
  132. bool UiInteractableStateColor::VersionConverter(AZ::SerializeContext& context,
  133. AZ::SerializeContext::DataElementNode& classElement)
  134. {
  135. // conversion from version 1 to current:
  136. // - Need to convert AZ::Vector3 to AZ::Color
  137. if (classElement.GetVersion() <= 1)
  138. {
  139. if (!LyShine::ConvertSubElementFromVector3ToAzColor(context, classElement, "Color"))
  140. {
  141. return false;
  142. }
  143. }
  144. return true;
  145. }
  146. ////////////////////////////////////////////////////////////////////////////////////////////////////
  147. // UiInteractableStateAlpha class
  148. ////////////////////////////////////////////////////////////////////////////////////////////////////
  149. ////////////////////////////////////////////////////////////////////////////////////////////////////
  150. UiInteractableStateAlpha::UiInteractableStateAlpha()
  151. : m_alpha(1.0f)
  152. {
  153. }
  154. ////////////////////////////////////////////////////////////////////////////////////////////////////
  155. UiInteractableStateAlpha::UiInteractableStateAlpha(AZ::EntityId target, float alpha)
  156. : m_targetEntity(target)
  157. , m_alpha(alpha)
  158. {
  159. }
  160. ////////////////////////////////////////////////////////////////////////////////////////////////////
  161. void UiInteractableStateAlpha::Init(AZ::EntityId interactableEntityId)
  162. {
  163. UiInteractableStateAction::Init(interactableEntityId);
  164. if (!m_targetEntity.IsValid())
  165. {
  166. m_targetEntity = interactableEntityId;
  167. }
  168. }
  169. ////////////////////////////////////////////////////////////////////////////////////////////////////
  170. void UiInteractableStateAlpha::ApplyState()
  171. {
  172. UiVisualBus::Event(m_targetEntity, &UiVisualBus::Events::SetOverrideAlpha, m_alpha);
  173. }
  174. ////////////////////////////////////////////////////////////////////////////////////////////////////
  175. void UiInteractableStateAlpha::SetInteractableEntity(AZ::EntityId interactableEntityId)
  176. {
  177. m_interactableEntity = interactableEntityId;
  178. if (!m_targetEntity.IsValid())
  179. {
  180. m_targetEntity = m_interactableEntity;
  181. }
  182. }
  183. ////////////////////////////////////////////////////////////////////////////////////////////////////
  184. UiInteractableStateAlpha::EntityComboBoxVec UiInteractableStateAlpha::PopulateTargetEntityList()
  185. {
  186. return UiInteractableStateAction::PopulateTargetEntityList();
  187. }
  188. ////////////////////////////////////////////////////////////////////////////////////////////////////
  189. void UiInteractableStateAlpha::Reflect(AZ::ReflectContext* context)
  190. {
  191. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  192. if (serializeContext)
  193. {
  194. serializeContext->Class<UiInteractableStateAlpha, UiInteractableStateAction>()
  195. ->Version(2)
  196. ->Field("TargetEntity", &UiInteractableStateAlpha::m_targetEntity)
  197. ->Field("Alpha", &UiInteractableStateAlpha::m_alpha);
  198. AZ::EditContext* ec = serializeContext->GetEditContext();
  199. if (ec)
  200. {
  201. auto editInfo = ec->Class<UiInteractableStateAlpha>("Alpha", "Overrides the alpha on the target element.");
  202. editInfo->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  203. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  204. editInfo->DataElement("ComboBox", &UiInteractableStateAlpha::m_targetEntity, "Target", "The target element.")
  205. ->Attribute("EnumValues", &UiInteractableStateAlpha::PopulateTargetEntityList);
  206. editInfo->DataElement("Slider", &UiInteractableStateAlpha::m_alpha, "Alpha", "The opacity.");
  207. }
  208. }
  209. }
  210. ////////////////////////////////////////////////////////////////////////////////////////////////////
  211. // UiInteractableStateSprite class
  212. ////////////////////////////////////////////////////////////////////////////////////////////////////
  213. ////////////////////////////////////////////////////////////////////////////////////////////////////
  214. UiInteractableStateSprite::UiInteractableStateSprite()
  215. : m_sprite(nullptr)
  216. {
  217. }
  218. ////////////////////////////////////////////////////////////////////////////////////////////////////
  219. UiInteractableStateSprite::UiInteractableStateSprite(AZ::EntityId target, ISprite* sprite)
  220. : m_targetEntity(target)
  221. , m_sprite(sprite)
  222. {
  223. m_sprite->AddRef();
  224. }
  225. ////////////////////////////////////////////////////////////////////////////////////////////////////
  226. UiInteractableStateSprite::UiInteractableStateSprite(AZ::EntityId target, const AZStd::string& spritePath)
  227. : m_targetEntity(target)
  228. {
  229. m_spritePathname.SetAssetPath(spritePath.c_str());
  230. if (!m_spritePathname.GetAssetPath().empty())
  231. {
  232. m_sprite = AZ::Interface<ILyShine>::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str());
  233. }
  234. }
  235. ////////////////////////////////////////////////////////////////////////////////////////////////////
  236. UiInteractableStateSprite::~UiInteractableStateSprite()
  237. {
  238. SAFE_RELEASE(m_sprite);
  239. }
  240. ////////////////////////////////////////////////////////////////////////////////////////////////////
  241. void UiInteractableStateSprite::Init(AZ::EntityId interactableEntityId)
  242. {
  243. UiInteractableStateAction::Init(interactableEntityId);
  244. if (!m_targetEntity.IsValid())
  245. {
  246. m_targetEntity = interactableEntityId;
  247. }
  248. // If this is called from RC.exe for example these pointers will not be set. In that case
  249. // we only need to be able to load, init and save the component. It will never be
  250. // activated.
  251. if (!AZ::Interface<ILyShine>::Get())
  252. {
  253. return;
  254. }
  255. // for the case of serializing from disk, if we have sprite pathnames but the sprites
  256. // are not loaded then load them
  257. if (!m_sprite && !m_spritePathname.GetAssetPath().empty())
  258. {
  259. m_sprite = AZ::Interface<ILyShine>::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str());
  260. }
  261. if (!m_sprite)
  262. {
  263. LoadSpriteFromTargetElement();
  264. }
  265. }
  266. ////////////////////////////////////////////////////////////////////////////////////////////////////
  267. void UiInteractableStateSprite::ApplyState()
  268. {
  269. UiVisualBus::Event(m_targetEntity, &UiVisualBus::Events::SetOverrideSprite, m_sprite, m_spriteSheetCellIndex);
  270. }
  271. ////////////////////////////////////////////////////////////////////////////////////////////////////
  272. void UiInteractableStateSprite::SetInteractableEntity(AZ::EntityId interactableEntityId)
  273. {
  274. m_interactableEntity = interactableEntityId;
  275. if (!m_targetEntity.IsValid())
  276. {
  277. m_targetEntity = m_interactableEntity;
  278. }
  279. }
  280. ////////////////////////////////////////////////////////////////////////////////////////////////////
  281. void UiInteractableStateSprite::SetSprite(ISprite* sprite)
  282. {
  283. CSprite::ReplaceSprite(&m_sprite, sprite);
  284. }
  285. ////////////////////////////////////////////////////////////////////////////////////////////////////
  286. AZStd::string UiInteractableStateSprite::GetSpritePathname()
  287. {
  288. return m_spritePathname.GetAssetPath();
  289. }
  290. ////////////////////////////////////////////////////////////////////////////////////////////////////
  291. void UiInteractableStateSprite::SetSpritePathname(const AZStd::string& spritePath)
  292. {
  293. m_spritePathname.SetAssetPath(spritePath.c_str());
  294. OnSpritePathnameChange();
  295. }
  296. ////////////////////////////////////////////////////////////////////////////////////////////////////
  297. UiInteractableStateSprite::EntityComboBoxVec UiInteractableStateSprite::PopulateTargetEntityList()
  298. {
  299. return UiInteractableStateAction::PopulateTargetEntityList();
  300. }
  301. ////////////////////////////////////////////////////////////////////////////////////////////////////
  302. void UiInteractableStateSprite::OnSpritePathnameChange()
  303. {
  304. ISprite* newSprite = nullptr;
  305. if (!m_spritePathname.GetAssetPath().empty())
  306. {
  307. // Load the new texture.
  308. newSprite = AZ::Interface<ILyShine>::Get()->LoadSprite(m_spritePathname.GetAssetPath().c_str());
  309. }
  310. SAFE_RELEASE(m_sprite);
  311. m_sprite = newSprite;
  312. // Default to selecting first cell in sprite-sheet
  313. m_spriteSheetCellIndex = 0;
  314. }
  315. ////////////////////////////////////////////////////////////////////////////////////////////////////
  316. void UiInteractableStateSprite::Reflect(AZ::ReflectContext* context)
  317. {
  318. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  319. if (serializeContext)
  320. {
  321. serializeContext->Class<UiInteractableStateSprite, UiInteractableStateAction>()
  322. ->Version(4)
  323. ->Field("TargetEntity", &UiInteractableStateSprite::m_targetEntity)
  324. ->Field("Sprite", &UiInteractableStateSprite::m_spritePathname)
  325. ->Field("Index", &UiInteractableStateSprite::m_spriteSheetCellIndex);
  326. AZ::EditContext* ec = serializeContext->GetEditContext();
  327. if (ec)
  328. {
  329. auto editInfo = ec->Class<UiInteractableStateSprite>("Sprite", "Overrides the sprite on the target element.");
  330. editInfo->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  331. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  332. editInfo->DataElement("ComboBox", &UiInteractableStateSprite::m_targetEntity, "Target", "The target element.")
  333. ->Attribute("EnumValues", &UiInteractableStateSprite::PopulateTargetEntityList)
  334. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiInteractableStateSprite::OnTargetElementChange)
  335. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC_CE("RefreshEntireTree"));
  336. editInfo->DataElement("Sprite", &UiInteractableStateSprite::m_spritePathname, "Sprite", "The sprite.")
  337. ->Attribute("ChangeNotify", &UiInteractableStateSprite::OnSpritePathnameChange)
  338. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC_CE("RefreshEntireTree"));
  339. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiInteractableStateSprite::m_spriteSheetCellIndex, "Index", "Sprite-sheet index. Defines which cell in a sprite-sheet is displayed.")
  340. ->Attribute(AZ::Edit::Attributes::Visibility, &UiInteractableStateSprite::IsSpriteSheet)
  341. ->Attribute("EnumValues", &UiInteractableStateSprite::PopulateIndexStringList);
  342. }
  343. }
  344. }
  345. ////////////////////////////////////////////////////////////////////////////////////////////////////
  346. bool UiInteractableStateSprite::IsSpriteSheet()
  347. {
  348. if (!m_sprite)
  349. {
  350. return false;
  351. }
  352. // We could query the target element's UiImageBus to see if the
  353. // sprite-type is actually sprite-sheet, but instead we simply check if
  354. // the provided sprite has more than one sprite-sheet cell configured.
  355. return m_sprite->GetSpriteSheetCells().size() > 1;
  356. }
  357. ////////////////////////////////////////////////////////////////////////////////////////////////////
  358. void UiInteractableStateSprite::OnTargetElementChange()
  359. {
  360. if (!m_sprite && m_targetEntity.IsValid())
  361. {
  362. LoadSpriteFromTargetElement();
  363. }
  364. }
  365. ////////////////////////////////////////////////////////////////////////////////////////////////////
  366. void UiInteractableStateSprite::LoadSpriteFromTargetElement()
  367. {
  368. AZStd::string spritePathname;
  369. UiImageBus::EventResult(spritePathname, m_targetEntity, &UiImageBus::Events::GetSpritePathname);
  370. m_spritePathname.SetAssetPath(spritePathname.c_str());
  371. OnSpritePathnameChange();
  372. }
  373. ////////////////////////////////////////////////////////////////////////////////////////////////////
  374. UiInteractableStateSprite::AZu32ComboBoxVec UiInteractableStateSprite::PopulateIndexStringList() const
  375. {
  376. int indexCount = 0;
  377. UiIndexableImageBus::EventResult(indexCount, m_targetEntity, &UiIndexableImageBus::Events::GetImageIndexCount);
  378. if (indexCount > 0)
  379. {
  380. return LyShine::GetEnumSpriteIndexList(m_targetEntity, 0, indexCount - 1);
  381. }
  382. return LyShine::AZu32ComboBoxVec();
  383. }
  384. ////////////////////////////////////////////////////////////////////////////////////////////////////
  385. // UiInteractableStateFont class
  386. ////////////////////////////////////////////////////////////////////////////////////////////////////
  387. ////////////////////////////////////////////////////////////////////////////////////////////////////
  388. UiInteractableStateFont::UiInteractableStateFont()
  389. : m_fontFamily(nullptr)
  390. , m_fontEffectIndex(0)
  391. {
  392. InitCommon("default-ui");
  393. }
  394. ////////////////////////////////////////////////////////////////////////////////////////////////////
  395. UiInteractableStateFont::UiInteractableStateFont(AZ::EntityId target, const AZStd::string& pathname, unsigned int fontEffectIndex)
  396. : m_targetEntity(target)
  397. , m_fontFamily(nullptr)
  398. , m_fontEffectIndex(fontEffectIndex)
  399. {
  400. InitCommon(pathname);
  401. }
  402. ////////////////////////////////////////////////////////////////////////////////////////////////////
  403. void UiInteractableStateFont::InitCommon(const AZStd::string& fontPathname)
  404. {
  405. SetFontPathname(fontPathname);
  406. FontNotificationBus::Handler::BusConnect();
  407. }
  408. ////////////////////////////////////////////////////////////////////////////////////////////////////
  409. UiInteractableStateFont::~UiInteractableStateFont()
  410. {
  411. FontNotificationBus::Handler::BusDisconnect();
  412. }
  413. ////////////////////////////////////////////////////////////////////////////////////////////////////
  414. void UiInteractableStateFont::Init(AZ::EntityId interactableEntityId)
  415. {
  416. UiInteractableStateAction::Init(interactableEntityId);
  417. if (!m_targetEntity.IsValid())
  418. {
  419. m_targetEntity = interactableEntityId;
  420. }
  421. // This will load the font if needed
  422. SetFontPathname(m_fontFilename.GetAssetPath().c_str());
  423. }
  424. ////////////////////////////////////////////////////////////////////////////////////////////////////
  425. void UiInteractableStateFont::ApplyState()
  426. {
  427. UiVisualBus::Event(m_targetEntity, &UiVisualBus::Events::SetOverrideFont, m_fontFamily);
  428. UiVisualBus::Event(m_targetEntity, &UiVisualBus::Events::SetOverrideFontEffect, m_fontEffectIndex);
  429. }
  430. ////////////////////////////////////////////////////////////////////////////////////////////////////
  431. void UiInteractableStateFont::SetInteractableEntity(AZ::EntityId interactableEntityId)
  432. {
  433. m_interactableEntity = interactableEntityId;
  434. if (!m_targetEntity.IsValid())
  435. {
  436. m_targetEntity = m_interactableEntity;
  437. }
  438. }
  439. ////////////////////////////////////////////////////////////////////////////////////////////////////
  440. void UiInteractableStateFont::OnFontsReloaded()
  441. {
  442. // All old font pointers have been deleted and the old font family pointers have been removed from the CryFont list.
  443. // New fonts and font family objects have been created and added to the CryFont list.
  444. // However, the old font family objects are still around because we have a shared pointer to them.
  445. // Clear the font family shared pointers since they should no longer be used (their fonts have been deleted).
  446. // When the last one is cleared, the font family's custom deleter will be called and the object will be deleted.
  447. // This is OK because the custom deleter doesn't do anything if the font family is not in the CryFont's list (which it isn't)
  448. m_fontFamily = nullptr;
  449. SetFontPathname(m_fontFilename.GetAssetPath());
  450. // It's possible that the font failed to load. If it did, try to load and use the default font but leave the
  451. // assigned font path the same
  452. if (!m_fontFamily)
  453. {
  454. AZStd::string assignedFontFilepath = m_fontFilename.GetAssetPath();
  455. SetFontPathname("");
  456. m_fontFilename.SetAssetPath(assignedFontFilepath.c_str());
  457. }
  458. }
  459. ////////////////////////////////////////////////////////////////////////////////////////////////////
  460. void UiInteractableStateFont::SetFontPathname(const AZStd::string& pathname)
  461. {
  462. // Just to be safe we make sure is normalized
  463. AZStd::string fontPath = pathname;
  464. AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::Bus::Events::NormalizePath, fontPath);
  465. m_fontFilename.SetAssetPath(fontPath.c_str());
  466. // We should minimize what is done in constructors and Init since components may be constructed
  467. // in RC or other tools. Currrently this method is called from the constructor and Init.
  468. if (gEnv && gEnv->pCryFont &&
  469. (!m_fontFamily || gEnv->pCryFont->GetFontFamily(fontPath.c_str()) != m_fontFamily))
  470. {
  471. AZStd::string fileName = fontPath;
  472. if (fileName.empty())
  473. {
  474. fileName = "default-ui";
  475. }
  476. FontFamilyPtr fontFamily = gEnv->pCryFont->GetFontFamily(fileName.c_str());
  477. if (!fontFamily)
  478. {
  479. fontFamily = gEnv->pCryFont->LoadFontFamily(fileName.c_str());
  480. if (!fontFamily)
  481. {
  482. CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Error loading a font from %s.", fileName.c_str());
  483. }
  484. }
  485. if (fontFamily)
  486. {
  487. m_fontFamily = fontFamily;
  488. // we know that the input path is a root relative and normalized pathname
  489. m_fontFilename.SetAssetPath(fileName.c_str());
  490. // the font has changed so check that the font effect is valid
  491. unsigned int numEffects = m_fontFamily ? m_fontFamily->normal->GetNumEffects() : 0;
  492. if (m_fontEffectIndex >= numEffects)
  493. {
  494. m_fontEffectIndex = 0;
  495. AZ_Warning("UiInteractableState", false, "Font effect index is out of range for changed font, resetting index to 0");
  496. }
  497. }
  498. }
  499. }
  500. ////////////////////////////////////////////////////////////////////////////////////////////////////
  501. UiInteractableStateFont::EntityComboBoxVec UiInteractableStateFont::PopulateTargetEntityList()
  502. {
  503. return UiInteractableStateAction::PopulateTargetEntityList();
  504. }
  505. ////////////////////////////////////////////////////////////////////////////////////////////////////
  506. UiInteractableStateFont::FontEffectComboBoxVec UiInteractableStateFont::PopulateFontEffectList()
  507. {
  508. FontEffectComboBoxVec result;
  509. AZStd::vector<AZ::EntityId> entityIdList;
  510. // there is always a valid font since we default to "default-ui"
  511. // so just get the effects from the font and add their names to the result list
  512. // NOTE: Curently, in order for this to work, when the font is changed we need to do
  513. // "RefreshEntireTree" to get the combo box list refreshed.
  514. unsigned int numEffects = m_fontFamily ? m_fontFamily->normal->GetNumEffects() : 0;
  515. for (unsigned int i = 0; i < numEffects; ++i)
  516. {
  517. const char* name = m_fontFamily->normal->GetEffectName(i);
  518. result.push_back(AZStd::make_pair(i, name));
  519. }
  520. return result;
  521. }
  522. ////////////////////////////////////////////////////////////////////////////////////////////////////
  523. void UiInteractableStateFont::OnFontPathnameChange()
  524. {
  525. AZStd::string fontPath = m_fontFilename.GetAssetPath();
  526. SetFontPathname(fontPath);
  527. }
  528. ////////////////////////////////////////////////////////////////////////////////////////////////////
  529. void UiInteractableStateFont::Reflect(AZ::ReflectContext* context)
  530. {
  531. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  532. if (serializeContext)
  533. {
  534. serializeContext->Class<UiInteractableStateFont, UiInteractableStateAction>()
  535. ->Version(2)
  536. ->Field("TargetEntity", &UiInteractableStateFont::m_targetEntity)
  537. ->Field("FontFileName", &UiInteractableStateFont::m_fontFilename)
  538. ->Field("EffectIndex", &UiInteractableStateFont::m_fontEffectIndex);
  539. AZ::EditContext* ec = serializeContext->GetEditContext();
  540. if (ec)
  541. {
  542. auto editInfo = ec->Class<UiInteractableStateFont>("Font", "Overrides the font on the target element.");
  543. editInfo->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  544. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  545. editInfo->DataElement("ComboBox", &UiInteractableStateFont::m_targetEntity, "Target", "The target element.")
  546. ->Attribute("EnumValues", &UiInteractableStateFont::PopulateTargetEntityList);
  547. editInfo->DataElement("SimpleAssetRef", &UiInteractableStateFont::m_fontFilename, "Font path", "The font asset pathname.")
  548. ->Attribute("ChangeNotify", &UiInteractableStateFont::OnFontPathnameChange)
  549. ->Attribute("ChangeNotify", AZ_CRC_CE("RefreshEntireTree"));
  550. editInfo->DataElement("ComboBox", &UiInteractableStateFont::m_fontEffectIndex, "Font effect", "The font effect (from font file).")
  551. ->Attribute("EnumValues", &UiInteractableStateFont::PopulateFontEffectList);
  552. }
  553. }
  554. }