| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526 |
- #include "Base.h"
- #include "Container.h"
- #include "Layout.h"
- #include "AbsoluteLayout.h"
- #include "FlowLayout.h"
- #include "VerticalLayout.h"
- #include "ControlFactory.h"
- #include "Form.h"
- #include "Game.h"
- namespace gameplay
- {
- // If the user stops scrolling for this amount of time (in millis) before touch/click release, don't apply inertia.
- static const long SCROLL_INERTIA_DELAY = 100L;
- // Factor to multiply friction by before applying to velocity.
- static const float SCROLL_FRICTION_FACTOR = 5.0f;
- // Distance that must be scrolled before isScrolling() will return true, used e.g. to cancel button-click events.
- static const float SCROLL_THRESHOLD = 10.0f;
- // If the DPad or joystick is held down, this is the initial delay in milliseconds between focus change events.
- static const float FOCUS_CHANGE_REPEAT_DELAY = 300.0f;
- /**
- * Sort function for use with _controls.sort(), based on Z-Order.
- *
- * @param c1 The first control
- * @param c2 The second control
- * return true if the first controls z index is less than the second.
- */
- static bool sortControlsByZOrder(Control* c1, Control* c2);
- void Container::clearContacts()
- {
- for (int i = 0; i < MAX_CONTACT_INDICES; ++i)
- _contactIndices[i] = false;
- }
- Container::Container()
- : _layout(NULL), _activeControl(NULL), _scrollBarTopCap(NULL), _scrollBarVertical(NULL), _scrollBarBottomCap(NULL),
- _scrollBarLeftCap(NULL), _scrollBarHorizontal(NULL), _scrollBarRightCap(NULL),
- _scroll(SCROLL_NONE), _scrollBarBounds(Rectangle::empty()), _scrollPosition(Vector2::zero()),
- _scrollBarsAutoHide(false), _scrollBarOpacity(1.0f), _scrolling(false),
- _scrollingVeryFirstX(0), _scrollingVeryFirstY(0), _scrollingFirstX(0), _scrollingFirstY(0), _scrollingLastX(0), _scrollingLastY(0),
- _scrollingStartTimeX(0), _scrollingStartTimeY(0), _scrollingLastTime(0),
- _scrollingVelocity(Vector2::zero()), _scrollingFriction(1.0f), _scrollWheelSpeed(400.0f),
- _scrollingRight(false), _scrollingDown(false),
- _scrollingMouseVertically(false), _scrollingMouseHorizontally(false),
- _scrollBarOpacityClip(NULL), _zIndexDefault(0),
- _selectButtonDown(false), _lastFrameTime(0), _totalWidth(0), _totalHeight(0),
- _initializedWithScroll(false), _scrollWheelRequiresFocus(false), _allowRelayout(true)
- {
- clearContacts();
- }
- Container::~Container()
- {
- std::vector<Control*>::iterator it;
- for (it = _controls.begin(); it < _controls.end(); it++)
- {
- SAFE_RELEASE((*it));
- }
- SAFE_RELEASE(_layout);
- }
- Container* Container::create(const char* id, Theme::Style* style, Layout::Type layoutType)
- {
- GP_ASSERT(style);
- Container* container = new Container();
- container->_layout = createLayout(layoutType);
- if (id)
- container->_id = id;
- container->_style = style;
- return container;
- }
- Control* Container::create(Theme::Style* style, Properties* properties)
- {
- GP_ASSERT(properties);
- Container* container = new Container();
- container->initialize(style, properties);
- return container;
- }
- void Container::initialize(Theme::Style* style, Properties* properties)
- {
- Control::initialize(style, properties);
- // Parse layout
- Properties* layoutNS = properties->getNamespace("layout", true, false);
- if (layoutNS)
- {
- _layout = createLayout(getLayoutType(layoutNS->getString("type")));
- switch (_layout->getType())
- {
- case Layout::LAYOUT_FLOW:
- static_cast<FlowLayout*>(_layout)->setSpacing(layoutNS->getInt("horizontalSpacing"), layoutNS->getInt("verticalSpacing"));
- break;
- case Layout::LAYOUT_VERTICAL:
- static_cast<VerticalLayout*>(_layout)->setSpacing(layoutNS->getInt("spacing"));
- break;
- }
- }
- else
- {
- _layout = createLayout(getLayoutType(properties->getString("layout")));
- }
- setScroll(getScroll(properties->getString("scroll")));
- _scrollBarsAutoHide = properties->getBool("scrollBarsAutoHide");
- if (_scrollBarsAutoHide)
- {
- _scrollBarOpacity = 0.0f;
- }
-
- _scrollWheelRequiresFocus = properties->getBool("scrollWheelRequiresFocus");
- if (properties->exists("scrollingFriction"))
- _scrollingFriction = properties->getFloat("scrollingFriction");
- if (properties->exists("scrollWheelSpeed"))
- _scrollWheelSpeed = properties->getFloat("scrollWheelSpeed");
- addControls(style->getTheme(), properties);
- _layout->update(this, _scrollPosition);
- const char* activeControl = properties->getString("activeControl");
- if (activeControl)
- {
- for (size_t i = 0, count = _controls.size(); i < count; ++i)
- {
- if (_controls[i]->_id == activeControl)
- {
- _activeControl = _controls[i];
- break;
- }
- }
- }
- }
- void Container::addControls(Theme* theme, Properties* properties)
- {
- GP_ASSERT(theme);
- GP_ASSERT(properties);
- // Add all the controls to this container.
- Properties* controlSpace = properties->getNextNamespace();
- while (controlSpace != NULL)
- {
- Control* control = NULL;
- const char* controlStyleName = controlSpace->getString("style");
- Theme::Style* controlStyle = NULL;
- if (controlStyleName)
- {
- controlStyle = theme->getStyle(controlStyleName);
- }
- else
- {
- controlStyle = theme->getEmptyStyle();
- }
- std::string controlName(controlSpace->getNamespace());
- std::transform(controlName.begin(), controlName.end(), controlName.begin(), (int(*)(int))toupper);
- control = ControlFactory::getInstance()->createControl(controlName.c_str(), controlStyle, controlSpace);
- // Add the new control to the form.
- if (control)
- {
- addControl(control);
- control->release();
- }
- // Get the next control.
- controlSpace = properties->getNextNamespace();
- }
- // Sort controls by Z-Order.
- sortControls();
- }
- Layout* Container::getLayout()
- {
- return _layout;
- }
- unsigned int Container::addControl(Control* control)
- {
- GP_ASSERT(control);
- // Remove the control from its current parent
- if (control->_parent && control->_parent != this)
- {
- control->_parent->removeControl(control);
- }
- if (control->getZIndex() == -1)
- {
- control->setZIndex(_zIndexDefault++);
- }
- if (control->getFocusIndex() == -1)
- {
- // Find the current largest focus index
- int maxFocusIndex = 0;
- for (size_t i = 0, count = _controls.size(); i < count; ++i)
- {
- if (_controls[i]->_focusIndex > maxFocusIndex)
- maxFocusIndex = _controls[i]->_focusIndex;
- }
- control->setFocusIndex(maxFocusIndex + 1);
- }
- if (control->_parent != this)
- {
- _controls.push_back(control);
- control->addRef();
- control->_parent = this;
- sortControls();
- return (unsigned int)(_controls.size() - 1);
- }
- else
- {
- // Control is already in this container.
- // Do nothing but determine and return control's index.
- const size_t size = _controls.size();
- for (size_t i = 0; i < size; ++i)
- {
- Control* c = _controls[i];
- if (c == control)
- {
- return (unsigned int)i;
- }
- }
- // Should never reach this.
- GP_ASSERT(false);
- return 0;
- }
- }
- void Container::insertControl(Control* control, unsigned int index)
- {
- GP_ASSERT(control);
- if (control->_parent && control->_parent != this)
- {
- control->_parent->removeControl(control);
- }
- if (control->_parent != this)
- {
- std::vector<Control*>::iterator it = _controls.begin() + index;
- _controls.insert(it, control);
- control->addRef();
- control->_parent = this;
- }
- }
- void Container::removeControl(unsigned int index)
- {
- GP_ASSERT(index < _controls.size());
- std::vector<Control*>::iterator it = _controls.begin() + index;
- Control* control = *it;
- _controls.erase(it);
- control->_parent = NULL;
- if (_activeControl == control)
- _activeControl = NULL;
- Form::verifyRemovedControlState(control);
- SAFE_RELEASE(control);
- }
- void Container::removeControl(const char* id)
- {
- GP_ASSERT(id);
- for (size_t i = 0, size = _controls.size(); i < size; ++i)
- {
- Control* c = _controls[i];
- if (strcmp(id, c->getId()) == 0)
- {
- removeControl((unsigned int)i);
- return;
- }
- }
- }
- void Container::removeControl(Control* control)
- {
- GP_ASSERT(control);
- for (size_t i = 0, size = _controls.size(); i < size; ++i)
- {
- Control* c = _controls[i];
- if (c == control)
- {
- removeControl((unsigned int)i);
- return;
- }
- }
- }
- Control* Container::getControl(unsigned int index) const
- {
- GP_ASSERT(index < _controls.size());
- return _controls[index];
- }
- Control* Container::getControl(const char* id) const
- {
- GP_ASSERT(id);
- std::vector<Control*>::const_iterator it;
- for (it = _controls.begin(); it < _controls.end(); it++)
- {
- Control* c = *it;
- GP_ASSERT(c);
- if (strcmp(id, c->getId()) == 0)
- {
- return c;
- }
- else if (c->isContainer())
- {
- Control* cc = ((Container*)c)->getControl(id);
- if (cc)
- {
- return cc;
- }
- }
- }
- return NULL;
- }
- unsigned int Container::getControlCount() const
- {
- return (unsigned int)_controls.size();
- }
- const std::vector<Control*>& Container::getControls() const
- {
- return _controls;
- }
- bool Container::isForm() const
- {
- return false;
- }
- void Container::setScroll(Scroll scroll)
- {
- if (scroll != _scroll)
- {
- _scroll = scroll;
- _dirty = true;
- // Scrollable containers can be focused (to allow scrolling)
- if (_scroll != SCROLL_NONE)
- _canFocus = true;
- }
- }
- Container::Scroll Container::getScroll() const
- {
- return _scroll;
- }
- void Container::setScrollBarsAutoHide(bool autoHide)
- {
- if (autoHide != _scrollBarsAutoHide)
- {
- _scrollBarsAutoHide = autoHide;
- _dirty = true;
- }
- }
- bool Container::isScrollBarsAutoHide() const
- {
- return _scrollBarsAutoHide;
- }
- bool Container::isScrolling() const
- {
- if (_parent && _parent->isScrolling())
- return true;
- return (_scrolling &&
- (abs(_scrollingLastX - _scrollingVeryFirstX) > SCROLL_THRESHOLD ||
- abs(_scrollingLastY - _scrollingVeryFirstY) > SCROLL_THRESHOLD));
- }
- const Vector2& Container::getScrollPosition() const
- {
- return _scrollPosition;
- }
- void Container::setScrollPosition(const Vector2& scrollPosition)
- {
- _scrollPosition = scrollPosition;
- }
- Animation* Container::getAnimation(const char* id) const
- {
- std::vector<Control*>::const_iterator itr = _controls.begin();
- std::vector<Control*>::const_iterator end = _controls.end();
- Control* control = NULL;
- for (; itr != end; itr++)
- {
- control = *itr;
- GP_ASSERT(control);
- Animation* animation = control->getAnimation(id);
- if (animation)
- return animation;
- if (control->isContainer())
- {
- animation = ((Container*)control)->getAnimation(id);
- if (animation)
- return animation;
- }
- }
- return NULL;
- }
- const char* Container::getType() const
- {
- return "container";
- }
- bool Container::getScrollWheelRequiresFocus() const
- {
- return _scrollWheelRequiresFocus;
- }
- void Container::setScrollWheelRequiresFocus(bool required)
- {
- _scrollWheelRequiresFocus = required;
- }
- bool Container::setFocus()
- {
- // If this container (or one of its children) already has focus, do nothing
- if (Form::_focusControl && (Form::_focusControl == this || Form::_focusControl->isChild(this)))
- return true;
- // First try to set focus to our active control
- if (_activeControl)
- {
- if (_activeControl->setFocus())
- return true;
- }
- // Try to set focus to one of our children
- for (size_t i = 0, count = _controls.size(); i < count; ++i)
- {
- if (_controls[i]->setFocus())
- return true;
- }
- // Lastly, try to set focus to ourself if none of our children will accept it
- return Control::setFocus();
- }
- Control* Container::getActiveControl() const
- {
- return _activeControl;
- }
- void Container::setActiveControl(Control* control)
- {
- if (std::find(_controls.begin(), _controls.end(), control) != _controls.end())
- {
- _activeControl = control;
- // If a control within this container currently has focus, switch focus to the new active control
- if (Form::_focusControl && Form::_focusControl != control && Form::_focusControl->isChild(this))
- Form::setFocusControl(control);
- }
- }
- void Container::update(const Control* container, const Vector2& offset)
- {
- // Update this container's viewport.
- Control::update(container, offset);
- Control::State state = getState();
- // Get scrollbar images and diminish clipping bounds to make room for scrollbars.
- if ((_scroll & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL)
- {
- _scrollBarLeftCap = getImage("scrollBarLeftCap", state);
- _scrollBarHorizontal = getImage("horizontalScrollBar", state);
- _scrollBarRightCap = getImage("scrollBarRightCap", state);
- GP_ASSERT(_scrollBarLeftCap && _scrollBarHorizontal && _scrollBarRightCap);
- _viewportBounds.height -= _scrollBarHorizontal->getRegion().height;
- _viewportClipBounds.height -= _scrollBarHorizontal->getRegion().height;
- }
- if ((_scroll & SCROLL_VERTICAL) == SCROLL_VERTICAL)
- {
- _scrollBarTopCap = getImage("scrollBarTopCap", state);
- _scrollBarVertical = getImage("verticalScrollBar", state);
- _scrollBarBottomCap = getImage("scrollBarBottomCap", state);
- GP_ASSERT(_scrollBarTopCap && _scrollBarVertical && _scrollBarBottomCap);
- _viewportBounds.width -= _scrollBarVertical->getRegion().width;
- _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
- }
- GP_ASSERT(_layout);
- if (_scroll != SCROLL_NONE)
- {
- updateScroll();
- }
- else
- {
- _layout->update(this, Vector2::zero());
- }
- // Handle automatically sizing based on our children
- if (_autoWidth == Control::AUTO_SIZE_FIT || _autoHeight == Control::AUTO_SIZE_FIT)
- {
- Vector2 oldSize(_bounds.width, _bounds.height);
- bool sizeChanged = false;
- bool relayout = false;
- if (_autoWidth == Control::AUTO_SIZE_FIT)
- {
- // Size ourself to tightly fit the width of our children
- float width = 0;
- for (std::vector<Control*>::const_iterator it = _controls.begin(); it < _controls.end(); ++it)
- {
- Control* ctrl = *it;
- if (ctrl->isXPercentage() || ctrl->isWidthPercentage())
- {
- // We (this control's parent) are resizing and our child's layout
- // depends on our size, so we need to dirty it
- ctrl->_dirty = true;
- relayout = _allowRelayout;
- }
- else
- {
- float w = ctrl->getX() + ctrl->getWidth();
- if (width < w)
- width = w;
- }
- }
- width += getBorder(state).left + getBorder(state).right + getPadding().left + getPadding().right;
- if (width != oldSize.x)
- {
- setWidth(width);
- sizeChanged = true;
- }
- }
- if (_autoHeight == Control::AUTO_SIZE_FIT)
- {
- // Size ourself to tightly fit the height of our children
- float height = 0;
- for (std::vector<Control*>::const_iterator it = _controls.begin(); it < _controls.end(); ++it)
- {
- Control* ctrl = *it;
- if (ctrl->isYPercentage() || ctrl->isHeightPercentage())
- {
- // We (this control's parent) are resizing and our child's layout
- // depends on our size, so we need to dirty it
- ctrl->_dirty = true;
- relayout = _allowRelayout;
- }
- else
- {
- float h = ctrl->getY() + ctrl->getHeight();
- if (height < h)
- height = h;
- }
- }
- height += getBorder(state).top + getBorder(state).bottom + getPadding().top + getPadding().bottom;
- if (height != oldSize.y)
- {
- setHeight(height);
- sizeChanged = true;
- }
- }
- if (sizeChanged && relayout)
- {
- // Our size changed and as a result we need to force another layout.
- // Prevent infinitely recursive layouts by disabling relayout for the next call.
- _allowRelayout = false;
- update(container, offset);
- _allowRelayout = true;
- }
- }
- }
- void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, bool cleared, float targetHeight)
- {
- if (needsClear)
- {
- GL_ASSERT( glEnable(GL_SCISSOR_TEST) );
- float clearY = targetHeight - _clearBounds.y - _clearBounds.height;
- GL_ASSERT( glScissor(_clearBounds.x, clearY, _clearBounds.width, _clearBounds.height) );
- Game::getInstance()->clear(Game::CLEAR_COLOR, Vector4::zero(), 1.0f, 0);
- GL_ASSERT( glDisable(GL_SCISSOR_TEST) );
- needsClear = false;
- cleared = true;
- }
- else if (!cleared)
- {
- needsClear = true;
- }
- if (!_visible)
- {
- _dirty = false;
- return;
- }
- spriteBatch->start();
- Control::drawBorder(spriteBatch, clip);
- spriteBatch->finish();
- std::vector<Control*>::const_iterator it;
- Rectangle boundsUnion = Rectangle::empty();
- for (it = _controls.begin(); it < _controls.end(); it++)
- {
- Control* control = *it;
- GP_ASSERT(control);
- if (!needsClear || control->isDirty() || control->_clearBounds.intersects(boundsUnion))
- {
- control->draw(spriteBatch, _viewportClipBounds, needsClear, cleared, targetHeight);
- Rectangle::combine(control->_clearBounds, boundsUnion, &boundsUnion);
- }
- }
- if (_scroll != SCROLL_NONE && (_scrollBarOpacity > 0.0f))
- {
- // Draw scroll bars.
- Rectangle clipRegion(_viewportClipBounds);
- spriteBatch->start();
- if (_scrollBarBounds.height > 0 && ((_scroll & SCROLL_VERTICAL) == SCROLL_VERTICAL))
- {
- const Rectangle& topRegion = _scrollBarTopCap->getRegion();
- const Theme::UVs& topUVs = _scrollBarTopCap->getUVs();
- Vector4 topColor = _scrollBarTopCap->getColor();
- topColor.w *= _scrollBarOpacity * _opacity;
- const Rectangle& verticalRegion = _scrollBarVertical->getRegion();
- const Theme::UVs& verticalUVs = _scrollBarVertical->getUVs();
- Vector4 verticalColor = _scrollBarVertical->getColor();
- verticalColor.w *= _scrollBarOpacity * _opacity;
- const Rectangle& bottomRegion = _scrollBarBottomCap->getRegion();
- const Theme::UVs& bottomUVs = _scrollBarBottomCap->getUVs();
- Vector4 bottomColor = _scrollBarBottomCap->getColor();
- bottomColor.w *= _scrollBarOpacity * _opacity;
- clipRegion.width += verticalRegion.width;
- Rectangle bounds(_viewportBounds.x + _viewportBounds.width, _viewportBounds.y + _scrollBarBounds.y, topRegion.width, topRegion.height);
- spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, topUVs.u1, topUVs.v1, topUVs.u2, topUVs.v2, topColor, clipRegion);
- bounds.y += topRegion.height;
- bounds.height = _scrollBarBounds.height - topRegion.height - bottomRegion.height;
- spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, verticalUVs.u1, verticalUVs.v1, verticalUVs.u2, verticalUVs.v2, verticalColor, clipRegion);
- bounds.y += bounds.height;
- bounds.height = bottomRegion.height;
- spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, bottomUVs.u1, bottomUVs.v1, bottomUVs.u2, bottomUVs.v2, bottomColor, clipRegion);
- }
- if (_scrollBarBounds.width > 0 && ((_scroll & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL))
- {
- const Rectangle& leftRegion = _scrollBarLeftCap->getRegion();
- const Theme::UVs& leftUVs = _scrollBarLeftCap->getUVs();
- Vector4 leftColor = _scrollBarLeftCap->getColor();
- leftColor.w *= _scrollBarOpacity * _opacity;
- const Rectangle& horizontalRegion = _scrollBarHorizontal->getRegion();
- const Theme::UVs& horizontalUVs = _scrollBarHorizontal->getUVs();
- Vector4 horizontalColor = _scrollBarHorizontal->getColor();
- horizontalColor.w *= _scrollBarOpacity * _opacity;
- const Rectangle& rightRegion = _scrollBarRightCap->getRegion();
- const Theme::UVs& rightUVs = _scrollBarRightCap->getUVs();
- Vector4 rightColor = _scrollBarRightCap->getColor();
- rightColor.w *= _scrollBarOpacity * _opacity;
- clipRegion.height += horizontalRegion.height;
-
- Rectangle bounds(_viewportBounds.x + _scrollBarBounds.x, _viewportBounds.y + _viewportBounds.height, leftRegion.width, leftRegion.height);
- spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, leftUVs.u1, leftUVs.v1, leftUVs.u2, leftUVs.v2, leftColor, clipRegion);
- bounds.x += leftRegion.width;
- bounds.width = _scrollBarBounds.width - leftRegion.width - rightRegion.width;
- spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, horizontalUVs.u1, horizontalUVs.v1, horizontalUVs.u2, horizontalUVs.v2, horizontalColor, clipRegion);
- bounds.x += bounds.width;
- bounds.width = rightRegion.width;
- spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, rightUVs.u1, rightUVs.v1, rightUVs.u2, rightUVs.v2, rightColor, clipRegion);
- }
- spriteBatch->finish();
- if (_scrollingVelocity.isZero())
- {
- _dirty = false;
- }
- }
- else
- {
- _dirty = false;
- }
- }
- bool Container::isDirty()
- {
- if (_dirty)
- {
- return true;
- }
- else
- {
- std::vector<Control*>::const_iterator it;
- for (it = _controls.begin(); it < _controls.end(); it++)
- {
- GP_ASSERT(*it);
- if ((*it)->isDirty())
- {
- return true;
- }
- }
- }
- return false;
- }
- static bool canReceiveFocus(Control* control)
- {
- if (control->getFocusIndex() < 0 || !(control->isEnabled() && control->isVisible()))
- return false;
- if (control->canFocus())
- return true;
- if (control->isContainer())
- {
- Container* container = static_cast<Container*>(control);
- for (unsigned int i = 0, count = (unsigned int)container->getControlCount(); i < count; ++i)
- {
- if (canReceiveFocus(container->getControl(i)))
- return true;
- }
- }
- return false;
- }
- bool Container::moveFocus(Direction direction)
- {
- switch (direction)
- {
- case NEXT:
- case PREVIOUS:
- return moveFocusNextPrevious(direction);
- case UP:
- case DOWN:
- case LEFT:
- case RIGHT:
- return moveFocusDirectional(direction);
- default:
- return false;
- }
- }
- bool Container::moveFocusNextPrevious(Direction direction)
- {
- // Get the current control that has focus (either directly or indirectly) within this container
- Control* current = NULL;
- if (Form::_focusControl && Form::_focusControl->isChild(this))
- {
- if (Form::_focusControl->_parent == this)
- {
- // Currently focused control is a direct child of us
- current = Form::_focusControl;
- }
- else
- {
- // Currently focused control is a child of one of our child containers
- for (size_t i = 0, count = _controls.size(); i < count; ++i)
- {
- if (Form::_focusControl->isChild(_controls[i]))
- {
- current = _controls[i];
- break;
- }
- }
- }
- }
- Control* nextCtrl = NULL;
- int nextIndex = direction == NEXT ? INT_MAX : INT_MIN;
- bool moveFirst = false;
- if (current)
- {
- // There is a control inside us that currently has focus, so find the next control that
- // should receive focus.
- int focusableControlCount = 0; // track the number of valid focusable controls in this container
- for (size_t i = 0, count = _controls.size(); i < count; ++i)
- {
- Control* ctrl = _controls[i];
- if (!canReceiveFocus(ctrl))
- continue;
- if ((direction == NEXT && ctrl->_focusIndex > current->_focusIndex && ctrl->_focusIndex < nextIndex) ||
- (direction == PREVIOUS && ctrl->_focusIndex < current->_focusIndex && ctrl->_focusIndex > nextIndex))
- {
- nextCtrl = ctrl;
- nextIndex = ctrl->_focusIndex;
- }
- ++focusableControlCount;
- }
- if (nextCtrl)
- {
- if (nextCtrl->isContainer() && static_cast<Container*>(nextCtrl)->moveFocus(direction))
- return true;
- if (nextCtrl->setFocus())
- return true;
- }
- // Search up into our parent container for a focus move
- if (_parent && _parent->moveFocus(direction))
- return true;
- // We didn't find a control to move to, so we must be the first or last focusable control in our parent.
- // Wrap focus to the other side of the container.
- if (focusableControlCount > 1)
- {
- moveFirst = true;
- }
- }
- else
- {
- moveFirst = true;
- }
- if (moveFirst)
- {
- nextIndex = direction == NEXT ? INT_MAX : INT_MIN;
- nextCtrl = NULL;
- for (size_t i = 0, count = _controls.size(); i < count; ++i)
- {
- Control* ctrl = _controls[i];
- if (!canReceiveFocus(ctrl))
- continue;
- if ((direction == NEXT && ctrl->_focusIndex < nextIndex) ||
- (direction == PREVIOUS && ctrl->_focusIndex > nextIndex))
- {
- nextCtrl = ctrl;
- nextIndex = ctrl->_focusIndex;
- }
- }
- if (nextCtrl)
- {
- if (nextCtrl->isContainer() && static_cast<Container*>(nextCtrl)->moveFocus(direction))
- return true;
- if (nextCtrl->setFocus())
- return true;
- }
- }
- return false;
- }
- bool Container::moveFocusDirectional(Direction direction)
- {
- Control* startControl = Form::_focusControl;
- if (startControl == NULL)
- return false;
- const Rectangle& startBounds = startControl->_absoluteBounds;
- Control* next = NULL;
- Vector2 vStart, vNext;
- float distance = FLT_MAX;
- switch (direction)
- {
- case UP:
- vStart.set(startBounds.x + startBounds.width * 0.5f, startBounds.y);
- break;
- case DOWN:
- vStart.set(startBounds.x + startBounds.width * 0.5f, startBounds.bottom());
- break;
- case LEFT:
- vStart.set(startBounds.x, startBounds.y + startBounds.height * 0.5f);
- break;
- case RIGHT:
- vStart.set(startBounds.right(), startBounds.y + startBounds.height * 0.5f);
- break;
- }
- for (size_t i = 0, count = _controls.size(); i < count; ++i)
- {
- Control* ctrl = _controls[i];
- if (!canReceiveFocus(ctrl))
- continue;
- const Rectangle& nextBounds = ctrl->getAbsoluteBounds();
- switch (direction)
- {
- case UP:
- vNext.set(nextBounds.x + nextBounds.width * 0.5f, nextBounds.bottom());
- if (vNext.y > vStart.y)
- continue;
- break;
- case DOWN:
- vNext.set(nextBounds.x + nextBounds.width * 0.5f, nextBounds.y);
- if (vNext.y < vStart.y)
- continue;
- break;
- case LEFT:
- vNext.set(nextBounds.right(), nextBounds.y + nextBounds.height * 0.5f);
- if (vNext.x > vStart.x)
- continue;
- break;
- case RIGHT:
- vNext.set(nextBounds.x, nextBounds.y + nextBounds.height * 0.5f);
- if (vNext.x < vStart.x)
- continue;
- break;
- }
- float nextDistance = vStart.distance(vNext);
- if (std::fabs(nextDistance) < distance)
- {
- distance = nextDistance;
- next = ctrl;
- }
- }
- if (next)
- {
- // If this control is a container, try to move focus to the first control within it
- if (next->isContainer())
- {
- if (static_cast<Container*>(next)->moveFocusDirectional(direction))
- return true;
- }
- if (next->setFocus())
- return true;
- }
- else
- {
- // If no control was found, try searching in our parent container
- if (_parent && _parent->moveFocusDirectional(direction))
- return true;
- }
- return false;
- }
- void Container::startScrolling(float x, float y, bool resetTime)
- {
- _scrollingVelocity.set(-x, y);
- _scrolling = true;
- _scrollBarOpacity = 1.0f;
- _dirty = true;
- if (_scrollBarOpacityClip && _scrollBarOpacityClip->isPlaying())
- {
- _scrollBarOpacityClip->stop();
- _scrollBarOpacityClip = NULL;
- }
- if (resetTime)
- {
- _lastFrameTime = Game::getAbsoluteTime();
- }
- }
- void Container::stopScrolling()
- {
- _scrollingVelocity.set(0, 0);
- _scrolling = false;
- _dirty = true;
- }
- bool Container::isContainer() const
- {
- return true;
- }
- Layout::Type Container::getLayoutType(const char* layoutString)
- {
- if (!layoutString)
- {
- return Layout::LAYOUT_ABSOLUTE;
- }
- std::string layoutName(layoutString);
- std::transform(layoutName.begin(), layoutName.end(), layoutName.begin(), (int(*)(int))toupper);
- if (layoutName == "LAYOUT_ABSOLUTE")
- {
- return Layout::LAYOUT_ABSOLUTE;
- }
- else if (layoutName == "LAYOUT_VERTICAL")
- {
- return Layout::LAYOUT_VERTICAL;
- }
- else if (layoutName == "LAYOUT_FLOW")
- {
- return Layout::LAYOUT_FLOW;
- }
- else
- {
- // Default.
- return Layout::LAYOUT_ABSOLUTE;
- }
- }
- Layout* Container::createLayout(Layout::Type type)
- {
- switch (type)
- {
- case Layout::LAYOUT_ABSOLUTE:
- return AbsoluteLayout::create();
- case Layout::LAYOUT_FLOW:
- return FlowLayout::create();
- case Layout::LAYOUT_VERTICAL:
- return VerticalLayout::create();
- default:
- return AbsoluteLayout::create();
- }
- }
- void Container::updateScroll()
- {
- if (!_initializedWithScroll)
- {
- _initializedWithScroll = true;
- _layout->update(this, _scrollPosition);
- }
- Control::State state = getState();
- // Update time.
- if (!_lastFrameTime)
- {
- _lastFrameTime = Game::getAbsoluteTime();
- }
- double frameTime = Game::getAbsoluteTime();
- float elapsedTime = (float)(frameTime - _lastFrameTime);
- _lastFrameTime = frameTime;
- const Theme::Border& containerBorder = getBorder(state);
- const Theme::Padding& containerPadding = getPadding();
- // Calculate total width and height.
- _totalWidth = _totalHeight = 0.0f;
- std::vector<Control*> controls = getControls();
- for (size_t i = 0, controlsCount = controls.size(); i < controlsCount; i++)
- {
- Control* control = controls.at(i);
- const Rectangle& bounds = control->getBounds();
- const Theme::Margin& margin = control->getMargin();
- float newWidth = bounds.x + bounds.width + margin.right;
- if (newWidth > _totalWidth)
- {
- _totalWidth = newWidth;
- }
- float newHeight = bounds.y + bounds.height + margin.bottom;
- if (newHeight > _totalHeight)
- {
- _totalHeight = newHeight;
- }
- }
- float vWidth = getImageRegion("verticalScrollBar", state).width;
- float hHeight = getImageRegion("horizontalScrollBar", state).height;
- float clipWidth = _absoluteBounds.width - containerBorder.left - containerBorder.right - containerPadding.left - containerPadding.right - vWidth;
- float clipHeight = _absoluteBounds.height - containerBorder.top - containerBorder.bottom - containerPadding.top - containerPadding.bottom - hHeight;
- // Apply and dampen inertia.
- if (!_scrollingVelocity.isZero())
- {
- // Calculate the time passed since last update.
- float elapsedSecs = (float)elapsedTime * 0.001f;
- _scrollPosition.x += _scrollingVelocity.x * elapsedSecs;
- _scrollPosition.y += _scrollingVelocity.y * elapsedSecs;
- if (!_scrolling)
- {
- float dampening = 1.0f - _scrollingFriction * SCROLL_FRICTION_FACTOR * elapsedSecs;
- _scrollingVelocity.x *= dampening;
- _scrollingVelocity.y *= dampening;
- if (fabs(_scrollingVelocity.x) < 100.0f)
- _scrollingVelocity.x = 0.0f;
- if (fabs(_scrollingVelocity.y) < 100.0f)
- _scrollingVelocity.y = 0.0f;
- }
- }
- // Stop scrolling when the far edge is reached.
- if (-_scrollPosition.x > _totalWidth - clipWidth)
- {
- _scrollPosition.x = -(_totalWidth - clipWidth);
- _scrollingVelocity.x = 0;
- }
-
- if (-_scrollPosition.y > _totalHeight - clipHeight)
- {
- _scrollPosition.y = -(_totalHeight - clipHeight);
- _scrollingVelocity.y = 0;
- }
- if (_scrollPosition.x > 0)
- {
- _scrollPosition.x = 0;
- _scrollingVelocity.x = 0;
- }
- if (_scrollPosition.y > 0)
- {
- _scrollPosition.y = 0;
- _scrollingVelocity.y = 0;
- }
- float scrollWidth = 0;
- if (clipWidth < _totalWidth)
- scrollWidth = (clipWidth / _totalWidth) * clipWidth;
- float scrollHeight = 0;
- if (clipHeight < _totalHeight)
- scrollHeight = (clipHeight / _totalHeight) * clipHeight;
- _scrollBarBounds.set(((-_scrollPosition.x) / _totalWidth) * clipWidth,
- ((-_scrollPosition.y) / _totalHeight) * clipHeight,
- scrollWidth, scrollHeight);
- // If scroll velocity is 0 and scrollbars are not always visible, trigger fade-out animation.
- if (!_scrolling && _scrollingVelocity.isZero() && _scrollBarsAutoHide && _scrollBarOpacity == 1.0f)
- {
- float to = 0;
- _scrollBarOpacity = 0.99f;
- if (!_scrollBarOpacityClip)
- {
- Animation* animation = createAnimationFromTo("scrollbar-fade-out", ANIMATE_SCROLLBAR_OPACITY, &_scrollBarOpacity, &to, Curve::QUADRATIC_IN_OUT, 500L);
- _scrollBarOpacityClip = animation->getClip();
- }
- _scrollBarOpacityClip->play();
- }
- // Position controls within scroll area.
- _layout->update(this, _scrollPosition);
- }
- void Container::sortControls()
- {
- if (_layout->getType() == Layout::LAYOUT_ABSOLUTE)
- {
- std::sort(_controls.begin(), _controls.end(), &sortControlsByZOrder);
- }
- }
- bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
- {
- switch (evt)
- {
- case Touch::TOUCH_PRESS:
- if (_contactIndex == INVALID_CONTACT_INDEX)
- {
- _contactIndex = (int) contactIndex;
- _scrollingLastX = _scrollingFirstX = _scrollingVeryFirstX = x;
- _scrollingLastY = _scrollingFirstY = _scrollingVeryFirstY = y;
- _scrollingVelocity.set(0, 0);
- _scrolling = true;
- _scrollingStartTimeX = _scrollingStartTimeY = 0;
- if (_scrollBarOpacityClip && _scrollBarOpacityClip->isPlaying())
- {
- _scrollBarOpacityClip->stop();
- _scrollBarOpacityClip = NULL;
- }
- _scrollBarOpacity = 1.0f;
- _dirty = true;
- return false;
- }
- break;
- case Touch::TOUCH_MOVE:
- if (_scrolling && _contactIndex == (int) contactIndex)
- {
- double gameTime = Game::getAbsoluteTime();
- // Calculate the latest movement delta for the next update to use.
- int vx = x - _scrollingLastX;
- int vy = y - _scrollingLastY;
- if (_scrollingMouseVertically)
- {
- float yRatio = _totalHeight / _absoluteBounds.height;
- vy *= yRatio;
- _scrollingVelocity.set(0, -vy);
- _scrollPosition.y -= vy;
- }
- else if (_scrollingMouseHorizontally)
- {
- float xRatio = _totalWidth / _absoluteBounds.width;
- vx *= xRatio;
- _scrollingVelocity.set(-vx, 0);
- _scrollPosition.x -= vx;
- }
- else
- {
- _scrollingVelocity.set(vx, vy);
- _scrollPosition.x += vx;
- _scrollPosition.y += vy;
- }
- _scrollingLastX = x;
- _scrollingLastY = y;
- // If the user changes direction, reset the start time and position.
- bool goingRight = (vx > 0);
- if (goingRight != _scrollingRight)
- {
- _scrollingFirstX = x;
- _scrollingRight = goingRight;
- _scrollingStartTimeX = gameTime;
- }
- bool goingDown = (vy > 0);
- if (goingDown != _scrollingDown)
- {
- _scrollingFirstY = y;
- _scrollingDown = goingDown;
- _scrollingStartTimeY = gameTime;
- }
- if (!_scrollingStartTimeX)
- _scrollingStartTimeX = gameTime;
- if (!_scrollingStartTimeY)
- _scrollingStartTimeY = gameTime;
- _scrollingLastTime = gameTime;
- _dirty = true;
- return _consumeInputEvents;
- }
- break;
- case Touch::TOUCH_RELEASE:
- if (_contactIndex == (int) contactIndex)
- {
- _contactIndex = INVALID_CONTACT_INDEX;
- _scrolling = false;
- double gameTime = Game::getAbsoluteTime();
- float timeSinceLastMove = (float)(gameTime - _scrollingLastTime);
- if (timeSinceLastMove > SCROLL_INERTIA_DELAY)
- {
- _scrollingVelocity.set(0, 0);
- _scrollingMouseVertically = _scrollingMouseHorizontally = false;
- _dirty = true;
- return _consumeInputEvents;
- }
- int dx = _scrollingLastX - _scrollingFirstX;
- int dy = _scrollingLastY - _scrollingFirstY;
- float timeTakenX = (float)(gameTime - _scrollingStartTimeX);
- float elapsedSecsX = timeTakenX * 0.001f;
- float timeTakenY = (float)(gameTime - _scrollingStartTimeY);
- float elapsedSecsY = timeTakenY * 0.001f;
- float vx = dx;
- float vy = dy;
- if (elapsedSecsX > 0)
- vx = (float)dx / elapsedSecsX;
- if (elapsedSecsY > 0)
- vy = (float)dy / elapsedSecsY;
- if (_scrollingMouseVertically)
- {
- float yRatio = _totalHeight / _absoluteBounds.height;
- vy *= yRatio;
- _scrollingVelocity.set(0, -vy);
- }
- else if (_scrollingMouseHorizontally)
- {
- float xRatio = _totalWidth / _absoluteBounds.width;
- vx *= xRatio;
- _scrollingVelocity.set(-vx, 0);
- }
- else
- {
- _scrollingVelocity.set(vx, vy);
- }
- _scrollingMouseVertically = _scrollingMouseHorizontally = false;
- _dirty = true;
- return _consumeInputEvents;
- }
- break;
- }
- return false;
- }
- bool Container::mouseEventScroll(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
- {
- switch (evt)
- {
- case Mouse::MOUSE_PRESS_LEFT_BUTTON:
- {
- if (_scrollBarVertical)
- {
- float vWidth = _scrollBarVertical->getRegion().width;
- Rectangle vBounds(_viewportBounds.x + _viewportBounds.width,
- _scrollBarBounds.y,
- vWidth, _scrollBarBounds.height);
- if (x >= vBounds.x &&
- x <= vBounds.x + vBounds.width)
- {
- // Then we're within the horizontal bounds of the vertical scrollbar.
- // We want to either jump up or down, or drag the scrollbar itself.
- if (y < vBounds.y)
- {
- _scrollPosition.y += _totalHeight / 5.0f;
- }
- else if (y > vBounds.y + vBounds.height)
- {
- _scrollPosition.y -= _totalHeight / 5.0f;
- }
- else
- {
- _scrollingMouseVertically = true;
- }
- }
- }
- if (_scrollBarHorizontal)
- {
- float hHeight = _scrollBarHorizontal->getRegion().height;
- Rectangle hBounds(_scrollBarBounds.x,
- _viewportBounds.y + _viewportBounds.height,
- _scrollBarBounds.width, hHeight);
- if (y >= hBounds.y &&
- y <= hBounds.y + hBounds.height)
- {
- // We're within the vertical bounds of the horizontal scrollbar.
- if (x < hBounds.x)
- _scrollPosition.x += _totalWidth / 5.0f;
- else if (x > hBounds.x + hBounds.width)
- _scrollPosition.x -= _totalWidth / 5.0f;
- else
- _scrollingMouseHorizontally = true;
- }
- }
- return touchEventScroll(Touch::TOUCH_PRESS, x, y, 0);
- }
- case Mouse::MOUSE_MOVE:
- return touchEventScroll(Touch::TOUCH_MOVE, x, y, 0);
- case Mouse::MOUSE_RELEASE_LEFT_BUTTON:
- return touchEventScroll(Touch::TOUCH_RELEASE, x, y, 0);
- case Mouse::MOUSE_WHEEL:
- {
- if (_scrollingVelocity.isZero())
- {
- _lastFrameTime = Game::getAbsoluteTime();
- }
- _scrolling = _scrollingMouseVertically = _scrollingMouseHorizontally = false;
- _scrollingVelocity.y += _scrollWheelSpeed * wheelDelta;
- if (_scrollBarOpacityClip && _scrollBarOpacityClip->isPlaying())
- {
- _scrollBarOpacityClip->stop();
- _scrollBarOpacityClip = NULL;
- }
- _scrollBarOpacity = 1.0f;
- _dirty = true;
- return _consumeInputEvents;
- }
- }
- return false;
- }
- bool Container::inContact()
- {
- for (int i = 0; i < MAX_CONTACT_INDICES; ++i)
- {
- if (_contactIndices[i])
- return true;
- }
- return false;
- }
- Container::Scroll Container::getScroll(const char* scroll)
- {
- if (!scroll)
- return Container::SCROLL_NONE;
- if (strcmp(scroll, "SCROLL_NONE") == 0)
- {
- return Container::SCROLL_NONE;
- }
- else if (strcmp(scroll, "SCROLL_HORIZONTAL") == 0)
- {
- return Container::SCROLL_HORIZONTAL;
- }
- else if (strcmp(scroll, "SCROLL_VERTICAL") == 0)
- {
- return Container::SCROLL_VERTICAL;
- }
- else if (strcmp(scroll, "SCROLL_BOTH") == 0)
- {
- return Container::SCROLL_BOTH;
- }
- else
- {
- GP_ERROR("Failed to get corresponding scroll state for unsupported value '%s'.", scroll);
- }
- return Container::SCROLL_NONE;
- }
- float Container::getScrollingFriction() const
- {
- return _scrollingFriction;
- }
- void Container::setScrollingFriction(float friction)
- {
- _scrollingFriction = friction;
- }
- float Container::getScrollWheelSpeed() const
- {
- return _scrollWheelSpeed;
- }
- void Container::setScrollWheelSpeed(float speed)
- {
- _scrollWheelSpeed = speed;
- }
- static bool sortControlsByZOrder(Control* c1, Control* c2)
- {
- if (c1->getZIndex() < c2->getZIndex())
- return true;
- return false;
- }
- unsigned int Container::getAnimationPropertyComponentCount(int propertyId) const
- {
- switch(propertyId)
- {
- case ANIMATE_SCROLLBAR_OPACITY:
- return 1;
- default:
- return Control::getAnimationPropertyComponentCount(propertyId);
- }
- }
- void Container::getAnimationPropertyValue(int propertyId, AnimationValue* value)
- {
- GP_ASSERT(value);
- switch(propertyId)
- {
- case ANIMATE_SCROLLBAR_OPACITY:
- value->setFloat(0, _scrollBarOpacity);
- break;
- default:
- Control::getAnimationPropertyValue(propertyId, value);
- break;
- }
- }
- void Container::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
- {
- GP_ASSERT(value);
- switch(propertyId)
- {
- case ANIMATE_SCROLLBAR_OPACITY:
- _scrollBarOpacity = Curve::lerp(blendWeight, _opacity, value->getFloat(0));
- _dirty = true;
- break;
- default:
- Control::setAnimationPropertyValue(propertyId, value, blendWeight);
- break;
- }
- }
- }
|