Window.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. //
  2. // Copyright (c) 2008-2013 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 "Context.h"
  24. #include "Cursor.h"
  25. #include "InputEvents.h"
  26. #include "UI.h"
  27. #include "UIEvents.h"
  28. #include "Window.h"
  29. #include "DebugNew.h"
  30. namespace Urho3D
  31. {
  32. static const int DEFAULT_RESIZE_BORDER = 4;
  33. extern const char* UI_CATEGORY;
  34. OBJECTTYPESTATIC(Window);
  35. Window::Window(Context* context) :
  36. BorderImage(context),
  37. movable_(false),
  38. resizable_(false),
  39. resizeBorder_(DEFAULT_RESIZE_BORDER, DEFAULT_RESIZE_BORDER, DEFAULT_RESIZE_BORDER, DEFAULT_RESIZE_BORDER),
  40. dragMode_(DRAG_NONE),
  41. modal_(false),
  42. modalShadeColor_(Color::TRANSPARENT),
  43. modalFrameColor_(Color::TRANSPARENT),
  44. modalFrameSize_(IntVector2::ZERO)
  45. {
  46. bringToFront_ = true;
  47. clipChildren_ = true;
  48. enabled_ = true;
  49. }
  50. Window::~Window()
  51. {
  52. }
  53. void Window::RegisterObject(Context* context)
  54. {
  55. context->RegisterFactory<Window>(UI_CATEGORY);
  56. COPY_BASE_ATTRIBUTES(Window, BorderImage);
  57. UPDATE_ATTRIBUTE_DEFAULT_VALUE(Window, "Bring To Front", true);
  58. UPDATE_ATTRIBUTE_DEFAULT_VALUE(Window, "Clip Children", true);
  59. UPDATE_ATTRIBUTE_DEFAULT_VALUE(Window, "Is Enabled", true);
  60. REF_ACCESSOR_ATTRIBUTE(Window, VAR_INTRECT, "Resize Border", GetResizeBorder, SetResizeBorder, IntRect, IntRect(DEFAULT_RESIZE_BORDER, \
  61. DEFAULT_RESIZE_BORDER, DEFAULT_RESIZE_BORDER, DEFAULT_RESIZE_BORDER), AM_FILE);
  62. ACCESSOR_ATTRIBUTE(Window, VAR_BOOL, "Is Movable", IsMovable, SetMovable, bool, false, AM_FILE);
  63. ACCESSOR_ATTRIBUTE(Window, VAR_BOOL, "Is Resizable", IsResizable, SetResizable, bool, false, AM_FILE);
  64. ACCESSOR_ATTRIBUTE(Window, VAR_BOOL, "Is Modal", IsModal, SetModal, bool, false, AM_FILE);
  65. REF_ACCESSOR_ATTRIBUTE(Window, VAR_COLOR, "Modal Shade Color", GetModalShadeColor, SetModalShadeColor, Color, Color::TRANSPARENT, AM_FILE);
  66. REF_ACCESSOR_ATTRIBUTE(Window, VAR_COLOR, "Modal Frame Color", GetModalFrameColor, SetModalFrameColor, Color, Color::TRANSPARENT, AM_FILE);
  67. REF_ACCESSOR_ATTRIBUTE(Window, VAR_INTVECTOR2, "Modal Frame Size", GetModalFrameSize, SetModalFrameSize, IntVector2, IntVector2::ZERO, AM_FILE);
  68. }
  69. void Window::GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexData, const IntRect& currentScissor)
  70. {
  71. if (modal_)
  72. {
  73. // Modal shade
  74. if (modalShadeColor_ != Color::TRANSPARENT)
  75. {
  76. UIElement* rootElement = GetRoot();
  77. const IntVector2& rootSize = rootElement->GetSize();
  78. UIBatch batch(rootElement, BLEND_ALPHA, IntRect(0, 0, rootSize.x_, rootSize.y_), 0, &vertexData);
  79. batch.AddQuad(0, 0, rootSize.x_, rootSize.y_, 0, 0, 0, 0, modalShadeColor_);
  80. UIBatch::AddOrMerge(batch, batches);
  81. }
  82. // Modal frame
  83. if (modalFrameColor_ != Color::TRANSPARENT && modalFrameSize_ != IntVector2::ZERO)
  84. {
  85. UIBatch batch(this, BLEND_ALPHA, currentScissor, 0, &vertexData);
  86. int x = GetIndentWidth();
  87. IntVector2 size = GetSize();
  88. size.x_ -= x;
  89. batch.AddQuad(x - modalFrameSize_.x_, -modalFrameSize_.y_, size.x_ + 2 * modalFrameSize_.x_, size.y_ + 2 * modalFrameSize_.y_, 0, 0, 0, 0, modalFrameColor_);
  90. UIBatch::AddOrMerge(batch, batches);
  91. }
  92. }
  93. BorderImage::GetBatches(batches, vertexData, currentScissor);
  94. }
  95. void Window::OnHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
  96. {
  97. if (dragMode_ == DRAG_NONE)
  98. {
  99. WindowDragMode mode = GetDragMode(position);
  100. SetCursorShape(mode, cursor);
  101. }
  102. else
  103. SetCursorShape(dragMode_, cursor);
  104. }
  105. void Window::OnDragBegin(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
  106. {
  107. if (buttons != MOUSEB_LEFT || !CheckAlignment())
  108. {
  109. dragMode_ = DRAG_NONE;
  110. return;
  111. }
  112. dragBeginCursor_ = screenPosition;
  113. dragBeginPosition_ = GetPosition();
  114. dragBeginSize_ = GetSize();
  115. dragMode_ = GetDragMode(position);
  116. SetCursorShape(dragMode_, cursor);
  117. }
  118. void Window::OnDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
  119. {
  120. if (dragMode_ == DRAG_NONE)
  121. return;
  122. IntVector2 delta = screenPosition - dragBeginCursor_;
  123. const IntVector2& position_ = GetPosition();
  124. const IntVector2& size_ = GetSize();
  125. const IntVector2& minSize_ = GetMinSize();
  126. const IntVector2& maxSize_ = GetMaxSize();
  127. switch (dragMode_)
  128. {
  129. case DRAG_MOVE:
  130. SetPosition(dragBeginPosition_ + delta);
  131. break;
  132. case DRAG_RESIZE_TOPLEFT:
  133. SetPosition(Clamp(dragBeginPosition_.x_ + delta.x_, position_.x_ - (maxSize_.x_ - size_.x_), position_.x_ + (size_.x_ - minSize_.x_)),
  134. Clamp(dragBeginPosition_.y_ + delta.y_, position_.y_ - (maxSize_.y_ - size_.y_), position_.y_ + (size_.y_ - minSize_.y_)));
  135. SetSize(dragBeginSize_ - delta);
  136. break;
  137. case DRAG_RESIZE_TOP:
  138. SetPosition(dragBeginPosition_.x_, Clamp(dragBeginPosition_.y_ + delta.y_, position_.y_ - (maxSize_.y_ - size_.y_), position_.y_ + (size_.y_ - minSize_.y_)));
  139. SetSize(dragBeginSize_.x_, dragBeginSize_.y_ - delta.y_);
  140. break;
  141. case DRAG_RESIZE_TOPRIGHT:
  142. SetPosition(dragBeginPosition_.x_, dragBeginPosition_.y_ + delta.y_);
  143. SetSize(dragBeginSize_.x_ + delta.x_, dragBeginSize_.y_ - delta.y_);
  144. break;
  145. case DRAG_RESIZE_RIGHT:
  146. SetSize(dragBeginSize_.x_ + delta.x_, dragBeginSize_.y_);
  147. break;
  148. case DRAG_RESIZE_BOTTOMRIGHT:
  149. SetSize(dragBeginSize_ + delta);
  150. break;
  151. case DRAG_RESIZE_BOTTOM:
  152. SetSize(dragBeginSize_.x_, dragBeginSize_.y_ + delta.y_);
  153. break;
  154. case DRAG_RESIZE_BOTTOMLEFT:
  155. SetPosition(Clamp(dragBeginPosition_.x_ + delta.x_, position_.x_ - (maxSize_.x_ - size_.x_), position_.x_ + (size_.x_ - minSize_.x_)), dragBeginPosition_.y_);
  156. SetSize(dragBeginSize_.x_ - delta.x_, dragBeginSize_.y_ + delta.y_);
  157. break;
  158. case DRAG_RESIZE_LEFT:
  159. SetPosition(Clamp(dragBeginPosition_.x_ + delta.x_, position_.x_ - (maxSize_.x_ - size_.x_), position_.x_ + (size_.x_ - minSize_.x_)), dragBeginPosition_.y_);
  160. SetSize(dragBeginSize_.x_ - delta.x_, dragBeginSize_.y_);
  161. break;
  162. default:
  163. break;
  164. }
  165. ValidatePosition();
  166. SetCursorShape(dragMode_, cursor);
  167. }
  168. void Window::OnDragEnd(const IntVector2& position, const IntVector2& screenPosition, Cursor* cursor)
  169. {
  170. dragMode_ = DRAG_NONE;
  171. }
  172. void Window::SetMovable(bool enable)
  173. {
  174. movable_ = enable;
  175. }
  176. void Window::SetResizable(bool enable)
  177. {
  178. resizable_ = enable;
  179. }
  180. void Window::SetResizeBorder(const IntRect& rect)
  181. {
  182. resizeBorder_.left_ = Max(rect.left_, 0);
  183. resizeBorder_.top_ = Max(rect.top_, 0);
  184. resizeBorder_.right_ = Max(rect.right_, 0);
  185. resizeBorder_.bottom_ = Max(rect.bottom_, 0);
  186. }
  187. void Window::SetModal(bool modal)
  188. {
  189. // UI may be null at shutdown if for example a script was holding a reference to this window
  190. UI* ui = GetSubsystem<UI>();
  191. if (!ui)
  192. return;
  193. if (ui->SetModalElement(this, modal))
  194. {
  195. modal_ = modal;
  196. using namespace ModalChanged;
  197. VariantMap eventData;
  198. eventData[P_ELEMENT] = (void*)this;
  199. eventData[P_MODAL] = modal;
  200. SendEvent(E_MODALCHANGED, eventData);
  201. }
  202. }
  203. void Window::SetModalShadeColor(const Color& color)
  204. {
  205. modalShadeColor_ = color;
  206. }
  207. void Window::SetModalFrameColor(const Color& color)
  208. {
  209. modalFrameColor_ = color;
  210. }
  211. void Window::SetModalFrameSize(const IntVector2& size)
  212. {
  213. modalFrameSize_ = size;
  214. }
  215. WindowDragMode Window::GetDragMode(const IntVector2& position) const
  216. {
  217. WindowDragMode mode = DRAG_NONE;
  218. // Top row
  219. if (position.y_ < resizeBorder_.top_)
  220. {
  221. if (movable_)
  222. mode = DRAG_MOVE;
  223. if (resizable_)
  224. {
  225. mode = DRAG_RESIZE_TOP;
  226. if (position.x_ < resizeBorder_.left_)
  227. mode = DRAG_RESIZE_TOPLEFT;
  228. if (position.x_ >= GetWidth() - resizeBorder_.right_)
  229. mode = DRAG_RESIZE_TOPRIGHT;
  230. }
  231. }
  232. // Bottom row
  233. else if (position.y_ >= GetHeight() - resizeBorder_.bottom_)
  234. {
  235. if (movable_)
  236. mode = DRAG_MOVE;
  237. if (resizable_)
  238. {
  239. mode = DRAG_RESIZE_BOTTOM;
  240. if (position.x_ < resizeBorder_.left_)
  241. mode = DRAG_RESIZE_BOTTOMLEFT;
  242. if (position.x_ >= GetWidth() - resizeBorder_.right_)
  243. mode = DRAG_RESIZE_BOTTOMRIGHT;
  244. }
  245. }
  246. // Middle
  247. else
  248. {
  249. if (movable_)
  250. mode = DRAG_MOVE;
  251. if (resizable_)
  252. {
  253. if (position.x_ < resizeBorder_.left_)
  254. mode = DRAG_RESIZE_LEFT;
  255. if (position.x_ >= GetWidth() - resizeBorder_.right_)
  256. mode = DRAG_RESIZE_RIGHT;
  257. }
  258. }
  259. return mode;
  260. }
  261. void Window::SetCursorShape(WindowDragMode mode, Cursor* cursor) const
  262. {
  263. if (!cursor)
  264. return;
  265. switch (mode)
  266. {
  267. case DRAG_NONE:
  268. case DRAG_MOVE:
  269. cursor->SetShape(CS_NORMAL);
  270. break;
  271. case DRAG_RESIZE_TOP:
  272. case DRAG_RESIZE_BOTTOM:
  273. cursor->SetShape(CS_RESIZEVERTICAL);
  274. break;
  275. case DRAG_RESIZE_LEFT:
  276. case DRAG_RESIZE_RIGHT:
  277. cursor->SetShape(CS_RESIZEHORIZONTAL);
  278. break;
  279. case DRAG_RESIZE_TOPRIGHT:
  280. case DRAG_RESIZE_BOTTOMLEFT:
  281. cursor->SetShape(CS_RESIZEDIAGONAL_TOPRIGHT);
  282. break;
  283. case DRAG_RESIZE_TOPLEFT:
  284. case DRAG_RESIZE_BOTTOMRIGHT:
  285. cursor->SetShape(CS_RESIZEDIAGONAL_TOPLEFT);
  286. break;
  287. default:
  288. break;
  289. }
  290. }
  291. void Window::ValidatePosition()
  292. {
  293. // Check that window does not go more than halfway outside its parent in either dimension
  294. if (!parent_)
  295. return;
  296. const IntVector2& parentSize = parent_->GetSize();
  297. IntVector2 position = GetPosition();
  298. IntVector2 halfSize = GetSize() / 2;
  299. position.x_ = Clamp(position.x_, -halfSize.x_, parentSize.x_ - halfSize.x_);
  300. position.y_ = Clamp(position.y_, -halfSize.y_, parentSize.y_ - halfSize.y_);
  301. SetPosition(position);
  302. }
  303. bool Window::CheckAlignment() const
  304. {
  305. // Only top left-alignment is supported for move and resize
  306. if (GetHorizontalAlignment() == HA_LEFT && GetVerticalAlignment() == VA_TOP)
  307. return true;
  308. else
  309. return false;
  310. }
  311. }