Window.cpp 12 KB

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