Window.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  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 "Window.h"
  28. #include "DebugNew.h"
  29. namespace Urho3D
  30. {
  31. static const int DEFAULT_RESIZE_BORDER = 4;
  32. OBJECTTYPESTATIC(Window);
  33. Window::Window(Context* context) :
  34. BorderImage(context),
  35. movable_(false),
  36. resizable_(false),
  37. resizeBorder_(DEFAULT_RESIZE_BORDER, DEFAULT_RESIZE_BORDER, DEFAULT_RESIZE_BORDER, DEFAULT_RESIZE_BORDER),
  38. dragMode_(DRAG_NONE),
  39. modal_(false),
  40. modalShadeColor_(Color::TRANSPARENT),
  41. modalFrameColor_(Color::TRANSPARENT),
  42. modalFrameSize_(IntVector2::ZERO)
  43. {
  44. bringToFront_ = true;
  45. clipChildren_ = true;
  46. enabled_ = true;
  47. }
  48. Window::~Window()
  49. {
  50. }
  51. void Window::RegisterObject(Context* context)
  52. {
  53. context->RegisterFactory<Window>();
  54. REF_ACCESSOR_ATTRIBUTE(Window, VAR_INTRECT, "Resize Border", GetResizeBorder, SetResizeBorder, IntRect, IntRect(DEFAULT_RESIZE_BORDER, \
  55. DEFAULT_RESIZE_BORDER, DEFAULT_RESIZE_BORDER, DEFAULT_RESIZE_BORDER), AM_FILE);
  56. ACCESSOR_ATTRIBUTE(Window, VAR_BOOL, "Is Movable", IsMovable, SetMovable, bool, false, AM_FILE);
  57. ACCESSOR_ATTRIBUTE(Window, VAR_BOOL, "Is Resizable", IsResizable, SetResizable, bool, false, AM_FILE);
  58. ACCESSOR_ATTRIBUTE(Window, VAR_BOOL, "Is Modal", IsModal, SetModal, bool, false, AM_FILE);
  59. REF_ACCESSOR_ATTRIBUTE(Window, VAR_COLOR, "Modal Shade Color", GetModalShadeColor, SetModalShadeColor, Color, Color::TRANSPARENT, AM_FILE);
  60. REF_ACCESSOR_ATTRIBUTE(Window, VAR_COLOR, "Modal Frame Color", GetModalFrameColor, SetModalFrameColor, Color, Color::TRANSPARENT, AM_FILE);
  61. REF_ACCESSOR_ATTRIBUTE(Window, VAR_INTVECTOR2, "Modal Frame Size", GetModalFrameSize, SetModalFrameSize, IntVector2, IntVector2::ZERO, AM_FILE);
  62. COPY_BASE_ATTRIBUTES(Window, BorderImage);
  63. }
  64. void Window::GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexData, const IntRect& currentScissor)
  65. {
  66. if (modal_ && modalShadeColor_ != Color::TRANSPARENT)
  67. {
  68. // Modal shade
  69. UIElement* rootElement = GetRoot();
  70. const IntVector2& rootSize = rootElement->GetSize();
  71. IntRect currentScissor(0, 0, rootSize.x_, rootSize.y_);
  72. UIBatch batch(rootElement, BLEND_ALPHA, IntRect(0, 0, rootSize.x_, rootSize.y_), 0, &vertexData);
  73. batch.AddQuad(0, 0, rootSize.x_, rootSize.y_, 0, 0, 0, 0, modalShadeColor_);
  74. UIBatch::AddOrMerge(batch, batches);
  75. }
  76. BorderImage::GetBatches(batches, vertexData, currentScissor);
  77. if (modal_ && modalFrameColor_ != Color::TRANSPARENT && modalFrameSize_ != IntVector2::ZERO)
  78. {
  79. // Modal frame
  80. UIBatch batch(this, BLEND_ALPHA, currentScissor, 0, &vertexData);
  81. int x = GetIndentWidth();
  82. IntVector2 size = GetSize();
  83. size.x_ -= x;
  84. // Left
  85. batch.AddQuad(x - modalFrameSize_.x_, 0, modalFrameSize_.x_, size.y_, 0, 0, 0, 0, modalFrameColor_);
  86. // Top
  87. batch.AddQuad(x - modalFrameSize_.x_, -modalFrameSize_.y_, size.x_ + 2 * modalFrameSize_.x_, modalFrameSize_.y_, 0, 0, 0, 0, modalFrameColor_);
  88. // Right
  89. batch.AddQuad(size.x_, 0, modalFrameSize_.x_, size.y_, 0, 0, 0, 0, modalFrameColor_);
  90. // Bottom
  91. batch.AddQuad(x - modalFrameSize_.x_, size.y_, size.x_ + 2 * modalFrameSize_.x_, modalFrameSize_.y_, 0, 0, 0, 0, modalFrameColor_);
  92. UIBatch::AddOrMerge(batch, batches);
  93. }
  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* ui = GetSubsystem<UI>();
  190. if (ui->SetModalElement(modal ? this : 0))
  191. modal_ = modal;
  192. }
  193. void Window::SetModalShadeColor(const Color& color)
  194. {
  195. modalShadeColor_ = color;
  196. }
  197. void Window::SetModalFrameColor(const Color& color)
  198. {
  199. modalFrameColor_ = color;
  200. }
  201. void Window::SetModalFrameSize(const IntVector2& size)
  202. {
  203. modalFrameSize_ = size;
  204. }
  205. WindowDragMode Window::GetDragMode(const IntVector2& position) const
  206. {
  207. WindowDragMode mode = DRAG_NONE;
  208. // Top row
  209. if (position.y_ < resizeBorder_.top_)
  210. {
  211. if (movable_)
  212. mode = DRAG_MOVE;
  213. if (resizable_)
  214. {
  215. mode = DRAG_RESIZE_TOP;
  216. if (position.x_ < resizeBorder_.left_)
  217. mode = DRAG_RESIZE_TOPLEFT;
  218. if (position.x_ >= GetWidth() - resizeBorder_.right_)
  219. mode = DRAG_RESIZE_TOPRIGHT;
  220. }
  221. }
  222. // Bottom row
  223. else if (position.y_ >= GetHeight() - resizeBorder_.bottom_)
  224. {
  225. if (movable_)
  226. mode = DRAG_MOVE;
  227. if (resizable_)
  228. {
  229. mode = DRAG_RESIZE_BOTTOM;
  230. if (position.x_ < resizeBorder_.left_)
  231. mode = DRAG_RESIZE_BOTTOMLEFT;
  232. if (position.x_ >= GetWidth() - resizeBorder_.right_)
  233. mode = DRAG_RESIZE_BOTTOMRIGHT;
  234. }
  235. }
  236. // Middle
  237. else
  238. {
  239. if (movable_)
  240. mode = DRAG_MOVE;
  241. if (resizable_)
  242. {
  243. if (position.x_ < resizeBorder_.left_)
  244. mode = DRAG_RESIZE_LEFT;
  245. if (position.x_ >= GetWidth() - resizeBorder_.right_)
  246. mode = DRAG_RESIZE_RIGHT;
  247. }
  248. }
  249. return mode;
  250. }
  251. void Window::SetCursorShape(WindowDragMode mode, Cursor* cursor) const
  252. {
  253. if (!cursor)
  254. return;
  255. switch (mode)
  256. {
  257. case DRAG_NONE:
  258. case DRAG_MOVE:
  259. cursor->SetShape(CS_NORMAL);
  260. break;
  261. case DRAG_RESIZE_TOP:
  262. case DRAG_RESIZE_BOTTOM:
  263. cursor->SetShape(CS_RESIZEVERTICAL);
  264. break;
  265. case DRAG_RESIZE_LEFT:
  266. case DRAG_RESIZE_RIGHT:
  267. cursor->SetShape(CS_RESIZEHORIZONTAL);
  268. break;
  269. case DRAG_RESIZE_TOPRIGHT:
  270. case DRAG_RESIZE_BOTTOMLEFT:
  271. cursor->SetShape(CS_RESIZEDIAGONAL_TOPRIGHT);
  272. break;
  273. case DRAG_RESIZE_TOPLEFT:
  274. case DRAG_RESIZE_BOTTOMRIGHT:
  275. cursor->SetShape(CS_RESIZEDIAGONAL_TOPLEFT);
  276. break;
  277. default:
  278. break;
  279. }
  280. }
  281. void Window::ValidatePosition()
  282. {
  283. // Check that window does not go more than halfway outside its parent in either dimension
  284. if (!parent_)
  285. return;
  286. const IntVector2& parentSize = parent_->GetSize();
  287. IntVector2 position = GetPosition();
  288. IntVector2 halfSize = GetSize() / 2;
  289. position.x_ = Clamp(position.x_, -halfSize.x_, parentSize.x_ - halfSize.x_);
  290. position.y_ = Clamp(position.y_, -halfSize.y_, parentSize.y_ - halfSize.y_);
  291. SetPosition(position);
  292. }
  293. bool Window::CheckAlignment() const
  294. {
  295. // Only top left-alignment is supported for move and resize
  296. if (GetHorizontalAlignment() == HA_LEFT && GetVerticalAlignment() == VA_TOP)
  297. return true;
  298. else
  299. return false;
  300. }
  301. }