| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124 |
- #include "Base.h"
- #include "Form.h"
- #include "AbsoluteLayout.h"
- #include "FlowLayout.h"
- #include "VerticalLayout.h"
- #include "Game.h"
- #include "Theme.h"
- #include "Label.h"
- #include "Button.h"
- #include "CheckBox.h"
- #include "Scene.h"
- // Scroll speed when using a DPad -- max scroll speed when using a joystick.
- static const float GAMEPAD_SCROLL_SPEED = 500.0f;
- // Distance a joystick must be pushed in order to trigger focus-change and/or scrolling.
- static const float JOYSTICK_THRESHOLD = 0.75f;
- // If the DPad or joystick is held down, this is the initial delay in milliseconds between focus changes.
- static const float GAMEPAD_FOCUS_REPEAT_DELAY = 300.0f;
- // Shaders used for drawing offscreen quad when form is attached to a node
- #define FORM_VSH "res/shaders/sprite.vert"
- #define FORM_FSH "res/shaders/sprite.frag"
- namespace gameplay
- {
- static std::vector<Form*> __forms;
- static Control* __focusControl = NULL;
- static Control* __activeControl[Touch::MAX_TOUCH_POINTS];
- static bool __shiftKeyDown = false;
- /**
- * Static initializer for forms.
- * @script{ignore}
- */
- struct FormInit
- {
- FormInit()
- {
- memset(__activeControl, 0, sizeof(__activeControl));
- }
- };
- static FormInit __init;
- Form::Form() : _node(NULL), _batched(true)
- {
- }
- Form::~Form()
- {
- // Remove this Form from the global list.
- std::vector<Form*>::iterator it = std::find(__forms.begin(), __forms.end(), this);
- if (it != __forms.end())
- {
- __forms.erase(it);
- }
- }
- Form* Form::create(const char* url)
- {
- Form* form = new Form();
- // Load Form from .form file.
- Properties* properties = Properties::create(url);
- if (!properties)
- {
- GP_WARN("Failed to load properties file for Form.");
- return NULL;
- }
- // Check if the Properties is valid and has a valid namespace.
- Properties* formProperties = (strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace();
- if (!formProperties || !(strcmpnocase(formProperties->getNamespace(), "form") == 0))
- {
- GP_WARN("Invalid properties file for form: %s", url);
- SAFE_DELETE(properties);
- return NULL;
- }
- // Load the form's theme style.
- Theme* theme = NULL;
- Theme::Style* style = NULL;
- if (formProperties->exists("theme"))
- {
- std::string themeFile;
- if (formProperties->getPath("theme", &themeFile))
- {
- theme = Theme::create(themeFile.c_str());
- }
- }
- if (!theme)
- {
- theme = Theme::getDefault();
- }
- if (theme)
- {
- // Load the form's style
- const char* styleName = formProperties->getString("style", "Form");
- style = theme->getStyle(styleName);
- if (!style)
- style = theme->getEmptyStyle();
- }
- form->_batched = formProperties->getBool("batchingEnabled", true);
- // Initialize the form and all of its child controls
- form->initialize("Form", style, formProperties);
- // Release the theme: its lifetime is controlled by addRef() and release() calls in initialize (above) and ~Control.
- if (theme != Theme::getDefault())
- {
- SAFE_RELEASE(theme);
- }
- SAFE_DELETE(properties);
- return form;
- }
- Form* Form::create(const char* id, Theme::Style* style, Layout::Type layoutType)
- {
- Form* form = new Form();
- form->_id = id ? id : "";
- form->_layout = createLayout(layoutType);
- form->initialize("Form", style, NULL);
- return form;
- }
- void Form::initialize(const char* typeName, Theme::Style* style, Properties* properties)
- {
- Container::initialize(typeName, style, properties);
- __forms.push_back(this);
- // After creation, update our bounds once so code that runs immediately after form
- // creation has access to up-to-date bounds.
- if (updateBoundsInternal(Vector2::zero()))
- updateBoundsInternal(Vector2::zero());
- }
- Form* Form::getForm(const char* id)
- {
- for (size_t i = 0, size = __forms.size(); i < size; ++i)
- {
- Form* f = __forms[i];
- GP_ASSERT(f);
- if (strcmp(id, f->getId()) == 0)
- {
- return f;
- }
- }
- return NULL;
- }
- Control* Form::getActiveControl(unsigned int touchPoint)
- {
- if (touchPoint >= Touch::MAX_TOUCH_POINTS)
- return NULL;
- return __activeControl[touchPoint];
- }
- Control* Form::getFocusControl()
- {
- return __focusControl;
- }
- void Form::clearFocus()
- {
- setFocusControl(NULL);
- }
- bool Form::isForm() const
- {
- return true;
- }
- void Form::setNode(Node* node)
- {
- if (_node != node)
- {
- _node = node;
- }
- }
- static unsigned int nextPowerOfTwo(unsigned int v)
- {
- if (!((v & (v - 1)) == 0))
- {
- v--;
- v |= v >> 1;
- v |= v >> 2;
- v |= v >> 4;
- v |= v >> 8;
- v |= v >> 16;
- return v + 1;
- }
- return v;
- }
- void Form::update(float elapsedTime)
- {
- Container::update(elapsedTime);
- // Do a two-pass bounds update:
- // 1. First pass updates leaf controls
- // 2. Second pass updates parent controls that depend on child sizes
- if (updateBoundsInternal(Vector2::zero()))
- updateBoundsInternal(Vector2::zero());
- }
- void Form::startBatch(SpriteBatch* batch)
- {
- // TODO (note: might want to pass a level number/depth here so that batch draw calls can be sorted correctly, such as all text on top)
- if (!batch->isStarted())
- {
- batch->setProjectionMatrix(_projectionMatrix);
- batch->start();
- if (_batched)
- _batches.push_back(batch);
- }
- }
- void Form::finishBatch(SpriteBatch* batch)
- {
- if (!_batched)
- {
- batch->finish();
- }
- }
- const Matrix& Form::getProjectionMatrix() const
- {
- return _projectionMatrix;
- }
- unsigned int Form::draw()
- {
- if (!_visible || _absoluteClipBounds.width == 0 || _absoluteClipBounds.height == 0)
- return 0;
- Game* game = Game::getInstance();
- Rectangle viewport = game->getViewport();
- // If we're drawing in 2D (i.e. not attached to a node), we need to clear the depth buffer
- if (_node)
- {
- // Drawing in 3D.
- // Setup a projection matrix for drawing the form via the node's world transform.
- Matrix world(_node->getWorldMatrix());
- world.scale(1, -1, 1);
- world.translate(0, -_absoluteClipBounds.height, 0);
- Matrix::multiply(_node->getViewProjectionMatrix(), world, &_projectionMatrix);
- }
- else
- {
- // Drawing in 2D, so we need to clear the depth buffer
- Game::getInstance()->clear(Game::CLEAR_DEPTH, Vector4::zero(), 1, 0);
- // Setup an ortho matrix that maps to the current viewport
- const Rectangle& viewport = Game::getInstance()->getViewport();
- Matrix::createOrthographicOffCenter(0, viewport.width, viewport.height, 0, 0, 1, &_projectionMatrix);
- }
- // Draw the form
- unsigned int drawCalls = Container::draw(this, _absoluteClipBounds);
- // Flush all batches that were queued during drawing and then empty the batch list
- if (_batched)
- {
- unsigned int batchCount = _batches.size();
- for (unsigned int i = 0; i < batchCount; ++i)
- _batches[i]->finish();
- _batches.clear();
- drawCalls = batchCount;
- }
- return drawCalls;
- }
- const char* Form::getType() const
- {
- return "form";
- }
- bool Form::isBatchingEnabled() const
- {
- return _batched;
- }
- void Form::setBatchingEnabled(bool enabled)
- {
- _batched = enabled;
- }
- void Form::updateInternal(float elapsedTime)
- {
- pollGamepads();
- for (size_t i = 0, size = __forms.size(); i < size; ++i)
- {
- Form* form = __forms[i];
- if (form && form->isEnabled() && form->isVisible())
- {
- form->update(elapsedTime);
- }
- }
- }
- bool Form::screenToForm(Control* ctrl, int* x, int* y)
- {
- Form* form = ctrl->getTopLevelForm();
- if (form)
- {
- if (form->_node)
- {
- // Form is attached to a scene node, so project the screen space point into the
- // form's coordinate space (which may be transformed by the node).
- Vector3 point;
- if (form->projectPoint(*x, *y, &point))
- {
- *x = (int)point.x;
- *y = form->_absoluteBounds.height - (int)point.y;
- }
- else
- {
- return false;
- }
- }
- return true;
- }
- return false;
- }
- Control* Form::findInputControl(int* x, int* y, bool focus, unsigned int contactIndex)
- {
- for (int i = (int)__forms.size() - 1; i >= 0; --i)
- {
- Form* form = __forms[i];
- if (!form || !form->isEnabled() || !form->isVisible())
- continue;
- // Convert to local form coordinates
- int formX = *x;
- int formY = *y;
- if (!screenToForm(form, &formX, &formY))
- continue;
- // Search for an input control within this form
- Control* ctrl = findInputControl(form, formX, formY, focus, contactIndex);
- if (ctrl)
- {
- *x = formX;
- *y = formY;
- return ctrl;
- }
- // If the form consumes input events and the point intersects the form,
- // don't traverse other forms below it.
- if (form->_consumeInputEvents && form->_absoluteClipBounds.contains(formX, formY))
- return NULL;
- }
- return NULL;
- }
- Control* Form::findInputControl(Control* control, int x, int y, bool focus, unsigned int contactIndex)
- {
- if (!(control->_visible && control->isEnabled()))
- return NULL;
- Control* result = NULL;
- // Does the passed in control's bounds intersect the specified coordinates - and
- // does the control support the specified input state?
- if (control->_consumeInputEvents && (!focus || control->canFocus()))
- {
- if (control->_absoluteClipBounds.contains(x, y))
- result = control;
- }
- // If the control has children, search for an input control inside it that also
- // supports the above conditions.
- if (control->isContainer())
- {
- Container* container = static_cast<Container*>(control);
- for (unsigned int i = 0, childCount = container->getControlCount(); i < childCount; ++i)
- {
- Control* ctrl = findInputControl(container->getControl(i), x, y, focus, contactIndex);
- if (ctrl)
- result = ctrl;
- }
- }
- return result;
- }
- Control* Form::handlePointerPressRelease(int* x, int* y, bool pressed, unsigned int contactIndex)
- {
- if (contactIndex >= Touch::MAX_TOUCH_POINTS)
- return NULL;
- Control* ctrl = NULL;
- int newX = *x;
- int newY = *y;
- if (pressed)
- {
- // Update active state changes
- if ((ctrl = findInputControl(&newX, &newY, false, contactIndex)) != NULL)
- {
- if (__activeControl[contactIndex] != ctrl || ctrl->_state != ACTIVE)
- {
- if (__activeControl[contactIndex])
- {
- __activeControl[contactIndex]->_state = NORMAL;
- __activeControl[contactIndex]->setDirty(DIRTY_STATE);
- }
- __activeControl[contactIndex] = ctrl;
- ctrl->_state = ACTIVE;
- ctrl->setDirty(DIRTY_STATE);
- }
- ctrl->notifyListeners(Control::Listener::PRESS);
- }
- }
- else // !pressed
- {
- Control* active = (__activeControl[contactIndex] && __activeControl[contactIndex]->_state == ACTIVE) ? __activeControl[contactIndex] : NULL;
- if (active)
- {
- active->addRef(); // protect against event-hanlder evil
- // Release happened for an active control (that was pressed)
- ctrl = active;
- // Transform point to form-space
- screenToForm(ctrl, &newX, &newY);
- // No longer any active control
- active->setDirty(DIRTY_STATE);
- active->_state = NORMAL;
- __activeControl[contactIndex] = NULL;
- }
- else
- {
- // Update active and hover control state on release
- Control* inputControl = findInputControl(&newX, &newY, false, contactIndex);
- if (inputControl)
- {
- ctrl = inputControl;
- if (__activeControl[contactIndex] != ctrl || ctrl->_state != HOVER)
- {
- if (__activeControl[contactIndex])
- {
- __activeControl[contactIndex]->_state = NORMAL;
- __activeControl[contactIndex]->setDirty(Control::DIRTY_STATE);
- }
- __activeControl[contactIndex] = ctrl;
- ctrl->_state = HOVER;
- ctrl->setDirty(DIRTY_STATE);
- }
- }
- else
- {
- // Control no longer active
- if (__activeControl[contactIndex])
- {
- __activeControl[contactIndex]->setDirty(Control::DIRTY_STATE);
- __activeControl[contactIndex]->_state = NORMAL;
- __activeControl[contactIndex] = NULL;
- }
- }
- }
- if (active)
- {
- // Fire release event for the previously active control
- active->notifyListeners(Control::Listener::RELEASE);
- // If the release event was received on the same control that was
- // originally pressed, fire a click event
- if (active->_absoluteClipBounds.contains(newX, newY))
- {
- if (!active->_parent || !active->_parent->isScrolling())
- {
- active->notifyListeners(Control::Listener::CLICK);
- }
- }
- active->release();
- }
- }
- *x = newX;
- *y = newY;
- return ctrl;
- }
- Control* Form::handlePointerMove(int* x, int* y, unsigned int contactIndex)
- {
- if (contactIndex >= Touch::MAX_TOUCH_POINTS)
- return NULL;
- Control* ctrl = NULL;
- // Handle hover control changes on move, only if there is no currently active control
- // (i.e. when the mouse or a finger is not down).
- if (__activeControl[contactIndex] && __activeControl[contactIndex]->_state == ACTIVE)
- {
- // Active controls always continue receiving pointer events, even when the pointer
- // is not on top of the control.
- ctrl = __activeControl[contactIndex];
- screenToForm(ctrl, x, y);
- }
- else
- {
- ctrl = findInputControl(x, y, false, contactIndex);
- if (ctrl)
- {
- // Update hover control
- if (__activeControl[contactIndex] != ctrl || ctrl->_state != HOVER)
- {
- if (__activeControl[contactIndex])
- {
- __activeControl[contactIndex]->_state = NORMAL;
- __activeControl[contactIndex]->setDirty(DIRTY_STATE);
- }
- __activeControl[contactIndex] = ctrl;
- ctrl->_state = HOVER;
- ctrl->setDirty(DIRTY_STATE);
- }
- }
- else
- {
- // No active/hover control
- if (__activeControl[contactIndex])
- {
- __activeControl[contactIndex]->setDirty(DIRTY_STATE);
- __activeControl[contactIndex]->_state = NORMAL;
- __activeControl[contactIndex] = NULL;
- }
- }
- }
- return ctrl;
- }
- void Form::verifyRemovedControlState(Control* control)
- {
- if (__focusControl == control)
- {
- __focusControl = NULL;
- }
- if (control->_state == ACTIVE || control->_state == HOVER)
- {
- for (unsigned int i = 0; i < Touch::MAX_TOUCH_POINTS; ++i)
- {
- if (__activeControl[i] == control)
- {
- __activeControl[i] = NULL;
- }
- }
- control->_state = NORMAL;
- }
- }
- // Generic pointer event handler that both touch and mouse events map to.
- // Mappings:
- // mouse - true for mouse events, false for touch events
- // evt - Mouse::MouseEvent or Touch::TouchEvent
- // x, y - Point of event
- // param - wheelData for mouse events, contactIndex for touch events
- bool Form::pointerEventInternal(bool mouse, int evt, int x, int y, int param)
- {
- // Do not process mouse input when mouse is captured
- if (mouse && Game::getInstance()->isMouseCaptured())
- return false;
- // Is this a press event (TOUCH_PRESS has the same value as MOUSE_PRESS_LEFT_BUTTON)
- bool pressEvent = evt == Touch::TOUCH_PRESS || (mouse && (evt == Mouse::MOUSE_PRESS_MIDDLE_BUTTON || evt == Mouse::MOUSE_PRESS_RIGHT_BUTTON));
- Control* ctrl = NULL;
- int formX = x;
- int formY = y;
- unsigned int contactIndex = mouse ? 0 : param;
- // Note: TOUCH_PRESS and TOUCH_RELEASE have same values as MOUSE_PRESS_LEFT_BUTTON and MOUSE_RELEASE_LEFT_BUTTON
- if (evt == Touch::TOUCH_PRESS)
- {
- ctrl = handlePointerPressRelease(&formX, &formY, true, contactIndex);
- }
- else if (evt == Touch::TOUCH_RELEASE)
- {
- ctrl = handlePointerPressRelease(&formX, &formY, false, contactIndex);
- }
- else if ((mouse && evt == Mouse::MOUSE_MOVE) || (!mouse && evt == Touch::TOUCH_MOVE))
- {
- ctrl = handlePointerMove(&formX, &formY, contactIndex);
- }
- // Dispatch input events to all controls that intersect this point
- if (ctrl == NULL)
- {
- formX = x;
- formY = y;
- ctrl = findInputControl(&formX, &formY, false, contactIndex);
- }
- if (ctrl)
- {
- // Handle setting focus for all press events
- if (pressEvent)
- {
- Control* focusControl = ctrl;
- while (focusControl && !focusControl->setFocus())
- focusControl = focusControl->_parent;
- if (focusControl == NULL)
- {
- // Nothing got focus on this press, so remove current focused control
- setFocusControl(NULL);
- }
- }
- // Dispatch the event from the bottom upwards, until a control intersecting the point consumes the event
- while (ctrl)
- {
- int localX = formX - ctrl->_absoluteBounds.x;
- int localY = formY - ctrl->_absoluteBounds.y;
- if (mouse)
- {
- if (ctrl->mouseEvent((Mouse::MouseEvent)evt, localX, localY, param))
- return true;
- // Forward to touch event hanlder if unhandled by mouse handler
- switch (evt)
- {
- case Mouse::MOUSE_PRESS_LEFT_BUTTON:
- if (ctrl->touchEvent(Touch::TOUCH_PRESS, localX, localY, 0))
- return true;
- break;
- case Mouse::MOUSE_RELEASE_LEFT_BUTTON:
- if (ctrl->touchEvent(Touch::TOUCH_RELEASE, localX, localY, 0))
- return true;
- break;
- case Mouse::MOUSE_MOVE:
- if (ctrl->touchEvent(Touch::TOUCH_MOVE, localX, localY, 0))
- return true;
- break;
- }
- }
- else
- {
- if (ctrl->touchEvent((Touch::TouchEvent)evt, localX, localY, contactIndex))
- return true;
- }
- // Handle container scrolling
- Control* tmp = ctrl;
- while (tmp)
- {
- if (tmp->isContainer())
- {
- Container* container = static_cast<Container*>(tmp);
- if (container->_scroll != SCROLL_NONE)
- {
- if (mouse)
- {
- if (container->mouseEventScroll((Mouse::MouseEvent)evt, formX - tmp->_absoluteBounds.x, formY - tmp->_absoluteBounds.y, param))
- return true;
- }
- else
- {
- if (container->touchEventScroll((Touch::TouchEvent)evt, formX - tmp->_absoluteBounds.x, formY - tmp->_absoluteBounds.y, param))
- return true;
- }
- break; // scrollable parent container found
- }
- }
- tmp = tmp->_parent;
- }
- // Consume all input events anyways?
- if (ctrl->getConsumeInputEvents())
- return true;
- ctrl = ctrl->getParent();
- }
- }
- else
- {
- // If this was a press event, remove all focus
- if (pressEvent)
- {
- setFocusControl(NULL);
- }
- }
- return false;
- }
- bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
- {
- return pointerEventInternal(false, evt, x, y, (int)contactIndex);
- }
- bool Form::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
- {
- return pointerEventInternal(true, evt, x, y, wheelDelta);
- }
- bool Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
- {
- switch (key)
- {
- case Keyboard::KEY_ESCAPE:
- return false; // ignore escape key presses
- case Keyboard::KEY_SHIFT:
- if (evt == Keyboard::KEY_PRESS)
- __shiftKeyDown = true;
- else if (evt == Keyboard::KEY_RELEASE)
- __shiftKeyDown = false;
- break;
- }
- // Handle focus changing
- if (__focusControl)
- {
- switch (evt)
- {
- case Keyboard::KEY_CHAR:
- switch (key)
- {
- case Keyboard::KEY_TAB:
- if (__focusControl->_parent)
- {
- if (__focusControl->_parent->moveFocus(__shiftKeyDown ? Container::PREVIOUS : Container::NEXT))
- return true;
- }
- break;
- }
- break;
- }
- }
- // Dispatch key events
- Control* ctrl = __focusControl;
- while (ctrl)
- {
- if (ctrl->isEnabled() && ctrl->isVisible())
- {
- if (ctrl->keyEvent(evt, key))
- return true;
- }
- ctrl = ctrl->getParent();
- }
- return false;
- }
- void Form::pollGamepads()
- {
- Game* game = Game::getInstance();
- // If no gamepads are connected, return immediately
- unsigned int gamepadCount = game->getGamepadCount();
- if (gamepadCount == 0)
- return;
- // For now, always use gamepad zero for controlling the UI.
- // Possibly allow the developer to set the active gamepad for UI later.
- Gamepad* gamepad = game->getGamepad(0, true);
- if (!gamepad)
- return;
- pollGamepad(gamepad);
- }
- bool Form::pollGamepad(Gamepad* gamepad)
- {
- // Get the currently focused control's container for focus management and scrolling
- if (!__focusControl)
- return false;
- // Get parent container
- Container* parentContainer = NULL;
- if (__focusControl->_parent)
- parentContainer = __focusControl->_parent;
- // Get scroll container
- Container* scrollContainer = NULL;
- if (__focusControl->isContainer())
- {
- scrollContainer = static_cast<Container*>(__focusControl);
- if (scrollContainer->_scroll == SCROLL_NONE)
- scrollContainer = NULL;
- }
- if (!scrollContainer && parentContainer && parentContainer->_scroll != SCROLL_NONE)
- scrollContainer = parentContainer;
- // Static static maintained across function calls
- static bool scrolling = false;
- static double lastFocusChangeTime = 0;
- bool focusPressed = false;
- bool stillScrolling = false;
- double currentTime = Game::getAbsoluteTime();
- double focusChangeElapsedTime = currentTime - lastFocusChangeTime;
- // Is a selection button down (i.e. buttons used for UI clicking/interactions)?
- bool selectButtonDown = gamepad->isButtonDown(Gamepad::BUTTON_A) || gamepad->isButtonDown(Gamepad::BUTTON_X);
- if (!selectButtonDown)
- {
- // Get values of analog joysticks 1 and 2 (assume left and right analog sticks)
- Vector2 joystick;
- unsigned int joystickCount = gamepad->getJoystickCount();
- gamepad->getJoystickValues(0, &joystick);
- if (parentContainer)
- {
- // The Dpad and left analog stick (i.e. first analog stick when there are two joysticks) controls focus
- if (gamepad->isButtonDown(Gamepad::BUTTON_UP) || (joystickCount > 1 && joystick.y > JOYSTICK_THRESHOLD))
- {
- focusPressed = true;
- if (focusChangeElapsedTime > GAMEPAD_FOCUS_REPEAT_DELAY && parentContainer->moveFocus(UP))
- {
- lastFocusChangeTime = currentTime;
- }
- }
- if (gamepad->isButtonDown(Gamepad::BUTTON_DOWN) || (joystickCount > 1 && joystick.y < -JOYSTICK_THRESHOLD))
- {
- focusPressed = true;
- if (focusChangeElapsedTime > GAMEPAD_FOCUS_REPEAT_DELAY && parentContainer->moveFocus(DOWN))
- {
- lastFocusChangeTime = currentTime;
- }
- }
- if (gamepad->isButtonDown(Gamepad::BUTTON_LEFT) || (joystickCount > 1 && joystick.x < -JOYSTICK_THRESHOLD))
- {
- focusPressed = true;
- if (focusChangeElapsedTime > GAMEPAD_FOCUS_REPEAT_DELAY && parentContainer->moveFocus(LEFT))
- {
- lastFocusChangeTime = currentTime;
- }
- }
- if (gamepad->isButtonDown(Gamepad::BUTTON_RIGHT) || (joystickCount > 1 && joystick.x > JOYSTICK_THRESHOLD))
- {
- focusPressed = true;
- if (focusChangeElapsedTime > GAMEPAD_FOCUS_REPEAT_DELAY && parentContainer->moveFocus(RIGHT))
- {
- lastFocusChangeTime = currentTime;
- }
- }
- }
- // The RIGHT analog stick (i.e. second), or ONLY analog stick (when only 1 joystick), is used to scroll.
- if (scrollContainer && joystickCount > 0)
- {
- if (joystickCount > 1)
- gamepad->getJoystickValues(1, &joystick);
- if (std::fabs(joystick.x) > JOYSTICK_THRESHOLD || std::fabs(joystick.y) > JOYSTICK_THRESHOLD)
- {
- scrollContainer->startScrolling(GAMEPAD_SCROLL_SPEED * joystick.x, GAMEPAD_SCROLL_SPEED * joystick.y, !scrolling);
- scrolling = stillScrolling = true;
- }
- }
- }
- if (!focusPressed)
- {
- // Reset focus repeat
- lastFocusChangeTime = 0;
- }
- if (scrolling && !stillScrolling)
- {
- scrolling = false;
- if (scrollContainer)
- scrollContainer->stopScrolling();
- }
- return focusPressed || scrolling;
- }
- bool Form::gamepadButtonEventInternal(Gamepad* gamepad)
- {
- if (!__focusControl)
- return false;
- bool selectButtonPressed = gamepad->isButtonDown(Gamepad::BUTTON_A) || gamepad->isButtonDown(Gamepad::BUTTON_X);
- // Fire press, release and click events to focused controls
- if (selectButtonPressed && __focusControl->_state != ACTIVE)
- {
- if (__activeControl[0])
- __activeControl[0]->setDirty(DIRTY_STATE);
- __activeControl[0] = __focusControl;
- __focusControl->_state = ACTIVE;
- __focusControl->notifyListeners(Control::Listener::PRESS);
- return true;
- }
- else if (!selectButtonPressed && __focusControl->_state == ACTIVE)
- {
- if (__activeControl[0])
- __activeControl[0]->setDirty(DIRTY_STATE);
- for (unsigned int i = 0; i < Touch::MAX_TOUCH_POINTS; ++i)
- {
- if (__activeControl[i] == __focusControl)
- {
- __activeControl[i] = NULL;
- }
- }
- __focusControl->_state = NORMAL;
- __focusControl->notifyListeners(Control::Listener::RELEASE);
- __focusControl->notifyListeners(Control::Listener::CLICK);
- return true;
- }
- // Dispatch gamepad button events to focused controls (or their parents)
- Control* ctrl = __focusControl;
- while (ctrl)
- {
- if (ctrl->isEnabled() && ctrl->isVisible())
- {
- if (ctrl->gamepadButtonEvent(gamepad))
- return true;
- }
- ctrl = ctrl->getParent();
- }
- return false;
- }
- bool Form::gamepadTriggerEventInternal(Gamepad* gamepad, unsigned int index)
- {
- // Dispatch gamepad trigger events to focused controls (or their parents)
- Control* ctrl = __focusControl;
- while (ctrl)
- {
- if (ctrl->isEnabled() && ctrl->isVisible())
- {
- if (ctrl->gamepadTriggerEvent(gamepad, index))
- return true;
- }
- ctrl = ctrl->getParent();
- }
- return false;
- }
- bool Form::gamepadJoystickEventInternal(Gamepad* gamepad, unsigned int index)
- {
- // Dispatch gamepad joystick events to focused controls (or their parents)
- Control* ctrl = __focusControl;
- while (ctrl)
- {
- if (ctrl->isEnabled() && ctrl->isVisible())
- {
- if (ctrl->gamepadJoystickEvent(gamepad, index))
- return true;
- }
- ctrl = ctrl->getParent();
- }
- return false;
- }
- void Form::resizeEventInternal(unsigned int width, unsigned int height)
- {
- for (size_t i = 0, size = __forms.size(); i < size; ++i)
- {
- Form* form = __forms[i];
- if (form)
- {
- // Dirty the form
- form->setDirty(Control::DIRTY_BOUNDS | Control::DIRTY_STATE);
- }
- }
- }
- bool Form::projectPoint(int x, int y, Vector3* point)
- {
- if (!_node)
- return false;
- Scene* scene = _node->getScene();
- Camera* camera;
- if (scene && (camera = scene->getActiveCamera()))
- {
- // Get info about the form's position.
- Matrix m = _node->getWorldMatrix();
- Vector3 pointOnPlane(0, 0, 0);
- m.transformPoint(&pointOnPlane);
- // Unproject point into world space.
- Ray ray;
- camera->pickRay(Game::getInstance()->getViewport(), x, y, &ray);
- // Find the quad's plane. We know its normal is the quad's forward vector.
- Vector3 normal = _node->getForwardVectorWorld().normalize();
- // To get the plane's distance from the origin, we project a point on the
- // plane onto the plane's normal vector.
- const float distance = fabs(Vector3::dot(pointOnPlane, normal));
- Plane plane(normal, -distance);
- // Check for collision with plane.
- float collisionDistance = ray.intersects(plane);
- if (collisionDistance != Ray::INTERSECTS_NONE)
- {
- // Multiply the ray's direction vector by collision distance and add that to the ray's origin.
- point->set(ray.getOrigin() + collisionDistance*ray.getDirection());
- // Project this point into the plane.
- m.invert();
- m.transformPoint(point);
- return true;
- }
- }
- return false;
- }
- void Form::controlDisabled(Control* control)
- {
- if (__focusControl && (__focusControl == control || __focusControl->isChild(control)))
- {
- setFocusControl(NULL);
- }
- if (control->_state == ACTIVE || control->_state == HOVER)
- {
- for (unsigned int i = 0; i < Touch::MAX_TOUCH_POINTS; ++i)
- {
- if (__activeControl[i] == control)
- {
- __activeControl[i] = NULL;
- }
- }
- }
- }
- void Form::setFocusControl(Control* control)
- {
- Control* oldFocus = __focusControl;
- __focusControl = control;
- // Deactivate the old focus control
- if (oldFocus)
- {
- oldFocus->setDirty(Control::DIRTY_STATE);
- oldFocus->notifyListeners(Control::Listener::FOCUS_LOST);
- }
- // Activate the new focus control
- if (__focusControl)
- {
- __focusControl->setDirty(Control::DIRTY_STATE);
- __focusControl->notifyListeners(Control::Listener::FOCUS_GAINED);
- // Set the activeControl property of the control's parent container
- Container* parent = NULL;
- if (__focusControl->_parent)
- {
- parent = __focusControl->_parent;
- parent->_activeControl = __focusControl;
- }
- // If this control is inside a scrollable container and is not fully visible,
- // scroll the container so that it is.
- if (parent && parent->_scroll != SCROLL_NONE && !parent->_viewportBounds.isEmpty())
- {
- const Rectangle& bounds = __focusControl->getBounds();
- if (bounds.x < parent->_scrollPosition.x)
- {
- // Control is to the left of the scrolled viewport.
- parent->_scrollPosition.x = -bounds.x;
- }
- else if (bounds.x + bounds.width > parent->_scrollPosition.x + parent->_viewportBounds.width)
- {
- // Control is off to the right.
- parent->_scrollPosition.x = -(bounds.x + bounds.width - parent->_viewportBounds.width);
- }
- if (bounds.y < parent->_viewportBounds.y - parent->_scrollPosition.y)
- {
- // Control is above the viewport.
- parent->_scrollPosition.y = -bounds.y;
- }
- else if (bounds.y + bounds.height > parent->_viewportBounds.height - parent->_scrollPosition.y)
- {
- // Control is below the viewport.
- parent->_scrollPosition.y = -(bounds.y + bounds.height - parent->_viewportBounds.height);
- }
- }
- }
- }
- }
|