Form.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  1. #include "Base.h"
  2. #include "Form.h"
  3. #include "AbsoluteLayout.h"
  4. #include "FlowLayout.h"
  5. #include "VerticalLayout.h"
  6. #include "Game.h"
  7. #include "Theme.h"
  8. #include "Label.h"
  9. #include "Button.h"
  10. #include "CheckBox.h"
  11. #include "Scene.h"
  12. namespace gameplay
  13. {
  14. static std::vector<Form*> __forms;
  15. Form::Form() : _theme(NULL), _quad(NULL), _node(NULL), _frameBuffer(NULL), _spriteBatch(NULL)
  16. {
  17. }
  18. Form::Form(const Form& copy)
  19. {
  20. }
  21. Form::~Form()
  22. {
  23. SAFE_RELEASE(_quad);
  24. SAFE_RELEASE(_node);
  25. SAFE_RELEASE(_frameBuffer);
  26. SAFE_RELEASE(_theme);
  27. SAFE_DELETE(_spriteBatch);
  28. // Remove this Form from the global list.
  29. std::vector<Form*>::iterator it = std::find(__forms.begin(), __forms.end(), this);
  30. if (it != __forms.end())
  31. {
  32. __forms.erase(it);
  33. }
  34. }
  35. Form* Form::create(const char* url)
  36. {
  37. // Load Form from .form file.
  38. Properties* properties = Properties::create(url);
  39. if (properties == NULL)
  40. {
  41. GP_ASSERT(properties);
  42. return NULL;
  43. }
  44. // Check if the Properties is valid and has a valid namespace.
  45. Properties* formProperties = (strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace();
  46. assert(formProperties);
  47. if (!formProperties || !(strcmp(formProperties->getNamespace(), "form") == 0))
  48. {
  49. GP_ASSERT(formProperties);
  50. SAFE_DELETE(properties);
  51. return NULL;
  52. }
  53. // Create new form with given ID, theme and layout.
  54. const char* themeFile = formProperties->getString("theme");
  55. const char* layoutString = formProperties->getString("layout");
  56. Layout* layout;
  57. switch (getLayoutType(layoutString))
  58. {
  59. case Layout::LAYOUT_ABSOLUTE:
  60. layout = AbsoluteLayout::create();
  61. break;
  62. case Layout::LAYOUT_FLOW:
  63. layout = FlowLayout::create();
  64. break;
  65. case Layout::LAYOUT_VERTICAL:
  66. layout = VerticalLayout::create();
  67. break;
  68. default:
  69. GP_ERROR("Unsupported layout type '%d'.", getLayoutType(layoutString));
  70. }
  71. Theme* theme = Theme::create(themeFile);
  72. GP_ASSERT(theme);
  73. Form* form = new Form();
  74. form->_layout = layout;
  75. form->_theme = theme;
  76. // Get default projection matrix.
  77. Game* game = Game::getInstance();
  78. Matrix::createOrthographicOffCenter(0, game->getWidth(), game->getHeight(), 0, 0, 1, &form->_defaultProjectionMatrix);
  79. const char* styleName = formProperties->getString("style");
  80. form->initialize(theme->getStyle(styleName), formProperties);
  81. // Alignment
  82. if ((form->_alignment & Control::ALIGN_BOTTOM) == Control::ALIGN_BOTTOM)
  83. {
  84. form->_bounds.y = Game::getInstance()->getHeight() - form->_bounds.height;
  85. }
  86. else if ((form->_alignment & Control::ALIGN_VCENTER) == Control::ALIGN_VCENTER)
  87. {
  88. form->_bounds.y = Game::getInstance()->getHeight() * 0.5f - form->_bounds.height * 0.5f;
  89. }
  90. if ((form->_alignment & Control::ALIGN_RIGHT) == Control::ALIGN_RIGHT)
  91. {
  92. form->_bounds.x = Game::getInstance()->getWidth() - form->_bounds.width;
  93. }
  94. else if ((form->_alignment & Control::ALIGN_HCENTER) == Control::ALIGN_HCENTER)
  95. {
  96. form->_bounds.x = Game::getInstance()->getWidth() * 0.5f - form->_bounds.width * 0.5f;
  97. }
  98. form->_scroll = getScroll(formProperties->getString("scroll"));
  99. // Add all the controls to the form.
  100. form->addControls(theme, formProperties);
  101. SAFE_DELETE(properties);
  102. __forms.push_back(form);
  103. return form;
  104. }
  105. Form* Form::getForm(const char* id)
  106. {
  107. std::vector<Form*>::const_iterator it;
  108. for (it = __forms.begin(); it < __forms.end(); it++)
  109. {
  110. Form* f = *it;
  111. GP_ASSERT(f);
  112. if (strcmp(id, f->getID()) == 0)
  113. {
  114. return f;
  115. }
  116. }
  117. return NULL;
  118. }
  119. void Form::setSize(float width, float height)
  120. {
  121. if (_autoWidth)
  122. {
  123. width = Game::getInstance()->getWidth();
  124. }
  125. if (_autoHeight)
  126. {
  127. height = Game::getInstance()->getHeight();
  128. }
  129. if (width != _bounds.width || height != _bounds.height)
  130. {
  131. // Width and height must be powers of two to create a texture.
  132. int w = width;
  133. int h = height;
  134. if (!((w & (w - 1)) == 0))
  135. {
  136. w = nextHighestPowerOfTwo(w);
  137. }
  138. if (!((h & (h - 1)) == 0))
  139. {
  140. h = nextHighestPowerOfTwo(h);
  141. }
  142. _u2 = width / (float)w;
  143. _v1 = height / (float)h;
  144. // Create framebuffer if necessary.
  145. if (!_frameBuffer)
  146. {
  147. _frameBuffer = FrameBuffer::create(_id.c_str());
  148. GP_ASSERT(_frameBuffer);
  149. }
  150. // Re-create render target.
  151. RenderTarget* rt = RenderTarget::create(_id.c_str(), w, h);
  152. GP_ASSERT(rt);
  153. _frameBuffer->setRenderTarget(rt);
  154. SAFE_RELEASE(rt);
  155. // Re-create projection matrix.
  156. Matrix::createOrthographicOffCenter(0, width, height, 0, 0, 1, &_projectionMatrix);
  157. // Re-create sprite batch.
  158. SAFE_DELETE(_spriteBatch);
  159. _spriteBatch = SpriteBatch::create(_frameBuffer->getRenderTarget()->getTexture());
  160. GP_ASSERT(_spriteBatch);
  161. // Clear FBO.
  162. _frameBuffer->bind();
  163. Game* game = Game::getInstance();
  164. Rectangle prevViewport = game->getViewport();
  165. game->setViewport(Rectangle(0, 0, width, height));
  166. _theme->setProjectionMatrix(_projectionMatrix);
  167. GL_ASSERT( glClearColor(0, 0, 0, 0) );
  168. GL_ASSERT( glClear(GL_COLOR_BUFFER_BIT) );
  169. GL_ASSERT( glClearColor(0, 0, 0, 1) );
  170. _theme->setProjectionMatrix(_defaultProjectionMatrix);
  171. FrameBuffer::bindDefault();
  172. game->setViewport(prevViewport);
  173. _bounds.width = width;
  174. _bounds.height = height;
  175. _dirty = true;
  176. }
  177. }
  178. void Form::setBounds(const Rectangle& bounds)
  179. {
  180. setPosition(bounds.x, bounds.y);
  181. setSize(bounds.width, bounds.height);
  182. }
  183. void Form::setAutoWidth(bool autoWidth)
  184. {
  185. if (_autoWidth != autoWidth)
  186. {
  187. _autoWidth = autoWidth;
  188. _dirty = true;
  189. if (_autoWidth)
  190. {
  191. setSize(_bounds.width, Game::getInstance()->getWidth());
  192. }
  193. }
  194. }
  195. void Form::setAutoHeight(bool autoHeight)
  196. {
  197. if (_autoHeight != autoHeight)
  198. {
  199. _autoHeight = autoHeight;
  200. _dirty = true;
  201. if (_autoHeight)
  202. {
  203. setSize(_bounds.width, Game::getInstance()->getHeight());
  204. }
  205. }
  206. }
  207. void Form::setQuad(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p4)
  208. {
  209. Mesh* mesh = Mesh::createQuad(p1, p2, p3, p4);
  210. initializeQuad(mesh);
  211. SAFE_RELEASE(mesh);
  212. }
  213. void Form::setQuad(float x, float y, float width, float height)
  214. {
  215. float x2 = x + width;
  216. float y2 = y + height;
  217. float vertices[] =
  218. {
  219. x, y2, 0, 0, _v1,
  220. x, y, 0, 0, 0,
  221. x2, y2, 0, _u2, _v1,
  222. x2, y, 0, _u2, 0
  223. };
  224. VertexFormat::Element elements[] =
  225. {
  226. VertexFormat::Element(VertexFormat::POSITION, 3),
  227. VertexFormat::Element(VertexFormat::TEXCOORD0, 2)
  228. };
  229. Mesh* mesh = Mesh::createMesh(VertexFormat(elements, 2), 4, false);
  230. assert(mesh);
  231. mesh->setPrimitiveType(Mesh::TRIANGLE_STRIP);
  232. mesh->setVertexData(vertices, 0, 4);
  233. initializeQuad(mesh);
  234. SAFE_RELEASE(mesh);
  235. }
  236. void Form::setNode(Node* node)
  237. {
  238. _node = node;
  239. if (_node)
  240. {
  241. // Set this Form up to be 3D by initializing a quad.
  242. setQuad(0.0f, 0.0f, _bounds.width, _bounds.height);
  243. _node->setModel(_quad);
  244. }
  245. }
  246. void Form::update()
  247. {
  248. if (isDirty())
  249. {
  250. _clearBounds.set(_absoluteClipBounds);
  251. // Calculate the clipped bounds.
  252. float x = 0;
  253. float y = 0;
  254. float width = _bounds.width;
  255. float height = _bounds.height;
  256. Rectangle clip(0, 0, _bounds.width, _bounds.height);
  257. float clipX2 = clip.x + clip.width;
  258. float x2 = clip.x + x + width;
  259. if (x2 > clipX2)
  260. width -= x2 - clipX2;
  261. float clipY2 = clip.y + clip.height;
  262. float y2 = clip.y + y + height;
  263. if (y2 > clipY2)
  264. height -= y2 - clipY2;
  265. if (x < 0)
  266. {
  267. width += x;
  268. x = -x;
  269. }
  270. else
  271. {
  272. x = 0;
  273. }
  274. if (y < 0)
  275. {
  276. height += y;
  277. y = -y;
  278. }
  279. else
  280. {
  281. y = 0;
  282. }
  283. _clipBounds.set(x, y, width, height);
  284. // Calculate the absolute bounds.
  285. x = 0;
  286. y = 0;
  287. _absoluteBounds.set(x, y, _bounds.width, _bounds.height);
  288. // Calculate the absolute viewport bounds.
  289. // Absolute bounds minus border and padding.
  290. const Theme::Border& border = getBorder(_state);
  291. const Theme::Padding& padding = getPadding();
  292. x += border.left + padding.left;
  293. y += border.top + padding.top;
  294. width = _bounds.width - border.left - padding.left - border.right - padding.right;
  295. height = _bounds.height - border.top - padding.top - border.bottom - padding.bottom;
  296. _viewportBounds.set(x, y, width, height);
  297. // Calculate the clip area.
  298. // Absolute bounds, minus border and padding,
  299. // clipped to the parent container's clip area.
  300. clipX2 = clip.x + clip.width;
  301. x2 = x + width;
  302. if (x2 > clipX2)
  303. width = clipX2 - x;
  304. clipY2 = clip.y + clip.height;
  305. y2 = y + height;
  306. if (y2 > clipY2)
  307. height = clipY2 - y;
  308. if (x < clip.x)
  309. {
  310. float dx = clip.x - x;
  311. width -= dx;
  312. x = clip.x;
  313. }
  314. if (y < clip.y)
  315. {
  316. float dy = clip.y - y;
  317. height -= dy;
  318. y = clip.y;
  319. }
  320. _viewportClipBounds.set(x, y, width, height);
  321. _absoluteClipBounds.set(x - border.left - padding.left, y - border.top - padding.top,
  322. width + border.left + padding.left + border.right + padding.right,
  323. height + border.top + padding.top + border.bottom + padding.bottom);
  324. if (_clearBounds.isEmpty())
  325. {
  326. _clearBounds.set(_absoluteClipBounds);
  327. }
  328. // Cache themed attributes for performance.
  329. _skin = getSkin(_state);
  330. _opacity = getOpacity(_state);
  331. // Get scrollbar images and diminish clipping bounds to make room for scrollbars.
  332. if ((_scroll & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL)
  333. {
  334. _scrollBarLeftCap = getImage("scrollBarLeftCap", _state);
  335. _scrollBarHorizontal = getImage("horizontalScrollBar", _state);
  336. _scrollBarRightCap = getImage("scrollBarRightCap", _state);
  337. _viewportClipBounds.height -= _scrollBarHorizontal->getRegion().height;
  338. }
  339. if ((_scroll & SCROLL_VERTICAL) == SCROLL_VERTICAL)
  340. {
  341. _scrollBarTopCap = getImage("scrollBarTopCap", _state);
  342. _scrollBarVertical = getImage("verticalScrollBar", _state);
  343. _scrollBarBottomCap = getImage("scrollBarBottomCap", _state);
  344. _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
  345. }
  346. GP_ASSERT(_layout);
  347. _layout->update(this);
  348. if (_scroll != SCROLL_NONE)
  349. this->updateScroll(this);
  350. }
  351. }
  352. void Form::draw()
  353. {
  354. /*
  355. The first time a form is drawn, its contents are rendered into a framebuffer.
  356. The framebuffer will only be drawn into again when the contents of the form change.
  357. If this form has a node then it's a 3D form and the framebuffer will be used
  358. to texture a quad. The quad will be given the same dimensions as the form and
  359. must be transformed appropriately by the user, unless they call setQuad() themselves.
  360. On the other hand, if this form has not been set on a node, SpriteBatch will be used
  361. to render the contents of the frambuffer directly to the display.
  362. */
  363. // Check whether this form has changed since the last call to draw()
  364. // and if so, render into the framebuffer.
  365. if (isDirty())
  366. {
  367. GP_ASSERT(_frameBuffer);
  368. _frameBuffer->bind();
  369. Game* game = Game::getInstance();
  370. Rectangle prevViewport = game->getViewport();
  371. game->setViewport(Rectangle(0, 0, _bounds.width, _bounds.height));
  372. GP_ASSERT(_theme);
  373. _theme->setProjectionMatrix(_projectionMatrix);
  374. Container::draw(_theme->getSpriteBatch(), Rectangle(0, 0, _bounds.width, _bounds.height), _skin == NULL, _bounds.height);
  375. _theme->setProjectionMatrix(_defaultProjectionMatrix);
  376. // Rebind the default framebuffer and game viewport.
  377. FrameBuffer::bindDefault();
  378. // restore the previous game viewport
  379. game->setViewport(prevViewport);
  380. }
  381. if (_node)
  382. {
  383. GP_ASSERT(_quad);
  384. _quad->draw();
  385. }
  386. else
  387. {
  388. if (!_spriteBatch)
  389. {
  390. _spriteBatch = SpriteBatch::create(_frameBuffer->getRenderTarget()->getTexture());
  391. GP_ASSERT(_spriteBatch);
  392. }
  393. _spriteBatch->begin();
  394. _spriteBatch->draw(_bounds.x, _bounds.y, 0, _bounds.width, _bounds.height, 0, _v1, _u2, 0, Vector4::one());
  395. _spriteBatch->end();
  396. }
  397. }
  398. void Form::initializeQuad(Mesh* mesh)
  399. {
  400. // Release current model.
  401. SAFE_RELEASE(_quad);
  402. // Create the model.
  403. _quad = Model::create(mesh);
  404. // Create the material.
  405. Material* material = _quad->setMaterial("res/shaders/textured.vsh", "res/shaders/textured.fsh");
  406. GP_ASSERT(material);
  407. // Set the common render state block for the material.
  408. GP_ASSERT(_theme);
  409. GP_ASSERT(_theme->getSpriteBatch());
  410. RenderState::StateBlock* stateBlock = _theme->getSpriteBatch()->getStateBlock();
  411. GP_ASSERT(stateBlock);
  412. stateBlock->setDepthWrite(true);
  413. material->setStateBlock(stateBlock);
  414. // Bind the WorldViewProjection matrix.
  415. material->setParameterAutoBinding("u_worldViewProjectionMatrix", RenderState::WORLD_VIEW_PROJECTION_MATRIX);
  416. // Bind the texture.
  417. Texture::Sampler* sampler = Texture::Sampler::create(_frameBuffer->getRenderTarget()->getTexture());
  418. GP_ASSERT(sampler);
  419. sampler->setWrapMode(Texture::CLAMP, Texture::CLAMP);
  420. material->getParameter("u_diffuseTexture")->setValue(sampler);
  421. material->getParameter("u_diffuseColor")->setValue(Vector4::one());
  422. SAFE_RELEASE(sampler);
  423. }
  424. bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
  425. {
  426. // Check for a collision with each Form in __forms.
  427. // Pass the event on.
  428. std::vector<Form*>::const_iterator it;
  429. for (it = __forms.begin(); it < __forms.end(); it++)
  430. {
  431. Form* form = *it;
  432. GP_ASSERT(form);
  433. if (form->isEnabled())
  434. {
  435. Node* node = form->_node;
  436. if (node)
  437. {
  438. Scene* scene = node->getScene();
  439. GP_ASSERT(scene);
  440. Camera* camera = scene->getActiveCamera();
  441. if (camera)
  442. {
  443. // Get info about the form's position.
  444. Matrix m = node->getMatrix();
  445. Vector3 min(0, 0, 0);
  446. m.transformPoint(&min);
  447. // Unproject point into world space.
  448. Ray ray;
  449. camera->pickRay(Game::getInstance()->getViewport(), x, y, &ray);
  450. // Find the quad's plane.
  451. // We know its normal is the quad's forward vector.
  452. Vector3 normal = node->getForwardVectorWorld();
  453. // To get the plane's distance from the origin,
  454. // we'll find the distance from the plane defined
  455. // by the quad's forward vector and one of its points
  456. // to the plane defined by the same vector and the origin.
  457. const float& a = normal.x; const float& b = normal.y; const float& c = normal.z;
  458. const float d = -(a*min.x) - (b*min.y) - (c*min.z);
  459. const float distance = abs(d) / sqrt(a*a + b*b + c*c);
  460. Plane plane(normal, -distance);
  461. // Check for collision with plane.
  462. float collisionDistance = ray.intersects(plane);
  463. if (collisionDistance != Ray::INTERSECTS_NONE)
  464. {
  465. // Multiply the ray's direction vector by collision distance
  466. // and add that to the ray's origin.
  467. Vector3 point = ray.getOrigin() + collisionDistance*ray.getDirection();
  468. // Project this point into the plane.
  469. m.invert();
  470. m.transformPoint(&point);
  471. // Pass the touch event on.
  472. const Rectangle& bounds = form->getBounds();
  473. if (form->getState() == Control::FOCUS ||
  474. (evt == Touch::TOUCH_PRESS &&
  475. point.x >= bounds.x &&
  476. point.x <= bounds.x + bounds.width &&
  477. point.y >= bounds.y &&
  478. point.y <= bounds.y + bounds.height))
  479. {
  480. if (form->touchEvent(evt, point.x - bounds.x, bounds.height - point.y - bounds.y, contactIndex))
  481. {
  482. return true;
  483. }
  484. }
  485. }
  486. }
  487. }
  488. else
  489. {
  490. // Simply compare with the form's bounds.
  491. const Rectangle& bounds = form->getBounds();
  492. if (form->getState() == Control::FOCUS ||
  493. (evt == Touch::TOUCH_PRESS &&
  494. x >= bounds.x &&
  495. x <= bounds.x + bounds.width &&
  496. y >= bounds.y &&
  497. y <= bounds.y + bounds.height))
  498. {
  499. // Pass on the event's position relative to the form.
  500. if (form->touchEvent(evt, x - bounds.x, y - bounds.y, contactIndex))
  501. {
  502. return true;
  503. }
  504. }
  505. }
  506. }
  507. }
  508. return false;
  509. }
  510. void Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
  511. {
  512. std::vector<Form*>::const_iterator it;
  513. for (it = __forms.begin(); it < __forms.end(); it++)
  514. {
  515. Form* form = *it;
  516. GP_ASSERT(form);
  517. if (form->isEnabled())
  518. {
  519. form->keyEvent(evt, key);
  520. }
  521. }
  522. }
  523. int Form::nextHighestPowerOfTwo(int x)
  524. {
  525. x--;
  526. x |= x >> 1;
  527. x |= x >> 2;
  528. x |= x >> 4;
  529. x |= x >> 8;
  530. x |= x >> 16;
  531. return x + 1;
  532. }
  533. }