UI.cpp 77 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236
  1. //
  2. // Copyright (c) 2008-2022 the Urho3D project.
  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 "../Precompiled.h"
  23. #include "../Core/Context.h"
  24. #include "../Core/CoreEvents.h"
  25. #include "../Core/Profiler.h"
  26. #include "../Container/Sort.h"
  27. #include "../Graphics/Graphics.h"
  28. #include "../Graphics/GraphicsEvents.h"
  29. #include "../Graphics/Shader.h"
  30. #include "../Graphics/ShaderVariation.h"
  31. #include "../Graphics/Texture2D.h"
  32. #include "../Graphics/VertexBuffer.h"
  33. #include "../Graphics/Octree.h"
  34. #include "../Graphics/Viewport.h"
  35. #include "../Graphics/Camera.h"
  36. #include "../Graphics/Technique.h"
  37. #include "../Scene/Scene.h"
  38. #include "../Input/Input.h"
  39. #include "../Input/InputEvents.h"
  40. #include "../IO/Log.h"
  41. #include "../Math/Matrix3x4.h"
  42. #include "../Resource/ResourceCache.h"
  43. #include "../UI/CheckBox.h"
  44. #include "../UI/Cursor.h"
  45. #include "../UI/DropDownList.h"
  46. #include "../UI/FileSelector.h"
  47. #include "../UI/Font.h"
  48. #include "../UI/LineEdit.h"
  49. #include "../UI/ListView.h"
  50. #include "../UI/MessageBox.h"
  51. #include "../UI/ProgressBar.h"
  52. #include "../UI/ScrollBar.h"
  53. #include "../UI/Slider.h"
  54. #include "../UI/Sprite.h"
  55. #include "../UI/Text.h"
  56. #include "../UI/Text3D.h"
  57. #include "../UI/ToolTip.h"
  58. #include "../UI/UI.h"
  59. #include "../UI/UIEvents.h"
  60. #include "../UI/Window.h"
  61. #include "../UI/View3D.h"
  62. #include "../UI/UIComponent.h"
  63. #include <cassert>
  64. #include <SDL/SDL.h>
  65. #include "../DebugNew.h"
  66. namespace Urho3D
  67. {
  68. static MouseButton MakeTouchIDMask(int id)
  69. {
  70. return static_cast<MouseButton>(1u << static_cast<MouseButtonFlags::Integer>(id)); // NOLINT(misc-misplaced-widening-cast)
  71. }
  72. StringHash VAR_ORIGIN("Origin");
  73. const StringHash VAR_ORIGINAL_PARENT("OriginalParent");
  74. const StringHash VAR_ORIGINAL_CHILD_INDEX("OriginalChildIndex");
  75. const StringHash VAR_PARENT_CHANGED("ParentChanged");
  76. const float DEFAULT_DOUBLECLICK_INTERVAL = 0.5f;
  77. const float DEFAULT_DRAGBEGIN_INTERVAL = 0.5f;
  78. const float DEFAULT_TOOLTIP_DELAY = 0.5f;
  79. const int DEFAULT_DRAGBEGIN_DISTANCE = 5;
  80. const int DEFAULT_FONT_TEXTURE_MAX_SIZE = 2048;
  81. const char* UI_CATEGORY = "UI";
  82. UI::UI(Context* context) :
  83. Object(context),
  84. rootElement_(new UIElement(context)),
  85. rootModalElement_(new UIElement(context)),
  86. doubleClickInterval_(DEFAULT_DOUBLECLICK_INTERVAL),
  87. dragBeginInterval_(DEFAULT_DRAGBEGIN_INTERVAL),
  88. defaultToolTipDelay_(DEFAULT_TOOLTIP_DELAY),
  89. dragBeginDistance_(DEFAULT_DRAGBEGIN_DISTANCE),
  90. mouseButtons_(0),
  91. lastMouseButtons_(0),
  92. maxDoubleClickDist_(M_LARGE_VALUE),
  93. qualifiers_(0),
  94. maxFontTextureSize_(DEFAULT_FONT_TEXTURE_MAX_SIZE),
  95. initialized_(false),
  96. usingTouchInput_(false),
  97. #ifdef _WIN32
  98. nonFocusedMouseWheel_(false), // Default MS Windows behaviour
  99. #else
  100. nonFocusedMouseWheel_(true), // Default Mac OS X and Linux behaviour
  101. #endif
  102. useSystemClipboard_(false),
  103. #if defined(__ANDROID__) || defined(IOS) || defined(TVOS)
  104. useScreenKeyboard_(true),
  105. #else
  106. useScreenKeyboard_(false),
  107. #endif
  108. useMutableGlyphs_(false),
  109. forceAutoHint_(false),
  110. fontHintLevel_(FONT_HINT_LEVEL_NORMAL),
  111. fontSubpixelThreshold_(12),
  112. fontOversampling_(2),
  113. uiRendered_(false),
  114. nonModalBatchSize_(0),
  115. dragElementsCount_(0),
  116. dragConfirmedCount_(0),
  117. uiScale_(1.0f),
  118. customSize_(IntVector2::ZERO)
  119. {
  120. rootElement_->SetTraversalMode(TM_DEPTH_FIRST);
  121. rootModalElement_->SetTraversalMode(TM_DEPTH_FIRST);
  122. // Register UI library object factories
  123. RegisterUILibrary(context_);
  124. SubscribeToEvent(E_SCREENMODE, URHO3D_HANDLER(UI, HandleScreenMode));
  125. SubscribeToEvent(E_MOUSEBUTTONDOWN, URHO3D_HANDLER(UI, HandleMouseButtonDown));
  126. SubscribeToEvent(E_MOUSEBUTTONUP, URHO3D_HANDLER(UI, HandleMouseButtonUp));
  127. SubscribeToEvent(E_MOUSEMOVE, URHO3D_HANDLER(UI, HandleMouseMove));
  128. SubscribeToEvent(E_MOUSEWHEEL, URHO3D_HANDLER(UI, HandleMouseWheel));
  129. SubscribeToEvent(E_TOUCHBEGIN, URHO3D_HANDLER(UI, HandleTouchBegin));
  130. SubscribeToEvent(E_TOUCHEND, URHO3D_HANDLER(UI, HandleTouchEnd));
  131. SubscribeToEvent(E_TOUCHMOVE, URHO3D_HANDLER(UI, HandleTouchMove));
  132. SubscribeToEvent(E_KEYDOWN, URHO3D_HANDLER(UI, HandleKeyDown));
  133. SubscribeToEvent(E_TEXTINPUT, URHO3D_HANDLER(UI, HandleTextInput));
  134. SubscribeToEvent(E_DROPFILE, URHO3D_HANDLER(UI, HandleDropFile));
  135. // Try to initialize right now, but skip if screen mode is not yet set
  136. Initialize();
  137. }
  138. UI::~UI() = default;
  139. void UI::SetCursor(Cursor* cursor)
  140. {
  141. if (cursor_ == cursor)
  142. return;
  143. // Remove old cursor (if any) and set new
  144. if (cursor_)
  145. {
  146. rootElement_->RemoveChild(cursor_);
  147. cursor_.Reset();
  148. }
  149. if (cursor)
  150. {
  151. rootElement_->AddChild(cursor);
  152. cursor_ = cursor;
  153. IntVector2 pos = cursor_->GetPosition();
  154. const IntVector2& rootSize = rootElement_->GetSize();
  155. const IntVector2& rootPos = rootElement_->GetPosition();
  156. pos.x_ = Clamp(pos.x_, rootPos.x_, rootPos.x_ + rootSize.x_ - 1);
  157. pos.y_ = Clamp(pos.y_, rootPos.y_, rootPos.y_ + rootSize.y_ - 1);
  158. cursor_->SetPosition(pos);
  159. }
  160. }
  161. void UI::SetFocusElement(UIElement* element, bool byKey)
  162. {
  163. using namespace FocusChanged;
  164. UIElement* originalElement = element;
  165. if (element)
  166. {
  167. // Return if already has focus
  168. if (focusElement_ == element)
  169. return;
  170. // Only allow child elements of the modal element to receive focus
  171. if (HasModalElement())
  172. {
  173. UIElement* topLevel = element->GetParent();
  174. while (topLevel && topLevel->GetParent() != rootElement_)
  175. topLevel = topLevel->GetParent();
  176. if (topLevel) // If parented to non-modal root then ignore
  177. return;
  178. }
  179. // Search for an element in the hierarchy that can alter focus. If none found, exit
  180. element = GetFocusableElement(element);
  181. if (!element)
  182. return;
  183. }
  184. // Remove focus from the old element
  185. if (focusElement_)
  186. {
  187. UIElement* oldFocusElement = focusElement_;
  188. focusElement_.Reset();
  189. VariantMap& focusEventData = GetEventDataMap();
  190. focusEventData[Defocused::P_ELEMENT] = oldFocusElement;
  191. oldFocusElement->SendEvent(E_DEFOCUSED, focusEventData);
  192. }
  193. // Then set focus to the new
  194. if (element && element->GetFocusMode() >= FM_FOCUSABLE)
  195. {
  196. focusElement_ = element;
  197. VariantMap& focusEventData = GetEventDataMap();
  198. focusEventData[Focused::P_ELEMENT] = element;
  199. focusEventData[Focused::P_BYKEY] = byKey;
  200. element->SendEvent(E_FOCUSED, focusEventData);
  201. }
  202. VariantMap& eventData = GetEventDataMap();
  203. eventData[P_CLICKEDELEMENT] = originalElement;
  204. eventData[P_ELEMENT] = element;
  205. SendEvent(E_FOCUSCHANGED, eventData);
  206. }
  207. bool UI::SetModalElement(UIElement* modalElement, bool enable)
  208. {
  209. if (!modalElement)
  210. return false;
  211. // Currently only allow modal window
  212. if (modalElement->GetType() != Window::GetTypeStatic())
  213. return false;
  214. assert(rootModalElement_);
  215. UIElement* currParent = modalElement->GetParent();
  216. if (enable)
  217. {
  218. // Make sure it is not already the child of the root modal element
  219. if (currParent == rootModalElement_)
  220. return false;
  221. // Adopt modal root as parent
  222. modalElement->SetVar(VAR_ORIGINAL_PARENT, currParent);
  223. modalElement->SetVar(VAR_ORIGINAL_CHILD_INDEX, currParent ? currParent->FindChild(modalElement) : M_MAX_UNSIGNED);
  224. modalElement->SetParent(rootModalElement_);
  225. // If it is a popup element, bring along its top-level parent
  226. auto* originElement = static_cast<UIElement*>(modalElement->GetVar(VAR_ORIGIN).GetPtr());
  227. if (originElement)
  228. {
  229. UIElement* element = originElement;
  230. while (element && element->GetParent() != rootElement_)
  231. element = element->GetParent();
  232. if (element)
  233. {
  234. originElement->SetVar(VAR_PARENT_CHANGED, element);
  235. UIElement* oriParent = element->GetParent();
  236. element->SetVar(VAR_ORIGINAL_PARENT, oriParent);
  237. element->SetVar(VAR_ORIGINAL_CHILD_INDEX, oriParent ? oriParent->FindChild(element) : M_MAX_UNSIGNED);
  238. element->SetParent(rootModalElement_);
  239. }
  240. }
  241. return true;
  242. }
  243. else
  244. {
  245. // Only the modal element can disable itself
  246. if (currParent != rootModalElement_)
  247. return false;
  248. // Revert back to original parent
  249. modalElement->SetParent(static_cast<UIElement*>(modalElement->GetVar(VAR_ORIGINAL_PARENT).GetPtr()),
  250. modalElement->GetVar(VAR_ORIGINAL_CHILD_INDEX).GetUInt());
  251. auto& vars = const_cast<VariantMap&>(modalElement->GetVars());
  252. vars.Erase(VAR_ORIGINAL_PARENT);
  253. vars.Erase(VAR_ORIGINAL_CHILD_INDEX);
  254. // If it is a popup element, revert back its top-level parent
  255. auto* originElement = static_cast<UIElement*>(modalElement->GetVar(VAR_ORIGIN).GetPtr());
  256. if (originElement)
  257. {
  258. auto* element = static_cast<UIElement*>(originElement->GetVar(VAR_PARENT_CHANGED).GetPtr());
  259. if (element)
  260. {
  261. const_cast<VariantMap&>(originElement->GetVars()).Erase(VAR_PARENT_CHANGED);
  262. element->SetParent(static_cast<UIElement*>(element->GetVar(VAR_ORIGINAL_PARENT).GetPtr()),
  263. element->GetVar(VAR_ORIGINAL_CHILD_INDEX).GetUInt());
  264. vars = const_cast<VariantMap&>(element->GetVars());
  265. vars.Erase(VAR_ORIGINAL_PARENT);
  266. vars.Erase(VAR_ORIGINAL_CHILD_INDEX);
  267. }
  268. }
  269. return true;
  270. }
  271. }
  272. void UI::Clear()
  273. {
  274. rootElement_->RemoveAllChildren();
  275. rootModalElement_->RemoveAllChildren();
  276. if (cursor_)
  277. rootElement_->AddChild(cursor_);
  278. }
  279. void UI::Update(float timeStep)
  280. {
  281. assert(rootElement_ && rootModalElement_);
  282. URHO3D_PROFILE(UpdateUI);
  283. // Expire hovers
  284. for (HashMap<WeakPtr<UIElement>, bool>::Iterator i = hoveredElements_.Begin(); i != hoveredElements_.End(); ++i)
  285. i->second_ = false;
  286. auto* input = GetSubsystem<Input>();
  287. bool mouseGrabbed = input->IsMouseGrabbed();
  288. IntVector2 cursorPos;
  289. bool cursorVisible;
  290. GetCursorPositionAndVisible(cursorPos, cursorVisible);
  291. // Drag begin based on time
  292. if (dragElementsCount_ > 0 && !mouseGrabbed)
  293. {
  294. for (HashMap<WeakPtr<UIElement>, UI::DragData*>::Iterator i = dragElements_.Begin(); i != dragElements_.End();)
  295. {
  296. WeakPtr<UIElement> dragElement = i->first_;
  297. UI::DragData* dragData = i->second_;
  298. if (!dragElement)
  299. {
  300. i = DragElementErase(i);
  301. continue;
  302. }
  303. if (!dragData->dragBeginPending)
  304. {
  305. ++i;
  306. continue;
  307. }
  308. if (dragData->dragBeginTimer.GetMSec(false) >= (unsigned)(dragBeginInterval_ * 1000))
  309. {
  310. dragData->dragBeginPending = false;
  311. IntVector2 beginSendPos = dragData->dragBeginSumPos / dragData->numDragButtons;
  312. dragConfirmedCount_++;
  313. if (!usingTouchInput_)
  314. dragElement->OnDragBegin(dragElement->ScreenToElement(beginSendPos), beginSendPos, dragData->dragButtons,
  315. qualifiers_, cursor_);
  316. else
  317. dragElement->OnDragBegin(dragElement->ScreenToElement(beginSendPos), beginSendPos, dragData->dragButtons, QUAL_NONE, nullptr);
  318. SendDragOrHoverEvent(E_DRAGBEGIN, dragElement, beginSendPos, IntVector2::ZERO, dragData);
  319. }
  320. ++i;
  321. }
  322. }
  323. // Mouse hover
  324. if (!mouseGrabbed && !input->GetTouchEmulation())
  325. {
  326. if (!usingTouchInput_ && cursorVisible)
  327. ProcessHover(cursorPos, mouseButtons_, qualifiers_, cursor_);
  328. }
  329. // Touch hover
  330. unsigned numTouches = input->GetNumTouches();
  331. for (unsigned i = 0; i < numTouches; ++i)
  332. {
  333. TouchState* touch = input->GetTouch(i);
  334. IntVector2 touchPos = touch->position_;
  335. touchPos = ConvertSystemToUI(touchPos);
  336. ProcessHover(touchPos, MakeTouchIDMask(touch->touchID_), QUAL_NONE, nullptr);
  337. }
  338. // End hovers that expired without refreshing
  339. for (HashMap<WeakPtr<UIElement>, bool>::Iterator i = hoveredElements_.Begin(); i != hoveredElements_.End();)
  340. {
  341. if (i->first_.Expired() || !i->second_)
  342. {
  343. UIElement* element = i->first_;
  344. if (element)
  345. {
  346. using namespace HoverEnd;
  347. VariantMap& eventData = GetEventDataMap();
  348. eventData[P_ELEMENT] = element;
  349. element->SendEvent(E_HOVEREND, eventData);
  350. }
  351. i = hoveredElements_.Erase(i);
  352. }
  353. else
  354. ++i;
  355. }
  356. Update(timeStep, rootElement_);
  357. Update(timeStep, rootModalElement_);
  358. }
  359. void UI::RenderUpdate()
  360. {
  361. assert(rootElement_ && rootModalElement_ && graphics_);
  362. URHO3D_PROFILE(GetUIBatches);
  363. uiRendered_ = false;
  364. // If the OS cursor is visible, do not render the UI's own cursor
  365. bool osCursorVisible = GetSubsystem<Input>()->IsMouseVisible();
  366. // Get rendering batches from the non-modal UI elements
  367. batches_.Clear();
  368. vertexData_.Clear();
  369. const IntVector2& rootSize = rootElement_->GetSize();
  370. const IntVector2& rootPos = rootElement_->GetPosition();
  371. // Note: the scissors operate on unscaled coordinates. Scissor scaling is only performed during render
  372. IntRect currentScissor = IntRect(rootPos.x_, rootPos.y_, rootPos.x_ + rootSize.x_, rootPos.y_ + rootSize.y_);
  373. if (rootElement_->IsVisible())
  374. GetBatches(batches_, vertexData_, rootElement_, currentScissor);
  375. // Save the batch size of the non-modal batches for later use
  376. nonModalBatchSize_ = batches_.Size();
  377. // Get rendering batches from the modal UI elements
  378. GetBatches(batches_, vertexData_, rootModalElement_, currentScissor);
  379. // Get batches from the cursor (and its possible children) last to draw it on top of everything
  380. if (cursor_ && cursor_->IsVisible() && !osCursorVisible)
  381. {
  382. currentScissor = IntRect(0, 0, rootSize.x_, rootSize.y_);
  383. cursor_->GetBatches(batches_, vertexData_, currentScissor);
  384. GetBatches(batches_, vertexData_, cursor_, currentScissor);
  385. }
  386. // Get batches for UI elements rendered into textures. Each element rendered into texture is treated as root element.
  387. for (auto it = renderToTexture_.Begin(); it != renderToTexture_.End();)
  388. {
  389. RenderToTextureData& data = it->second_;
  390. if (data.rootElement_.Expired())
  391. {
  392. it = renderToTexture_.Erase(it);
  393. continue;
  394. }
  395. if (data.rootElement_->IsEnabled())
  396. {
  397. data.batches_.Clear();
  398. data.vertexData_.Clear();
  399. UIElement* element = data.rootElement_;
  400. const IntVector2& size = element->GetSize();
  401. const IntVector2& pos = element->GetPosition();
  402. // Note: the scissors operate on unscaled coordinates. Scissor scaling is only performed during render
  403. IntRect scissor = IntRect(pos.x_, pos.y_, pos.x_ + size.x_, pos.y_ + size.y_);
  404. GetBatches(data.batches_, data.vertexData_, element, scissor);
  405. // UIElement does not have anything to show. Insert dummy batch that will clear the texture.
  406. if (data.batches_.Empty())
  407. {
  408. UIBatch batch(element, BLEND_REPLACE, scissor, nullptr, &data.vertexData_);
  409. batch.SetColor(Color::BLACK);
  410. batch.AddQuad(scissor.left_, scissor.top_, scissor.right_, scissor.bottom_, 0, 0);
  411. data.batches_.Push(batch);
  412. }
  413. }
  414. ++it;
  415. }
  416. }
  417. void UI::Render(bool renderUICommand)
  418. {
  419. URHO3D_PROFILE(RenderUI);
  420. // If the OS cursor is visible, apply its shape now if changed
  421. if (!renderUICommand)
  422. {
  423. bool osCursorVisible = GetSubsystem<Input>()->IsMouseVisible();
  424. if (cursor_ && osCursorVisible)
  425. cursor_->ApplyOSCursorShape();
  426. }
  427. // Perform the default backbuffer render only if not rendered yet, or additional renders through RenderUI command
  428. if (renderUICommand || !uiRendered_)
  429. {
  430. SetVertexData(vertexBuffer_, vertexData_);
  431. SetVertexData(debugVertexBuffer_, debugVertexData_);
  432. if (!renderUICommand)
  433. graphics_->ResetRenderTargets();
  434. // Render non-modal batches
  435. Render(vertexBuffer_, batches_, 0, nonModalBatchSize_);
  436. // Render debug draw
  437. Render(debugVertexBuffer_, debugDrawBatches_, 0, debugDrawBatches_.Size());
  438. // Render modal batches
  439. Render(vertexBuffer_, batches_, nonModalBatchSize_, batches_.Size());
  440. }
  441. // Render to UIComponent textures. This is skipped when called from the RENDERUI command
  442. if (!renderUICommand)
  443. {
  444. for (auto& item : renderToTexture_)
  445. {
  446. RenderToTextureData& data = item.second_;
  447. if (data.rootElement_->IsEnabled())
  448. {
  449. SetVertexData(data.vertexBuffer_, data.vertexData_);
  450. SetVertexData(data.debugVertexBuffer_, data.debugVertexData_);
  451. RenderSurface* surface = data.texture_->GetRenderSurface();
  452. graphics_->SetDepthStencil(surface->GetLinkedDepthStencil());
  453. graphics_->SetRenderTarget(0, surface);
  454. graphics_->SetViewport(IntRect(0, 0, surface->GetWidth(), surface->GetHeight()));
  455. graphics_->Clear(Urho3D::CLEAR_COLOR);
  456. Render(data.vertexBuffer_, data.batches_, 0, data.batches_.Size());
  457. Render(data.debugVertexBuffer_, data.debugDrawBatches_, 0, data.debugDrawBatches_.Size());
  458. data.debugDrawBatches_.Clear();
  459. data.debugVertexData_.Clear();
  460. }
  461. }
  462. if (renderToTexture_.Size())
  463. graphics_->ResetRenderTargets();
  464. }
  465. // Clear the debug draw batches and data
  466. debugDrawBatches_.Clear();
  467. debugVertexData_.Clear();
  468. uiRendered_ = true;
  469. }
  470. void UI::DebugDraw(UIElement* element)
  471. {
  472. if (element)
  473. {
  474. UIElement* root = element->GetRoot();
  475. if (!root)
  476. root = element;
  477. const IntVector2& rootSize = root->GetSize();
  478. const IntVector2& rootPos = root->GetPosition();
  479. IntRect scissor(rootPos.x_, rootPos.y_, rootPos.x_ + rootSize.x_, rootPos.y_ + rootSize.y_);
  480. if (root == rootElement_ || root == rootModalElement_)
  481. element->GetDebugDrawBatches(debugDrawBatches_, debugVertexData_, scissor);
  482. else
  483. {
  484. for (auto& item : renderToTexture_)
  485. {
  486. RenderToTextureData& data = item.second_;
  487. if (!data.rootElement_.Expired() && data.rootElement_ == root && data.rootElement_->IsEnabled())
  488. {
  489. element->GetDebugDrawBatches(data.debugDrawBatches_, data.debugVertexData_, scissor);
  490. break;
  491. }
  492. }
  493. }
  494. }
  495. }
  496. SharedPtr<UIElement> UI::LoadLayout(Deserializer& source, XMLFile* styleFile)
  497. {
  498. SharedPtr<XMLFile> xml(new XMLFile(context_));
  499. if (!xml->Load(source))
  500. return SharedPtr<UIElement>();
  501. else
  502. return LoadLayout(xml, styleFile);
  503. }
  504. SharedPtr<UIElement> UI::LoadLayout(XMLFile* file, XMLFile* styleFile)
  505. {
  506. URHO3D_PROFILE(LoadUILayout);
  507. SharedPtr<UIElement> root;
  508. if (!file)
  509. {
  510. URHO3D_LOGERROR("Null UI layout XML file");
  511. return root;
  512. }
  513. URHO3D_LOGDEBUG("Loading UI layout " + file->GetName());
  514. XMLElement rootElem = file->GetRoot("element");
  515. if (!rootElem)
  516. {
  517. URHO3D_LOGERROR("No root UI element in " + file->GetName());
  518. return root;
  519. }
  520. String typeName = rootElem.GetAttribute("type");
  521. if (typeName.Empty())
  522. typeName = "UIElement";
  523. root = DynamicCast<UIElement>(context_->CreateObject(typeName));
  524. if (!root)
  525. {
  526. URHO3D_LOGERROR("Could not create unknown UI element " + typeName);
  527. return root;
  528. }
  529. // Use default style file of the root element if it has one
  530. if (!styleFile)
  531. styleFile = rootElement_->GetDefaultStyle(false);
  532. // Set it as default for later use by children elements
  533. if (styleFile)
  534. root->SetDefaultStyle(styleFile);
  535. root->LoadXML(rootElem, styleFile);
  536. return root;
  537. }
  538. bool UI::SaveLayout(Serializer& dest, UIElement* element)
  539. {
  540. URHO3D_PROFILE(SaveUILayout);
  541. return element && element->SaveXML(dest);
  542. }
  543. void UI::SetClipboardText(const String& text)
  544. {
  545. clipBoard_ = text;
  546. if (useSystemClipboard_)
  547. SDL_SetClipboardText(text.CString());
  548. }
  549. void UI::SetDoubleClickInterval(float interval)
  550. {
  551. doubleClickInterval_ = Max(interval, 0.0f);
  552. }
  553. void UI::SetMaxDoubleClickDistance(float distPixels)
  554. {
  555. maxDoubleClickDist_ = distPixels;
  556. }
  557. void UI::SetDragBeginInterval(float interval)
  558. {
  559. dragBeginInterval_ = Max(interval, 0.0f);
  560. }
  561. void UI::SetDragBeginDistance(int pixels)
  562. {
  563. dragBeginDistance_ = Max(pixels, 0);
  564. }
  565. void UI::SetDefaultToolTipDelay(float delay)
  566. {
  567. defaultToolTipDelay_ = Max(delay, 0.0f);
  568. }
  569. void UI::SetMaxFontTextureSize(int size)
  570. {
  571. if (IsPowerOfTwo((unsigned)size) && size >= FONT_TEXTURE_MIN_SIZE)
  572. {
  573. if (size != maxFontTextureSize_)
  574. {
  575. maxFontTextureSize_ = size;
  576. ReleaseFontFaces();
  577. }
  578. }
  579. }
  580. void UI::SetNonFocusedMouseWheel(bool nonFocusedMouseWheel)
  581. {
  582. nonFocusedMouseWheel_ = nonFocusedMouseWheel;
  583. }
  584. void UI::SetUseSystemClipboard(bool enable)
  585. {
  586. useSystemClipboard_ = enable;
  587. }
  588. void UI::SetUseScreenKeyboard(bool enable)
  589. {
  590. useScreenKeyboard_ = enable;
  591. }
  592. void UI::SetUseMutableGlyphs(bool enable)
  593. {
  594. if (enable != useMutableGlyphs_)
  595. {
  596. useMutableGlyphs_ = enable;
  597. ReleaseFontFaces();
  598. }
  599. }
  600. void UI::SetForceAutoHint(bool enable)
  601. {
  602. if (enable != forceAutoHint_)
  603. {
  604. forceAutoHint_ = enable;
  605. ReleaseFontFaces();
  606. }
  607. }
  608. void UI::SetFontHintLevel(FontHintLevel level)
  609. {
  610. if (level != fontHintLevel_)
  611. {
  612. fontHintLevel_ = level;
  613. ReleaseFontFaces();
  614. }
  615. }
  616. void UI::SetFontSubpixelThreshold(float threshold)
  617. {
  618. assert(threshold >= 0);
  619. if (threshold != fontSubpixelThreshold_)
  620. {
  621. fontSubpixelThreshold_ = threshold;
  622. ReleaseFontFaces();
  623. }
  624. }
  625. void UI::SetFontOversampling(int oversampling)
  626. {
  627. assert(oversampling >= 1);
  628. oversampling = Clamp(oversampling, 1, 8);
  629. if (oversampling != fontOversampling_)
  630. {
  631. fontOversampling_ = oversampling;
  632. ReleaseFontFaces();
  633. }
  634. }
  635. void UI::SetScale(float scale)
  636. {
  637. uiScale_ = Max(scale, M_EPSILON);
  638. ResizeRootElement();
  639. }
  640. void UI::SetWidth(float width)
  641. {
  642. IntVector2 size = GetEffectiveRootElementSize(false);
  643. SetScale((float)size.x_ / width);
  644. }
  645. void UI::SetHeight(float height)
  646. {
  647. IntVector2 size = GetEffectiveRootElementSize(false);
  648. SetScale((float)size.y_ / height);
  649. }
  650. void UI::SetCustomSize(const IntVector2& size)
  651. {
  652. customSize_ = IntVector2(Max(0, size.x_), Max(0, size.y_));
  653. ResizeRootElement();
  654. }
  655. void UI::SetCustomSize(int width, int height)
  656. {
  657. customSize_ = IntVector2(Max(0, width), Max(0, height));
  658. ResizeRootElement();
  659. }
  660. IntVector2 UI::GetCursorPosition() const
  661. {
  662. if (cursor_)
  663. return cursor_->GetPosition();
  664. return ConvertSystemToUI(GetSubsystem<Input>()->GetMousePosition());
  665. }
  666. UIElement* UI::GetElementAt(const IntVector2& position, bool enabledOnly, IntVector2* elementScreenPosition)
  667. {
  668. UIElement* result = nullptr;
  669. if (HasModalElement())
  670. result = GetElementAt(rootModalElement_, position, enabledOnly);
  671. if (!result)
  672. result = GetElementAt(rootElement_, position, enabledOnly);
  673. // Mouse was not hovering UI element. Check elements rendered on 3D objects.
  674. if (!result && renderToTexture_.Size())
  675. {
  676. for (auto& item : renderToTexture_)
  677. {
  678. RenderToTextureData& data = item.second_;
  679. if (data.rootElement_.Expired() || !data.rootElement_->IsEnabled())
  680. continue;
  681. IntVector2 screenPosition = data.rootElement_->ScreenToElement(position);
  682. if (data.rootElement_->GetCombinedScreenRect().IsInside(screenPosition) == INSIDE)
  683. {
  684. result = GetElementAt(data.rootElement_, screenPosition, enabledOnly);
  685. if (result)
  686. {
  687. if (elementScreenPosition)
  688. *elementScreenPosition = screenPosition;
  689. break;
  690. }
  691. }
  692. }
  693. }
  694. else if (elementScreenPosition)
  695. *elementScreenPosition = position;
  696. return result;
  697. }
  698. UIElement* UI::GetElementAt(const IntVector2& position, bool enabledOnly)
  699. {
  700. return GetElementAt(position, enabledOnly, nullptr);
  701. }
  702. UIElement* UI::GetElementAt(UIElement* root, const IntVector2& position, bool enabledOnly)
  703. {
  704. IntVector2 positionCopy(position);
  705. const IntVector2& rootSize = root->GetSize();
  706. const IntVector2& rootPos = root->GetPosition();
  707. // If position is out of bounds of root element return null.
  708. if (position.x_ < rootPos.x_ || position.x_ > rootPos.x_ + rootSize.x_)
  709. return nullptr;
  710. if (position.y_ < rootPos.y_ || position.y_ > rootPos.y_ + rootSize.y_)
  711. return nullptr;
  712. // If UI is smaller than the screen, wrap if necessary
  713. if (rootSize.x_ > 0 && rootSize.y_ > 0)
  714. {
  715. if (positionCopy.x_ >= rootPos.x_ + rootSize.x_)
  716. positionCopy.x_ = rootPos.x_ + ((positionCopy.x_ - rootPos.x_) % rootSize.x_);
  717. if (positionCopy.y_ >= rootPos.y_ + rootSize.y_)
  718. positionCopy.y_ = rootPos.y_ + ((positionCopy.y_ - rootPos.y_) % rootSize.y_);
  719. }
  720. UIElement* result = nullptr;
  721. GetElementAt(result, root, positionCopy, enabledOnly);
  722. return result;
  723. }
  724. UIElement* UI::GetElementAt(int x, int y, bool enabledOnly)
  725. {
  726. return GetElementAt(IntVector2(x, y), enabledOnly);
  727. }
  728. IntVector2 UI::ConvertSystemToUI(const IntVector2& systemPos) const
  729. {
  730. return VectorFloorToInt(static_cast<Vector2>(systemPos) / GetScale());
  731. }
  732. IntVector2 UI::ConvertUIToSystem(const IntVector2& uiPos) const
  733. {
  734. return VectorFloorToInt(static_cast<Vector2>(uiPos) * GetScale());
  735. }
  736. UIElement* UI::GetFrontElement() const
  737. {
  738. const Vector<SharedPtr<UIElement> >& rootChildren = rootElement_->GetChildren();
  739. int maxPriority = M_MIN_INT;
  740. UIElement* front = nullptr;
  741. for (unsigned i = 0; i < rootChildren.Size(); ++i)
  742. {
  743. // Do not take into account input-disabled elements, hidden elements or those that are always in the front
  744. if (!rootChildren[i]->IsEnabled() || !rootChildren[i]->IsVisible() || !rootChildren[i]->GetBringToBack())
  745. continue;
  746. int priority = rootChildren[i]->GetPriority();
  747. if (priority > maxPriority)
  748. {
  749. maxPriority = priority;
  750. front = rootChildren[i];
  751. }
  752. }
  753. return front;
  754. }
  755. const Vector<UIElement*> UI::GetDragElements()
  756. {
  757. // Do not return the element until drag begin event has actually been posted
  758. if (!dragElementsConfirmed_.Empty())
  759. return dragElementsConfirmed_;
  760. for (HashMap<WeakPtr<UIElement>, UI::DragData*>::Iterator i = dragElements_.Begin(); i != dragElements_.End();)
  761. {
  762. WeakPtr<UIElement> dragElement = i->first_;
  763. UI::DragData* dragData = i->second_;
  764. if (!dragElement)
  765. {
  766. i = DragElementErase(i);
  767. continue;
  768. }
  769. if (!dragData->dragBeginPending)
  770. dragElementsConfirmed_.Push(dragElement);
  771. ++i;
  772. }
  773. return dragElementsConfirmed_;
  774. }
  775. UIElement* UI::GetDragElement(unsigned index)
  776. {
  777. GetDragElements();
  778. if (index >= dragElementsConfirmed_.Size())
  779. return nullptr;
  780. return dragElementsConfirmed_[index];
  781. }
  782. const String& UI::GetClipboardText() const
  783. {
  784. if (useSystemClipboard_)
  785. {
  786. char* text = SDL_GetClipboardText();
  787. clipBoard_ = String(text);
  788. if (text)
  789. SDL_free(text);
  790. }
  791. return clipBoard_;
  792. }
  793. bool UI::HasModalElement() const
  794. {
  795. return rootModalElement_->GetNumChildren() > 0;
  796. }
  797. void UI::Initialize()
  798. {
  799. auto* graphics = GetSubsystem<Graphics>();
  800. if (!graphics || !graphics->IsInitialized())
  801. return;
  802. URHO3D_PROFILE(InitUI);
  803. graphics_ = graphics;
  804. UIBatch::posAdjust = Vector3(Graphics::GetPixelUVOffset(), 0.0f);
  805. // Set initial root element size
  806. ResizeRootElement();
  807. vertexBuffer_ = new VertexBuffer(context_);
  808. debugVertexBuffer_ = new VertexBuffer(context_);
  809. initialized_ = true;
  810. SubscribeToEvent(E_BEGINFRAME, URHO3D_HANDLER(UI, HandleBeginFrame));
  811. SubscribeToEvent(E_POSTUPDATE, URHO3D_HANDLER(UI, HandlePostUpdate));
  812. SubscribeToEvent(E_RENDERUPDATE, URHO3D_HANDLER(UI, HandleRenderUpdate));
  813. URHO3D_LOGINFO("Initialized user interface");
  814. }
  815. void UI::Update(float timeStep, UIElement* element)
  816. {
  817. // Keep a weak pointer to the element in case it destroys itself on update
  818. WeakPtr<UIElement> elementWeak(element);
  819. element->Update(timeStep);
  820. if (elementWeak.Expired())
  821. return;
  822. const Vector<SharedPtr<UIElement> >& children = element->GetChildren();
  823. // Update of an element may modify its child vector. Use just index-based iteration to be safe
  824. for (unsigned i = 0; i < children.Size(); ++i)
  825. Update(timeStep, children[i]);
  826. }
  827. void UI::SetVertexData(VertexBuffer* dest, const PODVector<float>& vertexData)
  828. {
  829. if (vertexData.Empty())
  830. return;
  831. // Update quad geometry into the vertex buffer
  832. // Resize the vertex buffer first if too small or much too large
  833. unsigned numVertices = vertexData.Size() / UI_VERTEX_SIZE;
  834. if (dest->GetVertexCount() < numVertices || dest->GetVertexCount() > numVertices * 2)
  835. dest->SetSize(numVertices, MASK_POSITION | MASK_COLOR | MASK_TEXCOORD1, true);
  836. dest->SetData(&vertexData[0]);
  837. }
  838. void UI::Render(VertexBuffer* buffer, const PODVector<UIBatch>& batches, unsigned batchStart, unsigned batchEnd)
  839. {
  840. // Engine does not render when window is closed or device is lost
  841. assert(graphics_ && graphics_->IsInitialized() && !graphics_->IsDeviceLost());
  842. if (batches.Empty())
  843. return;
  844. unsigned alphaFormat = Graphics::GetAlphaFormat();
  845. RenderSurface* surface = graphics_->GetRenderTarget(0);
  846. IntVector2 viewSize = graphics_->GetViewport().Size();
  847. Vector2 invScreenSize(1.0f / (float)viewSize.x_, 1.0f / (float)viewSize.y_);
  848. Vector2 scale(2.0f * invScreenSize.x_, -2.0f * invScreenSize.y_);
  849. Vector2 offset(-1.0f, 1.0f);
  850. if (Graphics::GetGAPI() == GAPI_OPENGL && surface)
  851. {
  852. // On OpenGL, flip the projection if rendering to a texture so that the texture can be addressed in the
  853. // same way as a render texture produced on Direct3D.
  854. offset.y_ = -offset.y_;
  855. scale.y_ = -scale.y_;
  856. }
  857. Matrix4 projection(Matrix4::IDENTITY);
  858. projection.m00_ = scale.x_ * uiScale_;
  859. projection.m03_ = offset.x_;
  860. projection.m11_ = scale.y_ * uiScale_;
  861. projection.m13_ = offset.y_;
  862. projection.m22_ = 1.0f;
  863. projection.m23_ = 0.0f;
  864. projection.m33_ = 1.0f;
  865. graphics_->ClearParameterSources();
  866. graphics_->SetColorWrite(true);
  867. // Reverse winding if rendering to texture on OpenGL
  868. if (Graphics::GetGAPI() == GAPI_OPENGL && surface)
  869. graphics_->SetCullMode(CULL_CW);
  870. else
  871. graphics_->SetCullMode(CULL_CCW);
  872. graphics_->SetDepthTest(CMP_ALWAYS);
  873. graphics_->SetDepthWrite(false);
  874. graphics_->SetFillMode(FILL_SOLID);
  875. graphics_->SetStencilTest(false);
  876. graphics_->SetVertexBuffer(buffer);
  877. ShaderVariation* noTextureVS = graphics_->GetShader(VS, "Basic", "VERTEXCOLOR");
  878. ShaderVariation* diffTextureVS = graphics_->GetShader(VS, "Basic", "DIFFMAP VERTEXCOLOR");
  879. ShaderVariation* noTexturePS = graphics_->GetShader(PS, "Basic", "VERTEXCOLOR");
  880. ShaderVariation* diffTexturePS = graphics_->GetShader(PS, "Basic", "DIFFMAP VERTEXCOLOR");
  881. ShaderVariation* diffMaskTexturePS = graphics_->GetShader(PS, "Basic", "DIFFMAP ALPHAMASK VERTEXCOLOR");
  882. ShaderVariation* alphaTexturePS = graphics_->GetShader(PS, "Basic", "ALPHAMAP VERTEXCOLOR");
  883. for (unsigned i = batchStart; i < batchEnd; ++i)
  884. {
  885. const UIBatch& batch = batches[i];
  886. if (batch.vertexStart_ == batch.vertexEnd_)
  887. continue;
  888. ShaderVariation* ps;
  889. ShaderVariation* vs;
  890. if (!batch.customMaterial_)
  891. {
  892. if (!batch.texture_)
  893. {
  894. ps = noTexturePS;
  895. vs = noTextureVS;
  896. } else
  897. {
  898. // If texture contains only an alpha channel, use alpha shader (for fonts)
  899. vs = diffTextureVS;
  900. if (batch.texture_->GetFormat() == alphaFormat)
  901. ps = alphaTexturePS;
  902. else if (batch.blendMode_ != BLEND_ALPHA && batch.blendMode_ != BLEND_ADDALPHA && batch.blendMode_ != BLEND_PREMULALPHA)
  903. ps = diffMaskTexturePS;
  904. else
  905. ps = diffTexturePS;
  906. }
  907. } else
  908. {
  909. vs = diffTextureVS;
  910. ps = diffTexturePS;
  911. Technique* technique = batch.customMaterial_->GetTechnique(0);
  912. if (technique)
  913. {
  914. Pass* pass = nullptr;
  915. for (int i = 0; i < technique->GetNumPasses(); ++i)
  916. {
  917. pass = technique->GetPass(i);
  918. if (pass)
  919. {
  920. vs = graphics_->GetShader(VS, pass->GetVertexShader(), batch.customMaterial_->GetVertexShaderDefines());
  921. ps = graphics_->GetShader(PS, pass->GetPixelShader(), batch.customMaterial_->GetPixelShaderDefines());
  922. break;
  923. }
  924. }
  925. }
  926. }
  927. graphics_->SetShaders(vs, ps);
  928. if (graphics_->NeedParameterUpdate(SP_OBJECT, this))
  929. graphics_->SetShaderParameter(VSP_MODEL, Matrix3x4::IDENTITY);
  930. if (graphics_->NeedParameterUpdate(SP_CAMERA, this))
  931. graphics_->SetShaderParameter(VSP_VIEWPROJ, projection);
  932. if (graphics_->NeedParameterUpdate(SP_MATERIAL, this))
  933. graphics_->SetShaderParameter(PSP_MATDIFFCOLOR, Color(1.0f, 1.0f, 1.0f, 1.0f));
  934. float elapsedTime = GetSubsystem<Time>()->GetElapsedTime();
  935. graphics_->SetShaderParameter(VSP_ELAPSEDTIME, elapsedTime);
  936. graphics_->SetShaderParameter(PSP_ELAPSEDTIME, elapsedTime);
  937. IntRect scissor = batch.scissor_;
  938. scissor.left_ = (int)(scissor.left_ * uiScale_);
  939. scissor.top_ = (int)(scissor.top_ * uiScale_);
  940. scissor.right_ = (int)(scissor.right_ * uiScale_);
  941. scissor.bottom_ = (int)(scissor.bottom_ * uiScale_);
  942. // Flip scissor vertically if using OpenGL texture rendering
  943. if (Graphics::GetGAPI() == GAPI_OPENGL && surface)
  944. {
  945. int top = scissor.top_;
  946. int bottom = scissor.bottom_;
  947. scissor.top_ = viewSize.y_ - bottom;
  948. scissor.bottom_ = viewSize.y_ - top;
  949. }
  950. graphics_->SetBlendMode(batch.blendMode_);
  951. graphics_->SetScissorTest(true, scissor);
  952. if (!batch.customMaterial_)
  953. {
  954. graphics_->SetTexture(0, batch.texture_);
  955. } else
  956. {
  957. // Update custom shader parameters if needed
  958. if (graphics_->NeedParameterUpdate(SP_MATERIAL, reinterpret_cast<const void*>(batch.customMaterial_->GetShaderParameterHash())))
  959. {
  960. auto shader_parameters = batch.customMaterial_->GetShaderParameters();
  961. for (auto it = shader_parameters.Begin(); it != shader_parameters.End(); ++it)
  962. {
  963. graphics_->SetShaderParameter(it->second_.name_, it->second_.value_);
  964. }
  965. }
  966. // Apply custom shader textures
  967. auto textures = batch.customMaterial_->GetTextures();
  968. for (auto it = textures.Begin(); it != textures.End(); ++it)
  969. {
  970. graphics_->SetTexture(it->first_, it->second_);
  971. }
  972. }
  973. graphics_->Draw(TRIANGLE_LIST, batch.vertexStart_ / UI_VERTEX_SIZE,
  974. (batch.vertexEnd_ - batch.vertexStart_) / UI_VERTEX_SIZE);
  975. if (batch.customMaterial_)
  976. {
  977. // Reset textures used by the batch custom material
  978. auto textures = batch.customMaterial_->GetTextures();
  979. for (auto it = textures.Begin(); it != textures.End(); ++it)
  980. {
  981. graphics_->SetTexture(it->first_, 0);
  982. }
  983. }
  984. }
  985. }
  986. void UI::GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexData, UIElement* element, IntRect currentScissor)
  987. {
  988. // Set clipping scissor for child elements. No need to draw if zero size
  989. element->AdjustScissor(currentScissor);
  990. if (currentScissor.left_ == currentScissor.right_ || currentScissor.top_ == currentScissor.bottom_)
  991. return;
  992. element->SortChildren();
  993. const Vector<SharedPtr<UIElement> >& children = element->GetChildren();
  994. if (children.Empty())
  995. return;
  996. // For non-root elements draw all children of same priority before recursing into their children: assumption is that they have
  997. // same renderstate
  998. Vector<SharedPtr<UIElement> >::ConstIterator i = children.Begin();
  999. if (element->GetTraversalMode() == TM_BREADTH_FIRST)
  1000. {
  1001. Vector<SharedPtr<UIElement> >::ConstIterator j = i;
  1002. while (i != children.End())
  1003. {
  1004. int currentPriority = (*i)->GetPriority();
  1005. while (j != children.End() && (*j)->GetPriority() == currentPriority)
  1006. {
  1007. if ((*j)->IsWithinScissor(currentScissor) && (*j) != cursor_)
  1008. (*j)->GetBatches(batches, vertexData, currentScissor);
  1009. ++j;
  1010. }
  1011. // Now recurse into the children
  1012. while (i != j)
  1013. {
  1014. if ((*i)->IsVisible() && (*i) != cursor_)
  1015. GetBatches(batches, vertexData, *i, currentScissor);
  1016. ++i;
  1017. }
  1018. }
  1019. }
  1020. // On the root level draw each element and its children immediately after to avoid artifacts
  1021. else
  1022. {
  1023. while (i != children.End())
  1024. {
  1025. if ((*i) != cursor_)
  1026. {
  1027. if ((*i)->IsWithinScissor(currentScissor))
  1028. (*i)->GetBatches(batches, vertexData, currentScissor);
  1029. if ((*i)->IsVisible())
  1030. GetBatches(batches, vertexData, *i, currentScissor);
  1031. }
  1032. ++i;
  1033. }
  1034. }
  1035. }
  1036. void UI::GetElementAt(UIElement*& result, UIElement* current, const IntVector2& position, bool enabledOnly)
  1037. {
  1038. if (!current)
  1039. return;
  1040. current->SortChildren();
  1041. const Vector<SharedPtr<UIElement> >& children = current->GetChildren();
  1042. LayoutMode parentLayoutMode = current->GetLayoutMode();
  1043. for (unsigned i = 0; i < children.Size(); ++i)
  1044. {
  1045. UIElement* element = children[i];
  1046. bool hasChildren = element->GetNumChildren() > 0;
  1047. if (element != cursor_.Get() && element->IsVisible())
  1048. {
  1049. if (element->IsInside(position, true))
  1050. {
  1051. // Store the current result, then recurse into its children. Because children
  1052. // are sorted from lowest to highest priority, the topmost match should remain
  1053. if (element->IsEnabled() || !enabledOnly)
  1054. result = element;
  1055. if (hasChildren)
  1056. GetElementAt(result, element, position, enabledOnly);
  1057. // Layout optimization: if the element has no children, can break out after the first match
  1058. else if (parentLayoutMode != LM_FREE)
  1059. break;
  1060. }
  1061. else
  1062. {
  1063. if (hasChildren)
  1064. {
  1065. if (element->IsInsideCombined(position, true))
  1066. GetElementAt(result, element, position, enabledOnly);
  1067. }
  1068. // Layout optimization: if position is much beyond the visible screen, check how many elements we can skip,
  1069. // or if we already passed all visible elements
  1070. else if (parentLayoutMode != LM_FREE)
  1071. {
  1072. if (!i)
  1073. {
  1074. int screenPos = (parentLayoutMode == LM_HORIZONTAL) ? element->GetScreenPosition().x_ :
  1075. element->GetScreenPosition().y_;
  1076. int layoutMaxSize = current->GetLayoutElementMaxSize();
  1077. int spacing = current->GetLayoutSpacing();
  1078. if (screenPos < 0 && layoutMaxSize > 0)
  1079. {
  1080. auto toSkip = (unsigned)(-screenPos / (layoutMaxSize + spacing));
  1081. if (toSkip > 0)
  1082. i += (toSkip - 1);
  1083. }
  1084. }
  1085. // Note: we cannot check for the up / left limits of positioning, since the element may be off the visible
  1086. // screen but some of its layouted children will yet be visible. In down & right directions we can terminate
  1087. // the loop, since all further children will be further down or right.
  1088. else if (parentLayoutMode == LM_HORIZONTAL)
  1089. {
  1090. if (element->GetScreenPosition().x_ >= rootElement_->GetPosition().x_ + rootElement_->GetSize().x_)
  1091. break;
  1092. }
  1093. else if (parentLayoutMode == LM_VERTICAL)
  1094. {
  1095. if (element->GetScreenPosition().y_ >= rootElement_->GetPosition().y_ + rootElement_->GetSize().y_)
  1096. break;
  1097. }
  1098. }
  1099. }
  1100. }
  1101. }
  1102. }
  1103. UIElement* UI::GetFocusableElement(UIElement* element)
  1104. {
  1105. while (element)
  1106. {
  1107. if (element->GetFocusMode() != FM_NOTFOCUSABLE)
  1108. break;
  1109. element = element->GetParent();
  1110. }
  1111. return element;
  1112. }
  1113. void UI::GetCursorPositionAndVisible(IntVector2& pos, bool& visible)
  1114. {
  1115. // Prefer software cursor then OS-specific cursor
  1116. if (cursor_ && cursor_->IsVisible())
  1117. {
  1118. pos = cursor_->GetPosition();
  1119. visible = true;
  1120. }
  1121. else if (GetSubsystem<Input>()->GetMouseMode() == MM_RELATIVE)
  1122. visible = true;
  1123. else
  1124. {
  1125. auto* input = GetSubsystem<Input>();
  1126. visible = input->IsMouseVisible();
  1127. if (!visible && cursor_)
  1128. {
  1129. pos = cursor_->GetPosition();
  1130. }
  1131. else
  1132. {
  1133. pos = input->GetMousePosition();
  1134. pos = ConvertSystemToUI(pos);
  1135. }
  1136. }
  1137. }
  1138. void UI::SetCursorShape(CursorShape shape)
  1139. {
  1140. if (cursor_)
  1141. cursor_->SetShape(shape);
  1142. }
  1143. void UI::ReleaseFontFaces()
  1144. {
  1145. URHO3D_LOGDEBUG("Reloading font faces");
  1146. PODVector<Font*> fonts;
  1147. GetSubsystem<ResourceCache>()->GetResources<Font>(fonts);
  1148. for (unsigned i = 0; i < fonts.Size(); ++i)
  1149. fonts[i]->ReleaseFaces();
  1150. }
  1151. void UI::ProcessHover(const IntVector2& windowCursorPos, MouseButtonFlags buttons, QualifierFlags qualifiers, Cursor* cursor)
  1152. {
  1153. IntVector2 cursorPos;
  1154. WeakPtr<UIElement> element(GetElementAt(windowCursorPos, true, &cursorPos));
  1155. for (HashMap<WeakPtr<UIElement>, UI::DragData*>::Iterator i = dragElements_.Begin(); i != dragElements_.End();)
  1156. {
  1157. WeakPtr<UIElement> dragElement = i->first_;
  1158. UI::DragData* dragData = i->second_;
  1159. if (!dragElement)
  1160. {
  1161. i = DragElementErase(i);
  1162. continue;
  1163. }
  1164. bool dragSource = dragElement && (dragElement->GetDragDropMode() & DD_SOURCE);
  1165. bool dragTarget = element && (element->GetDragDropMode() & DD_TARGET);
  1166. bool dragDropTest = dragSource && dragTarget && element != dragElement;
  1167. // If drag start event has not been posted yet, do not do drag handling here
  1168. if (dragData->dragBeginPending)
  1169. dragSource = dragTarget = dragDropTest = false;
  1170. // Hover effect
  1171. // If a drag is going on, transmit hover only to the element being dragged, unless it's a drop target
  1172. if (element && element->IsEnabled())
  1173. {
  1174. if (dragElement == element || dragDropTest)
  1175. {
  1176. element->OnHover(element->ScreenToElement(cursorPos), cursorPos, buttons, qualifiers, cursor);
  1177. // Begin hover event
  1178. if (!hoveredElements_.Contains(element))
  1179. {
  1180. SendDragOrHoverEvent(E_HOVERBEGIN, element, cursorPos, IntVector2::ZERO, nullptr);
  1181. // Exit if element is destroyed by the event handling
  1182. if (!element)
  1183. return;
  1184. }
  1185. hoveredElements_[element] = true;
  1186. }
  1187. }
  1188. // Drag and drop test
  1189. if (dragDropTest)
  1190. {
  1191. bool accept = element->OnDragDropTest(dragElement);
  1192. if (accept)
  1193. {
  1194. using namespace DragDropTest;
  1195. VariantMap& eventData = GetEventDataMap();
  1196. eventData[P_SOURCE] = dragElement.Get();
  1197. eventData[P_TARGET] = element.Get();
  1198. eventData[P_ACCEPT] = accept;
  1199. SendEvent(E_DRAGDROPTEST, eventData);
  1200. accept = eventData[P_ACCEPT].GetBool();
  1201. }
  1202. if (cursor)
  1203. cursor->SetShape(accept ? CS_ACCEPTDROP : CS_REJECTDROP);
  1204. }
  1205. else if (dragSource && cursor)
  1206. cursor->SetShape(dragElement == element ? CS_ACCEPTDROP : CS_REJECTDROP);
  1207. ++i;
  1208. }
  1209. // Hover effect
  1210. // If no drag is going on, transmit hover event.
  1211. if (element && element->IsEnabled())
  1212. {
  1213. if (dragElementsCount_ == 0)
  1214. {
  1215. element->OnHover(element->ScreenToElement(cursorPos), cursorPos, buttons, qualifiers, cursor);
  1216. // Begin hover event
  1217. if (!hoveredElements_.Contains(element))
  1218. {
  1219. SendDragOrHoverEvent(E_HOVERBEGIN, element, cursorPos, IntVector2::ZERO, nullptr);
  1220. // Exit if element is destroyed by the event handling
  1221. if (!element)
  1222. return;
  1223. }
  1224. hoveredElements_[element] = true;
  1225. }
  1226. }
  1227. }
  1228. void UI::ProcessClickBegin(const IntVector2& windowCursorPos, MouseButton button, MouseButtonFlags buttons, QualifierFlags qualifiers, Cursor* cursor, bool cursorVisible)
  1229. {
  1230. if (cursorVisible)
  1231. {
  1232. IntVector2 cursorPos;
  1233. WeakPtr<UIElement> element(GetElementAt(windowCursorPos, true, &cursorPos));
  1234. bool newButton;
  1235. if (usingTouchInput_)
  1236. newButton = (buttons & button) == MOUSEB_NONE;
  1237. else
  1238. newButton = true;
  1239. buttons |= button;
  1240. if (element)
  1241. SetFocusElement(element);
  1242. // Focus change events may destroy the element, check again.
  1243. if (element)
  1244. {
  1245. // Handle focusing & bringing to front
  1246. element->BringToFront();
  1247. // Handle click
  1248. element->OnClickBegin(element->ScreenToElement(cursorPos), cursorPos, button, buttons, qualifiers, cursor);
  1249. SendClickEvent(E_UIMOUSECLICK, nullptr, element, cursorPos, button, buttons, qualifiers);
  1250. // Fire double click event if element matches and is in time and is within max distance from the original click
  1251. if (doubleClickElement_ && element == doubleClickElement_ &&
  1252. (clickTimer_.GetMSec(true) < (unsigned)(doubleClickInterval_ * 1000)) && lastMouseButtons_ == buttons && (windowCursorPos - doubleClickFirstPos_).Length() < maxDoubleClickDist_)
  1253. {
  1254. element->OnDoubleClick(element->ScreenToElement(cursorPos), cursorPos, button, buttons, qualifiers, cursor);
  1255. doubleClickElement_.Reset();
  1256. SendDoubleClickEvent(nullptr, element, doubleClickFirstPos_, cursorPos, button, buttons, qualifiers);
  1257. }
  1258. else
  1259. {
  1260. doubleClickElement_ = element;
  1261. doubleClickFirstPos_ = windowCursorPos;
  1262. clickTimer_.Reset();
  1263. }
  1264. // Handle start of drag. Click handling may have caused destruction of the element, so check the pointer again
  1265. bool dragElementsContain = dragElements_.Contains(element);
  1266. if (element && !dragElementsContain)
  1267. {
  1268. auto* dragData = new DragData();
  1269. dragElements_[element] = dragData;
  1270. dragData->dragBeginPending = true;
  1271. dragData->sumPos = cursorPos;
  1272. dragData->dragBeginSumPos = cursorPos;
  1273. dragData->dragBeginTimer.Reset();
  1274. dragData->dragButtons = button;
  1275. dragData->numDragButtons = CountSetBits((unsigned)dragData->dragButtons);
  1276. dragElementsCount_++;
  1277. dragElementsContain = dragElements_.Contains(element);
  1278. }
  1279. else if (element && dragElementsContain && newButton)
  1280. {
  1281. DragData* dragData = dragElements_[element];
  1282. dragData->sumPos += cursorPos;
  1283. dragData->dragBeginSumPos += cursorPos;
  1284. dragData->dragButtons |= button;
  1285. dragData->numDragButtons = CountSetBits((unsigned)dragData->dragButtons);
  1286. }
  1287. }
  1288. else
  1289. {
  1290. // If clicked over no element, or a disabled element, lose focus (but not if there is a modal element)
  1291. if (!HasModalElement())
  1292. SetFocusElement(nullptr);
  1293. SendClickEvent(E_UIMOUSECLICK, nullptr, element, cursorPos, button, buttons, qualifiers);
  1294. if (clickTimer_.GetMSec(true) < (unsigned)(doubleClickInterval_ * 1000) && lastMouseButtons_ == buttons && (windowCursorPos - doubleClickFirstPos_).Length() < maxDoubleClickDist_)
  1295. SendDoubleClickEvent(nullptr, element, doubleClickFirstPos_, cursorPos, button, buttons, qualifiers);
  1296. }
  1297. lastMouseButtons_ = buttons;
  1298. }
  1299. }
  1300. void UI::ProcessClickEnd(const IntVector2& windowCursorPos, MouseButton button, MouseButtonFlags buttons, QualifierFlags qualifiers, Cursor* cursor, bool cursorVisible)
  1301. {
  1302. WeakPtr<UIElement> element;
  1303. IntVector2 cursorPos = windowCursorPos;
  1304. if (cursorVisible)
  1305. element = GetElementAt(cursorPos, true, &cursorPos);
  1306. // Handle end of drag
  1307. for (HashMap<WeakPtr<UIElement>, UI::DragData*>::Iterator i = dragElements_.Begin(); i != dragElements_.End();)
  1308. {
  1309. WeakPtr<UIElement> dragElement = i->first_;
  1310. UI::DragData* dragData = i->second_;
  1311. if (!dragElement || !cursorVisible)
  1312. {
  1313. i = DragElementErase(i);
  1314. continue;
  1315. }
  1316. if (dragData->dragButtons & button)
  1317. {
  1318. // Handle end of click
  1319. if (element)
  1320. element->OnClickEnd(element->ScreenToElement(cursorPos), cursorPos, button, buttons, qualifiers, cursor,
  1321. dragElement);
  1322. SendClickEvent(E_UIMOUSECLICKEND, dragElement, element, cursorPos, button, buttons, qualifiers);
  1323. if (dragElement && dragElement->IsEnabled() && dragElement->IsVisible() && !dragData->dragBeginPending)
  1324. {
  1325. dragElement->OnDragEnd(dragElement->ScreenToElement(cursorPos), cursorPos, dragData->dragButtons, buttons,
  1326. cursor);
  1327. SendDragOrHoverEvent(E_DRAGEND, dragElement, cursorPos, IntVector2::ZERO, dragData);
  1328. bool dragSource = dragElement && (dragElement->GetDragDropMode() & DD_SOURCE);
  1329. if (dragSource)
  1330. {
  1331. bool dragTarget = element && (element->GetDragDropMode() & DD_TARGET);
  1332. bool dragDropFinish = dragSource && dragTarget && element != dragElement;
  1333. if (dragDropFinish)
  1334. {
  1335. bool accept = element->OnDragDropFinish(dragElement);
  1336. // OnDragDropFinish() may have caused destruction of the elements, so check the pointers again
  1337. if (accept && dragElement && element)
  1338. {
  1339. using namespace DragDropFinish;
  1340. VariantMap& eventData = GetEventDataMap();
  1341. eventData[P_SOURCE] = dragElement.Get();
  1342. eventData[P_TARGET] = element.Get();
  1343. eventData[P_ACCEPT] = accept;
  1344. SendEvent(E_DRAGDROPFINISH, eventData);
  1345. }
  1346. }
  1347. }
  1348. }
  1349. i = DragElementErase(i);
  1350. }
  1351. else
  1352. ++i;
  1353. }
  1354. }
  1355. void UI::ProcessMove(const IntVector2& windowCursorPos, const IntVector2& cursorDeltaPos, MouseButtonFlags buttons, QualifierFlags qualifiers, Cursor* cursor,
  1356. bool cursorVisible)
  1357. {
  1358. if (cursorVisible && dragElementsCount_ > 0 && buttons)
  1359. {
  1360. IntVector2 cursorPos;
  1361. GetElementAt(windowCursorPos, true, &cursorPos);
  1362. auto* input = GetSubsystem<Input>();
  1363. bool mouseGrabbed = input->IsMouseGrabbed();
  1364. for (HashMap<WeakPtr<UIElement>, UI::DragData*>::Iterator i = dragElements_.Begin(); i != dragElements_.End();)
  1365. {
  1366. WeakPtr<UIElement> dragElement = i->first_;
  1367. UI::DragData* dragData = i->second_;
  1368. if (!dragElement)
  1369. {
  1370. i = DragElementErase(i);
  1371. continue;
  1372. }
  1373. if (!(dragData->dragButtons & buttons))
  1374. {
  1375. ++i;
  1376. continue;
  1377. }
  1378. // Calculate the position that we should send for this drag event.
  1379. IntVector2 sendPos;
  1380. if (usingTouchInput_)
  1381. {
  1382. dragData->sumPos += cursorDeltaPos;
  1383. sendPos.x_ = dragData->sumPos.x_ / dragData->numDragButtons;
  1384. sendPos.y_ = dragData->sumPos.y_ / dragData->numDragButtons;
  1385. }
  1386. else
  1387. {
  1388. dragData->sumPos = cursorPos;
  1389. sendPos = cursorPos;
  1390. }
  1391. if (dragElement->IsEnabled() && dragElement->IsVisible())
  1392. {
  1393. // Signal drag begin if distance threshold was exceeded
  1394. if (dragData->dragBeginPending && !mouseGrabbed)
  1395. {
  1396. IntVector2 beginSendPos;
  1397. beginSendPos.x_ = dragData->dragBeginSumPos.x_ / dragData->numDragButtons;
  1398. beginSendPos.y_ = dragData->dragBeginSumPos.y_ / dragData->numDragButtons;
  1399. IntVector2 offset = cursorPos - beginSendPos;
  1400. if (Abs(offset.x_) >= dragBeginDistance_ || Abs(offset.y_) >= dragBeginDistance_)
  1401. {
  1402. dragData->dragBeginPending = false;
  1403. dragConfirmedCount_++;
  1404. dragElement->OnDragBegin(dragElement->ScreenToElement(beginSendPos), beginSendPos, buttons, qualifiers,
  1405. cursor);
  1406. SendDragOrHoverEvent(E_DRAGBEGIN, dragElement, beginSendPos, IntVector2::ZERO, dragData);
  1407. }
  1408. }
  1409. if (!dragData->dragBeginPending)
  1410. {
  1411. dragElement->OnDragMove(dragElement->ScreenToElement(sendPos), sendPos, cursorDeltaPos, buttons, qualifiers,
  1412. cursor);
  1413. SendDragOrHoverEvent(E_DRAGMOVE, dragElement, sendPos, cursorDeltaPos, dragData);
  1414. }
  1415. }
  1416. else
  1417. {
  1418. dragElement->OnDragEnd(dragElement->ScreenToElement(sendPos), sendPos, dragData->dragButtons, buttons, cursor);
  1419. SendDragOrHoverEvent(E_DRAGEND, dragElement, sendPos, IntVector2::ZERO, dragData);
  1420. dragElement.Reset();
  1421. }
  1422. ++i;
  1423. }
  1424. }
  1425. }
  1426. void UI::SendDragOrHoverEvent(StringHash eventType, UIElement* element, const IntVector2& screenPos, const IntVector2& deltaPos,
  1427. UI::DragData* dragData)
  1428. {
  1429. if (!element)
  1430. return;
  1431. IntVector2 relativePos = element->ScreenToElement(screenPos);
  1432. using namespace DragMove;
  1433. VariantMap& eventData = GetEventDataMap();
  1434. eventData[P_ELEMENT] = element;
  1435. eventData[P_X] = screenPos.x_;
  1436. eventData[P_Y] = screenPos.y_;
  1437. eventData[P_ELEMENTX] = relativePos.x_;
  1438. eventData[P_ELEMENTY] = relativePos.y_;
  1439. if (eventType == E_DRAGMOVE)
  1440. {
  1441. eventData[P_DX] = deltaPos.x_;
  1442. eventData[P_DY] = deltaPos.y_;
  1443. }
  1444. if (dragData)
  1445. {
  1446. eventData[P_BUTTONS] = (unsigned)dragData->dragButtons;
  1447. eventData[P_NUMBUTTONS] = dragData->numDragButtons;
  1448. }
  1449. element->SendEvent(eventType, eventData);
  1450. }
  1451. void UI::SendClickEvent(StringHash eventType, UIElement* beginElement, UIElement* endElement, const IntVector2& pos, MouseButton button,
  1452. MouseButtonFlags buttons, QualifierFlags qualifiers)
  1453. {
  1454. VariantMap& eventData = GetEventDataMap();
  1455. eventData[UIMouseClick::P_ELEMENT] = endElement;
  1456. eventData[UIMouseClick::P_X] = pos.x_;
  1457. eventData[UIMouseClick::P_Y] = pos.y_;
  1458. eventData[UIMouseClick::P_BUTTON] = button;
  1459. eventData[UIMouseClick::P_BUTTONS] = (unsigned)buttons;
  1460. eventData[UIMouseClick::P_QUALIFIERS] = (unsigned)qualifiers;
  1461. // For click end events, send also the element the click began on
  1462. if (eventType == E_UIMOUSECLICKEND)
  1463. eventData[UIMouseClickEnd::P_BEGINELEMENT] = beginElement;
  1464. if (endElement)
  1465. {
  1466. // Send also element version of the event
  1467. if (eventType == E_UIMOUSECLICK)
  1468. endElement->SendEvent(E_CLICK, eventData);
  1469. else if (eventType == E_UIMOUSECLICKEND)
  1470. endElement->SendEvent(E_CLICKEND, eventData);
  1471. }
  1472. // Send the global event from the UI subsystem last
  1473. SendEvent(eventType, eventData);
  1474. }
  1475. void UI::SendDoubleClickEvent(UIElement* beginElement, UIElement* endElement, const IntVector2& firstPos, const IntVector2& secondPos, MouseButton button,
  1476. MouseButtonFlags buttons, QualifierFlags qualifiers)
  1477. {
  1478. VariantMap& eventData = GetEventDataMap();
  1479. eventData[UIMouseDoubleClick::P_ELEMENT] = endElement;
  1480. eventData[UIMouseDoubleClick::P_X] = secondPos.x_;
  1481. eventData[UIMouseDoubleClick::P_Y] = secondPos.y_;
  1482. eventData[UIMouseDoubleClick::P_XBEGIN] = firstPos.x_;
  1483. eventData[UIMouseDoubleClick::P_YBEGIN] = firstPos.y_;
  1484. eventData[UIMouseDoubleClick::P_BUTTON] = button;
  1485. eventData[UIMouseDoubleClick::P_BUTTONS] = (unsigned)buttons;
  1486. eventData[UIMouseDoubleClick::P_QUALIFIERS] = (unsigned)qualifiers;
  1487. if (endElement)
  1488. {
  1489. // Send also element version of the event
  1490. endElement->SendEvent(E_DOUBLECLICK, eventData);
  1491. }
  1492. // Send the global event from the UI subsystem last
  1493. SendEvent(E_UIMOUSEDOUBLECLICK, eventData);
  1494. }
  1495. void UI::HandleScreenMode(StringHash eventType, VariantMap& eventData)
  1496. {
  1497. using namespace ScreenMode;
  1498. if (!initialized_)
  1499. Initialize();
  1500. else
  1501. ResizeRootElement();
  1502. }
  1503. void UI::HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
  1504. {
  1505. using namespace MouseButtonDown;
  1506. mouseButtons_ = MouseButtonFlags(eventData[P_BUTTONS].GetUInt());
  1507. qualifiers_ = QualifierFlags(eventData[P_QUALIFIERS].GetUInt());
  1508. usingTouchInput_ = false;
  1509. IntVector2 cursorPos;
  1510. bool cursorVisible;
  1511. GetCursorPositionAndVisible(cursorPos, cursorVisible);
  1512. // Handle drag cancelling
  1513. ProcessDragCancel();
  1514. auto* input = GetSubsystem<Input>();
  1515. if (!input->IsMouseGrabbed())
  1516. ProcessClickBegin(cursorPos, MouseButton(eventData[P_BUTTON].GetUInt()), mouseButtons_, qualifiers_, cursor_, cursorVisible);
  1517. }
  1518. void UI::HandleMouseButtonUp(StringHash eventType, VariantMap& eventData)
  1519. {
  1520. using namespace MouseButtonUp;
  1521. mouseButtons_ = MouseButtonFlags(eventData[P_BUTTONS].GetUInt());
  1522. qualifiers_ = QualifierFlags(eventData[P_QUALIFIERS].GetUInt());
  1523. IntVector2 cursorPos;
  1524. bool cursorVisible;
  1525. GetCursorPositionAndVisible(cursorPos, cursorVisible);
  1526. ProcessClickEnd(cursorPos, (MouseButton)eventData[P_BUTTON].GetUInt(), mouseButtons_, qualifiers_, cursor_, cursorVisible);
  1527. }
  1528. void UI::HandleMouseMove(StringHash eventType, VariantMap& eventData)
  1529. {
  1530. using namespace MouseMove;
  1531. mouseButtons_ = MouseButtonFlags(eventData[P_BUTTONS].GetUInt());
  1532. qualifiers_ = QualifierFlags(eventData[P_QUALIFIERS].GetUInt());
  1533. usingTouchInput_ = false;
  1534. auto* input = GetSubsystem<Input>();
  1535. const IntVector2& rootSize = rootElement_->GetSize();
  1536. const IntVector2& rootPos = rootElement_->GetPosition();
  1537. const IntVector2 mouseDeltaPos{ eventData[P_DX].GetInt(), eventData[P_DY].GetInt() };
  1538. const IntVector2 mousePos{ eventData[P_X].GetInt(), eventData[P_Y].GetInt() };
  1539. if (cursor_)
  1540. {
  1541. if (!input->IsMouseVisible())
  1542. {
  1543. if (!input->IsMouseLocked())
  1544. cursor_->SetPosition(ConvertSystemToUI(mousePos));
  1545. else if (cursor_->IsVisible())
  1546. {
  1547. // Relative mouse motion: move cursor only when visible
  1548. IntVector2 pos = cursor_->GetPosition();
  1549. pos += ConvertSystemToUI(mouseDeltaPos);
  1550. pos.x_ = Clamp(pos.x_, rootPos.x_, rootPos.x_ + rootSize.x_ - 1);
  1551. pos.y_ = Clamp(pos.y_, rootPos.y_, rootPos.y_ + rootSize.y_ - 1);
  1552. cursor_->SetPosition(pos);
  1553. }
  1554. }
  1555. else
  1556. {
  1557. // Absolute mouse motion: move always
  1558. cursor_->SetPosition(ConvertSystemToUI(mousePos));
  1559. }
  1560. }
  1561. IntVector2 cursorPos;
  1562. bool cursorVisible;
  1563. GetCursorPositionAndVisible(cursorPos, cursorVisible);
  1564. ProcessMove(cursorPos, mouseDeltaPos, mouseButtons_, qualifiers_, cursor_, cursorVisible);
  1565. }
  1566. void UI::HandleMouseWheel(StringHash eventType, VariantMap& eventData)
  1567. {
  1568. auto* input = GetSubsystem<Input>();
  1569. if (input->IsMouseGrabbed())
  1570. return;
  1571. using namespace MouseWheel;
  1572. mouseButtons_ = MouseButtonFlags(eventData[P_BUTTONS].GetInt());
  1573. qualifiers_ = QualifierFlags(eventData[P_QUALIFIERS].GetInt());
  1574. int delta = eventData[P_WHEEL].GetInt();
  1575. usingTouchInput_ = false;
  1576. IntVector2 cursorPos;
  1577. bool cursorVisible;
  1578. GetCursorPositionAndVisible(cursorPos, cursorVisible);
  1579. if (!nonFocusedMouseWheel_ && focusElement_)
  1580. focusElement_->OnWheel(delta, mouseButtons_, qualifiers_);
  1581. else
  1582. {
  1583. // If no element has actual focus or in non-focused mode, get the element at cursor
  1584. if (cursorVisible)
  1585. {
  1586. UIElement* element = GetElementAt(cursorPos);
  1587. if (nonFocusedMouseWheel_)
  1588. {
  1589. // Going up the hierarchy chain to find element that could handle mouse wheel
  1590. while (element && !element->IsWheelHandler())
  1591. {
  1592. element = element->GetParent();
  1593. }
  1594. }
  1595. else
  1596. // If the element itself is not focusable, search for a focusable parent,
  1597. // although the focusable element may not actually handle mouse wheel
  1598. element = GetFocusableElement(element);
  1599. if (element && (nonFocusedMouseWheel_ || element->GetFocusMode() >= FM_FOCUSABLE))
  1600. element->OnWheel(delta, mouseButtons_, qualifiers_);
  1601. }
  1602. }
  1603. }
  1604. void UI::HandleTouchBegin(StringHash eventType, VariantMap& eventData)
  1605. {
  1606. auto* input = GetSubsystem<Input>();
  1607. if (input->IsMouseGrabbed())
  1608. return;
  1609. using namespace TouchBegin;
  1610. IntVector2 pos(eventData[P_X].GetInt(), eventData[P_Y].GetInt());
  1611. pos = ConvertSystemToUI(pos);
  1612. usingTouchInput_ = true;
  1613. const MouseButton touchId = MakeTouchIDMask(eventData[P_TOUCHID].GetInt());
  1614. WeakPtr<UIElement> element(GetElementAt(pos));
  1615. if (element)
  1616. {
  1617. ProcessClickBegin(pos, touchId, touchDragElements_[element], QUAL_NONE, nullptr, true);
  1618. touchDragElements_[element] |= touchId;
  1619. }
  1620. else
  1621. ProcessClickBegin(pos, touchId, touchId, QUAL_NONE, nullptr, true);
  1622. }
  1623. void UI::HandleTouchEnd(StringHash eventType, VariantMap& eventData)
  1624. {
  1625. using namespace TouchEnd;
  1626. IntVector2 pos(eventData[P_X].GetInt(), eventData[P_Y].GetInt());
  1627. pos = ConvertSystemToUI(pos);
  1628. // Get the touch index
  1629. const MouseButton touchId = MakeTouchIDMask(eventData[P_TOUCHID].GetInt());
  1630. // Transmit hover end to the position where the finger was lifted
  1631. WeakPtr<UIElement> element(GetElementAt(pos));
  1632. // Clear any drag events that were using the touch id
  1633. for (auto i = touchDragElements_.Begin(); i != touchDragElements_.End();)
  1634. {
  1635. const MouseButtonFlags touches = i->second_;
  1636. if (touches & touchId)
  1637. i = touchDragElements_.Erase(i);
  1638. else
  1639. ++i;
  1640. }
  1641. if (element && element->IsEnabled())
  1642. element->OnHover(element->ScreenToElement(pos), pos, MOUSEB_NONE, QUAL_NONE, nullptr);
  1643. ProcessClickEnd(pos, touchId, MOUSEB_NONE, QUAL_NONE, nullptr, true);
  1644. }
  1645. void UI::HandleTouchMove(StringHash eventType, VariantMap& eventData)
  1646. {
  1647. using namespace TouchMove;
  1648. IntVector2 pos(eventData[P_X].GetInt(), eventData[P_Y].GetInt());
  1649. IntVector2 deltaPos(eventData[P_DX].GetInt(), eventData[P_DY].GetInt());
  1650. pos = ConvertSystemToUI(pos);
  1651. deltaPos = ConvertSystemToUI(deltaPos);
  1652. usingTouchInput_ = true;
  1653. const MouseButton touchId = MakeTouchIDMask(eventData[P_TOUCHID].GetInt());
  1654. ProcessMove(pos, deltaPos, touchId, QUAL_NONE, nullptr, true);
  1655. }
  1656. void UI::HandleKeyDown(StringHash eventType, VariantMap& eventData)
  1657. {
  1658. using namespace KeyDown;
  1659. mouseButtons_ = MouseButtonFlags(eventData[P_BUTTONS].GetUInt());
  1660. qualifiers_ = QualifierFlags(eventData[P_QUALIFIERS].GetUInt());
  1661. auto key = (Key)eventData[P_KEY].GetUInt();
  1662. // Cancel UI dragging
  1663. if (key == KEY_ESCAPE && dragElementsCount_ > 0)
  1664. {
  1665. ProcessDragCancel();
  1666. return;
  1667. }
  1668. // Dismiss modal element if any when ESC key is pressed
  1669. if (key == KEY_ESCAPE && HasModalElement())
  1670. {
  1671. UIElement* element = rootModalElement_->GetChild(rootModalElement_->GetNumChildren() - 1);
  1672. if (element->GetVars().Contains(VAR_ORIGIN))
  1673. // If it is a popup, dismiss by defocusing it
  1674. SetFocusElement(nullptr);
  1675. else
  1676. {
  1677. // If it is a modal window, by resetting its modal flag
  1678. auto* window = dynamic_cast<Window*>(element);
  1679. if (window && window->GetModalAutoDismiss())
  1680. window->SetModal(false);
  1681. }
  1682. return;
  1683. }
  1684. UIElement* element = focusElement_;
  1685. if (element)
  1686. {
  1687. // Switch focus between focusable elements in the same top level window
  1688. if (key == KEY_TAB)
  1689. {
  1690. UIElement* topLevel = element->GetParent();
  1691. while (topLevel && topLevel->GetParent() != rootElement_ && topLevel->GetParent() != rootModalElement_)
  1692. topLevel = topLevel->GetParent();
  1693. if (topLevel)
  1694. {
  1695. topLevel->GetChildren(tempElements_, true);
  1696. for (PODVector<UIElement*>::Iterator i = tempElements_.Begin(); i != tempElements_.End();)
  1697. {
  1698. if ((*i)->GetFocusMode() < FM_FOCUSABLE)
  1699. i = tempElements_.Erase(i);
  1700. else
  1701. ++i;
  1702. }
  1703. for (unsigned i = 0; i < tempElements_.Size(); ++i)
  1704. {
  1705. if (tempElements_[i] == element)
  1706. {
  1707. int dir = (qualifiers_ & QUAL_SHIFT) ? -1 : 1;
  1708. unsigned nextIndex = (tempElements_.Size() + i + dir) % tempElements_.Size();
  1709. UIElement* next = tempElements_[nextIndex];
  1710. SetFocusElement(next, true);
  1711. return;
  1712. }
  1713. }
  1714. }
  1715. }
  1716. // Defocus the element
  1717. else if (key == KEY_ESCAPE && element->GetFocusMode() == FM_FOCUSABLE_DEFOCUSABLE)
  1718. element->SetFocus(false);
  1719. // If none of the special keys, pass the key to the focused element
  1720. else
  1721. element->OnKey(key, mouseButtons_, qualifiers_);
  1722. }
  1723. }
  1724. void UI::HandleTextInput(StringHash eventType, VariantMap& eventData)
  1725. {
  1726. using namespace TextInput;
  1727. UIElement* element = focusElement_;
  1728. if (element)
  1729. element->OnTextInput(eventData[P_TEXT].GetString());
  1730. }
  1731. void UI::HandleBeginFrame(StringHash eventType, VariantMap& eventData)
  1732. {
  1733. // If have a cursor, and a drag is not going on, reset the cursor shape. Application logic that wants to apply
  1734. // custom shapes can do it after this, but needs to do it each frame
  1735. if (cursor_ && dragElementsCount_ == 0)
  1736. cursor_->SetShape(CS_NORMAL);
  1737. }
  1738. void UI::HandlePostUpdate(StringHash eventType, VariantMap& eventData)
  1739. {
  1740. using namespace PostUpdate;
  1741. Update(eventData[P_TIMESTEP].GetFloat());
  1742. }
  1743. void UI::HandleRenderUpdate(StringHash eventType, VariantMap& eventData)
  1744. {
  1745. RenderUpdate();
  1746. }
  1747. void UI::HandleDropFile(StringHash eventType, VariantMap& eventData)
  1748. {
  1749. auto* input = GetSubsystem<Input>();
  1750. // Sending the UI variant of the event only makes sense if the OS cursor is visible (not locked to window center)
  1751. if (input->IsMouseVisible())
  1752. {
  1753. IntVector2 screenPos = input->GetMousePosition();
  1754. screenPos = ConvertSystemToUI(screenPos);
  1755. UIElement* element = GetElementAt(screenPos);
  1756. using namespace UIDropFile;
  1757. VariantMap uiEventData;
  1758. uiEventData[P_FILENAME] = eventData[P_FILENAME];
  1759. uiEventData[P_X] = screenPos.x_;
  1760. uiEventData[P_Y] = screenPos.y_;
  1761. uiEventData[P_ELEMENT] = element;
  1762. if (element)
  1763. {
  1764. IntVector2 relativePos = element->ScreenToElement(screenPos);
  1765. uiEventData[P_ELEMENTX] = relativePos.x_;
  1766. uiEventData[P_ELEMENTY] = relativePos.y_;
  1767. }
  1768. SendEvent(E_UIDROPFILE, uiEventData);
  1769. }
  1770. }
  1771. HashMap<WeakPtr<UIElement>, UI::DragData*>::Iterator UI::DragElementErase(HashMap<WeakPtr<UIElement>, UI::DragData*>::Iterator i)
  1772. {
  1773. // If running the engine frame in response to an event (re-entering UI frame logic) the dragElements_ may already be empty
  1774. if (dragElements_.Empty())
  1775. return dragElements_.End();
  1776. dragElementsConfirmed_.Clear();
  1777. DragData* dragData = i->second_;
  1778. if (!dragData->dragBeginPending)
  1779. --dragConfirmedCount_;
  1780. i = dragElements_.Erase(i);
  1781. --dragElementsCount_;
  1782. delete dragData;
  1783. return i;
  1784. }
  1785. void UI::ProcessDragCancel()
  1786. {
  1787. // How to tell difference between drag cancel and new selection on multi-touch?
  1788. if (usingTouchInput_)
  1789. return;
  1790. IntVector2 cursorPos;
  1791. bool cursorVisible;
  1792. GetCursorPositionAndVisible(cursorPos, cursorVisible);
  1793. for (HashMap<WeakPtr<UIElement>, UI::DragData*>::Iterator i = dragElements_.Begin(); i != dragElements_.End();)
  1794. {
  1795. WeakPtr<UIElement> dragElement = i->first_;
  1796. UI::DragData* dragData = i->second_;
  1797. if (dragElement && dragElement->IsEnabled() && dragElement->IsVisible() && !dragData->dragBeginPending)
  1798. {
  1799. dragElement->OnDragCancel(dragElement->ScreenToElement(cursorPos), cursorPos, dragData->dragButtons, mouseButtons_,
  1800. cursor_);
  1801. SendDragOrHoverEvent(E_DRAGCANCEL, dragElement, cursorPos, IntVector2::ZERO, dragData);
  1802. i = DragElementErase(i);
  1803. }
  1804. else
  1805. ++i;
  1806. }
  1807. }
  1808. IntVector2 UI::SumTouchPositions(UI::DragData* dragData, const IntVector2& oldSendPos)
  1809. {
  1810. IntVector2 sendPos = oldSendPos;
  1811. if (usingTouchInput_)
  1812. {
  1813. MouseButtonFlags buttons = dragData->dragButtons;
  1814. dragData->sumPos = IntVector2::ZERO;
  1815. auto* input = GetSubsystem<Input>();
  1816. for (unsigned i = 0; (1u << i) <= (unsigned)buttons; i++)
  1817. {
  1818. auto mouseButton = static_cast<MouseButton>(1u << i); // NOLINT(misc-misplaced-widening-cast)
  1819. if (buttons & mouseButton)
  1820. {
  1821. TouchState* ts = input->GetTouch((unsigned)i);
  1822. if (!ts)
  1823. break;
  1824. IntVector2 pos = ts->position_;
  1825. pos = ConvertSystemToUI(pos);
  1826. dragData->sumPos += pos;
  1827. }
  1828. }
  1829. sendPos.x_ = dragData->sumPos.x_ / dragData->numDragButtons;
  1830. sendPos.y_ = dragData->sumPos.y_ / dragData->numDragButtons;
  1831. }
  1832. return sendPos;
  1833. }
  1834. void UI::ResizeRootElement()
  1835. {
  1836. IntVector2 effectiveSize = GetEffectiveRootElementSize();
  1837. rootElement_->SetSize(effectiveSize);
  1838. rootModalElement_->SetSize(effectiveSize);
  1839. }
  1840. IntVector2 UI::GetEffectiveRootElementSize(bool applyScale) const
  1841. {
  1842. // Use a fake size in headless mode
  1843. IntVector2 size = graphics_ ? IntVector2(graphics_->GetWidth(), graphics_->GetHeight()) : IntVector2(1024, 768);
  1844. if (customSize_.x_ > 0 && customSize_.y_ > 0)
  1845. size = customSize_;
  1846. if (applyScale)
  1847. {
  1848. size.x_ = RoundToInt((float)size.x_ / uiScale_);
  1849. size.y_ = RoundToInt((float)size.y_ / uiScale_);
  1850. }
  1851. return size;
  1852. }
  1853. void UI::SetElementRenderTexture(UIElement* element, Texture2D* texture)
  1854. {
  1855. if (element == nullptr)
  1856. {
  1857. URHO3D_LOGERROR("UI::SetElementRenderTexture called with null element.");
  1858. return;
  1859. }
  1860. auto it = renderToTexture_.Find(element);
  1861. if (texture && it == renderToTexture_.End())
  1862. {
  1863. RenderToTextureData data;
  1864. data.texture_ = texture;
  1865. data.rootElement_ = element;
  1866. data.vertexBuffer_ = new VertexBuffer(context_);
  1867. data.debugVertexBuffer_ = new VertexBuffer(context_);
  1868. renderToTexture_[element] = data;
  1869. }
  1870. else if (it != renderToTexture_.End())
  1871. {
  1872. if (texture == nullptr)
  1873. renderToTexture_.Erase(it);
  1874. else
  1875. it->second_.texture_ = texture;
  1876. }
  1877. }
  1878. void RegisterUILibrary(Context* context)
  1879. {
  1880. Font::RegisterObject(context);
  1881. UIElement::RegisterObject(context);
  1882. UISelectable::RegisterObject(context);
  1883. BorderImage::RegisterObject(context);
  1884. Sprite::RegisterObject(context);
  1885. Button::RegisterObject(context);
  1886. CheckBox::RegisterObject(context);
  1887. Cursor::RegisterObject(context);
  1888. Text::RegisterObject(context);
  1889. Text3D::RegisterObject(context);
  1890. Window::RegisterObject(context);
  1891. View3D::RegisterObject(context);
  1892. LineEdit::RegisterObject(context);
  1893. Slider::RegisterObject(context);
  1894. ScrollBar::RegisterObject(context);
  1895. ScrollView::RegisterObject(context);
  1896. ListView::RegisterObject(context);
  1897. Menu::RegisterObject(context);
  1898. DropDownList::RegisterObject(context);
  1899. FileSelector::RegisterObject(context);
  1900. MessageBox::RegisterObject(context);
  1901. ProgressBar::RegisterObject(context);
  1902. ToolTip::RegisterObject(context);
  1903. UIComponent::RegisterObject(context);
  1904. }
  1905. }