UIComponent.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. //
  2. // Copyright (c) 2014-2017, THUNDERBEAST GAMES LLC All rights reserved
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include "../Core/Context.h"
  23. #include "../IO/Log.h"
  24. #include "../Resource/ResourceCache.h"
  25. #include "../Graphics/Graphics.h"
  26. #include "../Graphics/Texture2D.h"
  27. #include "../Graphics/Technique.h"
  28. #include "../Graphics/Material.h"
  29. #include "../Graphics/Octree.h"
  30. #include "../Graphics/OctreeQuery.h"
  31. #include "../Graphics/Camera.h"
  32. #include "../Graphics/BillboardSet.h"
  33. #include "../Graphics/StaticModel.h"
  34. #include "../Graphics/Renderer.h"
  35. #include "../Scene/Node.h"
  36. #include "../Scene/Scene.h"
  37. #include "../Scene/SceneEvents.h"
  38. #include "UIView.h"
  39. #include "UIComponent.h"
  40. #include <Atomic/Input/Input.h>
  41. #include <Atomic/Input/InputEvents.h>
  42. #include <Atomic/UI/UI.h>
  43. #include <Atomic/UI/UIEvents.h>
  44. namespace Atomic
  45. {
  46. static const IntVector2 DEFAULT_UICOMPONENT_SIZE(UIVIEW_DEFAULT_TEXTURE_SIZE, UIVIEW_DEFAULT_TEXTURE_SIZE);
  47. UIComponent::UIComponent(Context* context) :
  48. Component(context),
  49. viewSize_(IntVector2(UIVIEW_DEFAULT_TEXTURE_SIZE, UIVIEW_DEFAULT_TEXTURE_SIZE)),
  50. mouseScreenRect_(IntRect::ZERO)
  51. {
  52. }
  53. UIComponent::~UIComponent()
  54. {
  55. }
  56. void UIComponent::RegisterObject(Context* context)
  57. {
  58. context->RegisterFactory<UIComponent>();
  59. ATOMIC_ACCESSOR_ATTRIBUTE("Size", GetSizeAttr, SetSizeAttr, IntVector2, DEFAULT_UICOMPONENT_SIZE, AM_DEFAULT);
  60. }
  61. const IntVector2& UIComponent::GetSizeAttr() const
  62. {
  63. return viewSize_;
  64. }
  65. void UIComponent::SetSizeAttr(const IntVector2& value)
  66. {
  67. viewSize_ = value;
  68. if (uiView_.NotNull())
  69. {
  70. uiView_->SetSize(viewSize_.x_, viewSize_.y_);
  71. }
  72. }
  73. void UIComponent::SetStaticModel(StaticModel* staticModel)
  74. {
  75. staticModel_ = staticModel_;
  76. }
  77. StaticModel* UIComponent::GetStaticModel() const
  78. {
  79. return staticModel_;
  80. }
  81. void UIComponent::SetUIView(UIView* view)
  82. {
  83. uiView_ = view;
  84. viewSize_.x_ = uiView_->GetWidth();
  85. viewSize_.y_ = uiView_->GetHeight();
  86. view->SetRenderToTexture(true, viewSize_.x_, viewSize_.y_);
  87. }
  88. void UIComponent::OnSetEnabled()
  89. {
  90. Component::OnSetEnabled();
  91. UpdateEventSubscriptions(IsEnabledEffective());
  92. }
  93. void UIComponent::UpdateMouseEventSubscriptions(bool unsubscribe)
  94. {
  95. if (!unsubscribe)
  96. {
  97. SubscribeToEvent(E_MOUSEBUTTONDOWN, ATOMIC_HANDLER(UIComponent, HandleMouseButtonDown));
  98. SubscribeToEvent(E_MOUSEBUTTONUP, ATOMIC_HANDLER(UIComponent, HandleMouseButtonUp));
  99. SubscribeToEvent(E_MOUSEMOVE, ATOMIC_HANDLER(UIComponent, HandleMouseMove));
  100. SubscribeToEvent(E_MOUSEWHEEL, ATOMIC_HANDLER(UIComponent, HandleMouseWheel));
  101. }
  102. else
  103. {
  104. UnsubscribeFromEvent(E_MOUSEBUTTONDOWN);
  105. UnsubscribeFromEvent(E_MOUSEBUTTONUP);
  106. UnsubscribeFromEvent(E_MOUSEMOVE);
  107. UnsubscribeFromEvent(E_MOUSEWHEEL);
  108. }
  109. }
  110. void UIComponent::UpdateEventSubscriptions(bool subscribe)
  111. {
  112. Scene* scene = GetScene();
  113. if (subscribe && scene)
  114. {
  115. SubscribeToEvent(scene, E_SCENEPOSTUPDATE, ATOMIC_HANDLER(UIComponent, HandleScenePostUpdate));
  116. UpdateMouseEventSubscriptions();
  117. }
  118. else
  119. {
  120. UpdateMouseEventSubscriptions(true);
  121. UnsubscribeFromEvent(E_SCENEPOSTUPDATE);
  122. }
  123. }
  124. void UIComponent::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
  125. {
  126. using namespace ScenePostUpdate;
  127. float timeStep = eventData[P_TIMESTEP].GetFloat();
  128. if (!IsEnabledEffective())
  129. {
  130. return;
  131. }
  132. // TODO: check visibility
  133. // early out if nothing to render
  134. if (staticModel_.Null())
  135. {
  136. staticModel_ = node_->GetComponent<StaticModel>();
  137. if (staticModel_.Null())
  138. {
  139. return;
  140. }
  141. }
  142. // if we don't have a view, let's make it
  143. if (uiView_.Null())
  144. {
  145. UIView* view = new UIView(context_);
  146. view->SetSize(viewSize_.x_, viewSize_.y_);
  147. SetUIView(view);
  148. }
  149. if (!staticModel_->GetMaterial())
  150. {
  151. if (defaultMaterial_.Null())
  152. {
  153. ResourceCache* cache = GetSubsystem<ResourceCache>();
  154. defaultMaterial_ = new Material(context_);
  155. defaultMaterial_->SetTechnique(0, cache->GetResource<Technique>("Techniques/Diff.xml"));
  156. defaultMaterial_->SetTexture(Atomic::TU_DIFFUSE, uiView_->GetRenderTexture());
  157. }
  158. staticModel_->SetMaterial(defaultMaterial_);
  159. }
  160. }
  161. Viewport* UIComponent::GetViewport() const
  162. {
  163. if (!GetScene())
  164. return 0;
  165. Renderer* renderer = GetSubsystem<Renderer>();
  166. for (unsigned i = 0; i < renderer->GetNumViewports(); ++i)
  167. {
  168. Viewport* viewport = renderer->GetViewport(i);
  169. // Find viewport with same scene
  170. // TODO: support multiple viewports
  171. if (viewport && viewport->GetScene() == GetScene())
  172. {
  173. return viewport;
  174. }
  175. }
  176. return 0;
  177. }
  178. bool UIComponent::CalcUIViewPos(const IntVector2& screenPos, IntVector2& viewPos)
  179. {
  180. if (staticModel_.Null() || uiView_.Null())
  181. {
  182. return false;
  183. }
  184. Viewport* viewport = GetViewport();
  185. if (!viewport)
  186. {
  187. return false;
  188. }
  189. Scene* scene = GetScene();
  190. Camera* camera = viewport->GetCamera();
  191. Octree* octree = scene->GetComponent<Octree>();
  192. bool rectIsDefault = mouseScreenRect_ == IntRect::ZERO;
  193. if (!camera || !octree || (!rectIsDefault && !mouseScreenRect_.IsInside(screenPos)))
  194. {
  195. return false;
  196. }
  197. IntRect vpRect = viewport->GetRect();
  198. if (vpRect == IntRect::ZERO)
  199. {
  200. Graphics* graphics = GetSubsystem<Graphics>();
  201. vpRect.right_ = graphics->GetWidth();
  202. vpRect.bottom_ = graphics->GetHeight();
  203. }
  204. Vector2 normPos(screenPos.x_ - mouseScreenRect_.left_, screenPos.y_ - mouseScreenRect_.top_);
  205. normPos /= rectIsDefault ? Vector2(vpRect.Width(), vpRect.Height()) : Vector2(mouseScreenRect_.Width(), mouseScreenRect_.Height());
  206. Ray ray(camera->GetScreenRay(normPos.x_, normPos.y_));
  207. PODVector<RayQueryResult> queryResultVector;
  208. RayOctreeQuery query(queryResultVector, ray, RAY_TRIANGLE_UV, M_INFINITY, DRAWABLE_GEOMETRY, DEFAULT_VIEWMASK);
  209. octree->Raycast(query);
  210. if (queryResultVector.Empty())
  211. return false;
  212. for (unsigned i = 0; i < queryResultVector.Size(); i++)
  213. {
  214. RayQueryResult& queryResult = queryResultVector[i];
  215. if (queryResult.drawable_ != staticModel_)
  216. {
  217. // ignore billboard sets by default
  218. if (queryResult.drawable_->GetTypeInfo()->IsTypeOf(BillboardSet::GetTypeStatic()))
  219. {
  220. continue;
  221. }
  222. return false;
  223. }
  224. Vector2& uv = queryResult.textureUV_;
  225. viewPos = IntVector2(uv.x_ * uiView_->GetWidth(), uv.y_ * uiView_->GetHeight());
  226. return true;
  227. }
  228. return false;
  229. }
  230. bool UIComponent::FilterInput() const
  231. {
  232. if (uiView_.Null() || staticModel_.Null() || !uiView_->GetMouseEnabled() || !uiView_->GetFocus())
  233. {
  234. return true;
  235. }
  236. return false;
  237. }
  238. void UIComponent::HandleMouseMove(StringHash eventType, VariantMap& eventData)
  239. {
  240. if (FilterInput())
  241. {
  242. return;
  243. }
  244. using namespace MouseMove;
  245. IntVector2 screenPos(eventData[P_X].GetInt(), eventData[P_Y].GetInt());
  246. IntVector2 viewPos;
  247. if (!CalcUIViewPos(screenPos, viewPos))
  248. {
  249. return;
  250. }
  251. tb::TBWidget *widget = uiView_->GetInternalWidget();
  252. widget->InvokePointerMove(viewPos.x_, viewPos.y_, tb::TB_MODIFIER_NONE, false);
  253. }
  254. // this is duplicated in UIView.cpp, don't want to expose in header
  255. // possible need a static TB <-> Atomic UI helper class to encapsulate
  256. static tb::MODIFIER_KEYS GetModifierKeys(int qualifiers, bool superKey)
  257. {
  258. tb::MODIFIER_KEYS code = tb::TB_MODIFIER_NONE;
  259. if (qualifiers & QUAL_ALT) code |= tb::TB_ALT;
  260. if (qualifiers & QUAL_CTRL) code |= tb::TB_CTRL;
  261. if (qualifiers & QUAL_SHIFT) code |= tb::TB_SHIFT;
  262. if (superKey) code |= tb::TB_SUPER;
  263. return code;
  264. }
  265. void UIComponent::HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
  266. {
  267. if (FilterInput())
  268. {
  269. return;
  270. }
  271. using namespace MouseButtonDown;
  272. Input* input = GetSubsystem<Input>();
  273. unsigned button = eventData[P_BUTTON].GetUInt();
  274. IntVector2 screenPos = GetSubsystem<Input>()->GetMousePosition();
  275. IntVector2 viewPos;
  276. if (!CalcUIViewPos(screenPos, viewPos))
  277. {
  278. return;
  279. }
  280. int qualifiers = input->GetQualifiers();
  281. #ifdef ATOMIC_PLATFORM_WINDOWS
  282. bool superdown = input->GetKeyDown(KEY_LCTRL) || input->GetKeyDown(KEY_RCTRL);
  283. #else
  284. bool superdown = input->GetKeyDown(KEY_LGUI) || input->GetKeyDown(KEY_RGUI);
  285. #endif
  286. tb::TBWidget *widget = uiView_->GetInternalWidget();
  287. const int clickCount = 1;
  288. tb::MODIFIER_KEYS mod = GetModifierKeys(qualifiers, superdown);
  289. if (button == MOUSEB_RIGHT)
  290. widget->InvokeRightPointerDown(viewPos.x_, viewPos.y_, clickCount, mod);
  291. else
  292. widget->InvokePointerDown(viewPos.x_, viewPos.y_, clickCount, mod, false);
  293. }
  294. void UIComponent::HandleMouseButtonUp(StringHash eventType, VariantMap& eventData)
  295. {
  296. if (FilterInput())
  297. {
  298. return;
  299. }
  300. using namespace MouseButtonUp;
  301. Input* input = GetSubsystem<Input>();
  302. unsigned button = eventData[P_BUTTON].GetUInt();
  303. IntVector2 screenPos = GetSubsystem<Input>()->GetMousePosition();
  304. IntVector2 viewPos;
  305. if (!CalcUIViewPos(screenPos, viewPos))
  306. {
  307. return;
  308. }
  309. int qualifiers = input->GetQualifiers();
  310. #ifdef ATOMIC_PLATFORM_WINDOWS
  311. bool superdown = input->GetKeyDown(KEY_LCTRL) || input->GetKeyDown(KEY_RCTRL);
  312. #else
  313. bool superdown = input->GetKeyDown(KEY_LGUI) || input->GetKeyDown(KEY_RGUI);
  314. #endif
  315. tb::MODIFIER_KEYS mod = GetModifierKeys(qualifiers, superdown);
  316. tb::TBWidget *widget = uiView_->GetInternalWidget();
  317. if (button == MOUSEB_RIGHT)
  318. widget->InvokeRightPointerUp(viewPos.x_, viewPos.y_, mod);
  319. else
  320. widget->InvokePointerUp(viewPos.x_, viewPos.y_, mod, false);
  321. }
  322. void UIComponent::HandleMouseWheel(StringHash eventType, VariantMap& eventData)
  323. {
  324. if (FilterInput())
  325. {
  326. return;
  327. }
  328. using namespace MouseWheel;
  329. if (uiView_.Null() || staticModel_.Null())
  330. {
  331. return;
  332. }
  333. int delta = eventData[P_WHEEL].GetInt();
  334. Input* input = GetSubsystem<Input>();
  335. tb::TBWidget *widget = uiView_->GetInternalWidget();
  336. widget->InvokeWheel(input->GetMousePosition().x_, input->GetMousePosition().y_, 0, -delta, tb::TB_MODIFIER_NONE);
  337. }
  338. void UIComponent::OnSceneSet(Scene* scene)
  339. {
  340. Component::OnSceneSet(scene);
  341. if (!scene)
  342. {
  343. UpdateEventSubscriptions(false);
  344. }
  345. else
  346. {
  347. UpdateEventSubscriptions(IsEnabledEffective());
  348. }
  349. }
  350. void UIComponent::OnNodeSet(Node* node)
  351. {
  352. Component::OnNodeSet(node);
  353. }
  354. }