Kaynağa Gözat

Merge pull request #430 from blackberry-gaming/next-ablake

Next ablake
Sean Paul Taylor 13 yıl önce
ebeveyn
işleme
28baae5da1

+ 0 - 2
gameplay/gameplay.vcxproj

@@ -90,7 +90,6 @@
     <ClCompile Include="src\RenderTarget.cpp" />
     <ClCompile Include="src\Scene.cpp" />
     <ClCompile Include="src\SceneLoader.cpp" />
-    <ClCompile Include="src\ScrollLayout.cpp" />
     <ClCompile Include="src\Slider.cpp" />
     <ClCompile Include="src\SpriteBatch.cpp" />
     <ClCompile Include="src\Technique.cpp" />
@@ -182,7 +181,6 @@
     <ClInclude Include="src\Scene.h" />
     <ClInclude Include="src\SceneLoader.h" />
     <ClInclude Include="src\ScreenDisplayer.h" />
-    <ClInclude Include="src\ScrollLayout.h" />
     <ClInclude Include="src\Slider.h" />
     <ClInclude Include="src\SpriteBatch.h" />
     <ClInclude Include="src\Technique.h" />

+ 0 - 6
gameplay/gameplay.vcxproj.filters

@@ -279,9 +279,6 @@
     <ClCompile Include="src\FlowLayout.cpp">
       <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="src\ScrollLayout.cpp">
-      <Filter>src</Filter>
-    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\Animation.h">
@@ -557,9 +554,6 @@
     <ClInclude Include="src\FlowLayout.h">
       <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="src\ScrollLayout.h">
-      <Filter>src</Filter>
-    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="res\shaders\bumped-specular.vsh">

+ 1 - 1
gameplay/src/CheckBox.cpp

@@ -5,7 +5,7 @@
 namespace gameplay
 {
 
-CheckBox::CheckBox() : _checked(false)
+CheckBox::CheckBox() : _checked(false), _image(NULL)
 {
 }
 

+ 1 - 1
gameplay/src/CheckBox.h

@@ -120,7 +120,7 @@ protected:
      *
      * @param clip The clipping rectangle of this control's parent container.
      */
-    void update(const Rectangle& clip, const Vector2& offset = Vector2::zero());
+    void update(const Rectangle& clip, const Vector2& offset);
 
     /**
      * Draw the checkbox icon associated with this control.

+ 357 - 9
gameplay/src/Container.cpp

@@ -4,7 +4,6 @@
 #include "AbsoluteLayout.h"
 #include "FlowLayout.h"
 #include "VerticalLayout.h"
-#include "ScrollLayout.h"
 #include "Label.h"
 #include "Button.h"
 #include "CheckBox.h"
@@ -16,7 +15,26 @@
 namespace gameplay
 {
 
-Container::Container() : _layout(NULL)
+    /**
+     * If the user stops scrolling for this amount of time (in millis) before
+     * touch/click release, don't apply inertia.
+     */
+    static const long STOP_TIME = 100L;
+
+    /**
+     * Factor to multiply friction by before applying to velocity.
+     */
+    static const float FRICTION_FACTOR = 5.0f;
+
+Container::Container()
+    : _layout(NULL), _scrollBarTopCap(NULL), _scrollBarVertical(NULL), _scrollBarBottomCap(NULL),
+      _scrollBarLeftCap(NULL), _scrollBarHorizontal(NULL), _scrollBarRightCap(NULL),
+      _scrollState(SCROLL_NONE), _scrollBarBounds(Rectangle::empty()), _scrollPosition(Vector2::zero()),
+      _scrolling(false), _firstX(0), _firstY(0),
+      _lastX(0), _lastY(0),
+      _startTimeX(0), _startTimeY(0), _lastTime(0),
+      _velocity(Vector2::zero()), _friction(1.0f),
+      _goingRight(false), _goingDown(false)
 {
 }
 
@@ -49,9 +67,6 @@ Container* Container::create(Layout::Type type)
     case Layout::LAYOUT_VERTICAL:
         layout = VerticalLayout::create();
         break;
-    case Layout::LAYOUT_SCROLL:
-        layout = ScrollLayout::create();
-        break;
     }
 
     Container* container = new Container();
@@ -67,6 +82,7 @@ Container* Container::create(Theme::Style* style, Properties* properties, Theme*
     const char* layoutString = properties->getString("layout");
     Container* container = Container::create(getLayoutType(layoutString));
     container->initialize(style, properties);
+    container->_scrollState = getScrollState(properties->getString("scroll"));
     container->addControls(theme, properties);
 
     return container;
@@ -225,6 +241,20 @@ const std::vector<Control*>& Container::getControls() const
     return _controls;
 }
 
+void Container::setScrollState(ScrollState scrollState)
+{
+    if (scrollState != _scrollState)
+    {
+        _scrollState = scrollState;
+        _dirty = true;
+    }
+}
+
+Container::ScrollState Container::getScrollState() const
+{
+    return _scrollState;
+}
+
 Animation* Container::getAnimation(const char* id) const
 {
     std::vector<Control*>::const_iterator itr = _controls.begin();
@@ -255,8 +285,30 @@ void Container::update(const Rectangle& clip, const Vector2& offset)
     // Update this container's viewport.
     Control::update(clip, offset);
 
+    // Get scrollbar images and diminish clipping bounds to make room for scrollbars.
+    if ((_scrollState & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL)
+    {
+        _scrollBarLeftCap = getImage("scrollBarLeftCap", _state);
+        _scrollBarHorizontal = getImage("horizontalScrollBar", _state);
+        _scrollBarRightCap = getImage("scrollBarRightCap", _state);
+
+        _viewportClipBounds.height -= _scrollBarHorizontal->getRegion().height;
+    }
+
+    if ((_scrollState & SCROLL_VERTICAL) == SCROLL_VERTICAL)
+    {
+        _scrollBarTopCap = getImage("scrollBarTopCap", _state);
+        _scrollBarVertical = getImage("verticalScrollBar", _state);
+        _scrollBarBottomCap = getImage("scrollBarBottomCap", _state);
+        
+        _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
+    }
+
     GP_ASSERT(_layout);
     _layout->update(this);
+
+    if (_scrollState != SCROLL_NONE)
+        this->updateScroll(this);
 }
 
 void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, float targetHeight)
@@ -289,7 +341,80 @@ void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needs
         }
     }
 
-    _dirty = false;
+    if (_scrollState != SCROLL_NONE)
+    {
+        // Draw scroll bars.
+        Rectangle clipRegion(_viewportClipBounds);
+
+        if (_scrollBarBounds.height > 0 && (_scrolling || _velocity.y) && _scrollBarTopCap && _scrollBarVertical && _scrollBarBottomCap)
+        {
+            const Rectangle& topRegion = _scrollBarTopCap->getRegion();
+            const Theme::UVs& topUVs = _scrollBarTopCap->getUVs();
+            Vector4 topColor = _scrollBarTopCap->getColor();
+
+            const Rectangle& verticalRegion = _scrollBarVertical->getRegion();
+            const Theme::UVs& verticalUVs = _scrollBarVertical->getUVs();
+            Vector4 verticalColor = _scrollBarVertical->getColor();
+
+            const Rectangle& bottomRegion = _scrollBarBottomCap->getRegion();
+            const Theme::UVs& bottomUVs = _scrollBarBottomCap->getUVs();
+            Vector4 bottomColor = _scrollBarBottomCap->getColor();
+
+            clipRegion.width += verticalRegion.width;
+
+            Rectangle bounds(_viewportBounds.x + _viewportBounds.width - verticalRegion.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 && (_scrolling || _velocity.x) && _scrollBarLeftCap && _scrollBarHorizontal && _scrollBarRightCap)
+        {
+            const Rectangle& leftRegion = _scrollBarLeftCap->getRegion();
+            const Theme::UVs& leftUVs = _scrollBarLeftCap->getUVs();
+            Vector4 leftColor = _scrollBarLeftCap->getColor();
+
+            const Rectangle& horizontalRegion = _scrollBarHorizontal->getRegion();
+            const Theme::UVs& horizontalUVs = _scrollBarHorizontal->getUVs();
+            Vector4 horizontalColor = _scrollBarHorizontal->getColor();
+
+            const Rectangle& rightRegion = _scrollBarRightCap->getRegion();
+            const Theme::UVs& rightUVs = _scrollBarRightCap->getUVs();
+            Vector4 rightColor = _scrollBarRightCap->getColor();
+
+            clipRegion.height += horizontalRegion.height;
+        
+            Rectangle bounds(_viewportBounds.x + _scrollBarBounds.x,
+                             _viewportBounds.y + _viewportBounds.height - horizontalRegion.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);
+        }
+
+        if (_velocity.isZero())
+        {
+            _dirty = false;
+        }
+    }
+    else
+    {
+        _dirty = false;
+    }
 }
 
 bool Container::isDirty()
@@ -328,9 +453,9 @@ bool Container::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int con
     float yPos = border.top + padding.top;
 
     Vector2* offset = NULL;
-    if (_layout->getType() == Layout::LAYOUT_SCROLL)
+    if (_scrollState != SCROLL_NONE)
     {
-        offset = &((ScrollLayout*)_layout)->_scrollPosition;
+        offset = &_scrollPosition;
     }
 
     std::vector<Control*>::const_iterator it;
@@ -382,7 +507,7 @@ bool Container::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int con
     if (!eventConsumed)
     {
         // Pass the event on to the layout.
-        if (_layout->touchEvent(evt, x - xPos, y - yPos, contactIndex))
+        if (touchEventScroll(evt, x - xPos, y - yPos, contactIndex))
         {
             _dirty = true;
         }
@@ -447,4 +572,227 @@ Layout::Type Container::getLayoutType(const char* layoutString)
     }
 }
 
+void Container::updateScroll(const Container* container)
+{
+    GP_ASSERT(container);
+
+    // Update Time.
+    static long lastFrameTime = Game::getGameTime();
+    long frameTime = Game::getGameTime();
+    long elapsedTime = (frameTime - lastFrameTime);
+    lastFrameTime = frameTime;
+
+    const Rectangle& containerBounds = container->getBounds();
+    const Theme::Border& containerBorder = container->getBorder(container->getState());
+    const Theme::Padding& containerPadding = container->getPadding();
+
+    // Calculate total width and height.
+    float totalWidth = 0;
+    float totalHeight = 0;
+    std::vector<Control*> controls = container->getControls();
+    unsigned int controlsCount = controls.size();
+    for (unsigned int i = 0; 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;
+        if (newWidth > totalWidth)
+        {
+            totalWidth = newWidth;
+        }
+
+        float newHeight = bounds.y + bounds.height;
+        if (newHeight > totalHeight)
+        {
+            totalHeight = newHeight;
+        }
+    }
+
+    float vWidth = container->getImageRegion("verticalScrollBar", container->getState()).width;
+    float hHeight = container->getImageRegion("horizontalScrollBar", container->getState()).height;
+    float clipWidth = containerBounds.width - containerBorder.left - containerBorder.right - containerPadding.left - containerPadding.right - vWidth;
+    float clipHeight = containerBounds.height - containerBorder.top - containerBorder.bottom - containerPadding.top - containerPadding.bottom - hHeight;
+
+    // Apply and dampen inertia.
+    if (!_scrolling && !_velocity.isZero())
+    {
+        // Calculate the time passed since last update.
+        float elapsedSecs = (float)elapsedTime * 0.001f;
+
+        _scrollPosition.x += _velocity.x * elapsedSecs;
+        _scrollPosition.y += _velocity.y * elapsedSecs;
+
+        float dampening = 1.0f - _friction * FRICTION_FACTOR * elapsedSecs;
+        _velocity.x *= dampening;
+        _velocity.y *= dampening;
+
+        if (abs(_velocity.x) < 100.0f)
+            _velocity.x = 0.0f;
+        if (abs(_velocity.y) < 100.0f)
+            _velocity.y = 0.0f;
+    }
+
+    // Stop scrolling when the far edge is reached.
+    if (-_scrollPosition.x > totalWidth - clipWidth)
+    {
+        _scrollPosition.x = -(totalWidth - clipWidth);
+        _velocity.x = 0;
+    }
+    
+    if (-_scrollPosition.y > totalHeight - clipHeight)
+    {
+        _scrollPosition.y = -(totalHeight - clipHeight);
+        _velocity.y = 0;
+    }
+
+    if (_scrollPosition.x > 0)
+    {
+        _scrollPosition.x = 0;
+        _velocity.x = 0;
+    }
+
+    if (_scrollPosition.y > 0)
+    {
+        _scrollPosition.y = 0;
+        _velocity.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);
+
+    // Position controls within scroll area.
+    for (unsigned int i = 0; i < controlsCount; i++)
+    {
+        Control* control = controls.at(i);
+        control->update(container->getClip(), _scrollPosition);
+    }
+}
+
+bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+{
+    switch(evt)
+    {
+    case Touch::TOUCH_PRESS:
+        _lastX = _firstX = x;
+        _lastY = _firstY = y;
+        _velocity.set(0, 0);
+        _scrolling = true;
+        _startTimeX = _startTimeY = 0;
+        break;
+
+    case Touch::TOUCH_MOVE:
+        if (_scrolling)
+        {
+            // Calculate the latest movement delta for the next update to use.
+            int vx = x - _lastX;
+            int vy = y - _lastY;
+            _velocity.set(vx, vy);
+            _scrollPosition.x += vx;
+            _scrollPosition.y += vy;
+            _lastX = x;
+            _lastY = y;
+
+            // If the user changes direction, reset the start time and position.
+            bool goingRight = (vx > 0);
+            if (goingRight != _goingRight)
+            {
+                _firstX = x;
+                _goingRight = goingRight;
+                _startTimeX = Game::getAbsoluteTime();
+            }
+
+            bool goingDown = (vy > 0);
+            if (goingDown != _goingDown)
+            {
+                _firstY = y;
+                _goingDown = goingDown;
+                _startTimeY = Game::getAbsoluteTime();
+            }
+
+            if (!_startTimeX)
+                _startTimeX = Game::getAbsoluteTime();
+
+            if (!_startTimeY)
+                _startTimeY = Game::getAbsoluteTime();
+
+            _lastTime = Game::getAbsoluteTime();
+
+            return true;
+        }
+        break;
+
+    case Touch::TOUCH_RELEASE:
+        long timeSinceLastMove = Game::getAbsoluteTime() - _lastTime;
+        if (timeSinceLastMove > STOP_TIME)
+        {
+            _velocity.set(0, 0);
+            _scrolling = false;
+            break;
+        }
+
+        int dx = _lastX - _firstX;
+        int dy = _lastY - _firstY;
+
+        long timeTakenX = Game::getAbsoluteTime() - _startTimeX;
+        float elapsedSecsX = (float)timeTakenX * 0.001f;
+        long timeTakenY = Game::getAbsoluteTime() - _startTimeY;
+        float elapsedSecsY = (float)timeTakenY * 0.001f;
+
+        float vx = dx;
+        float vy = dy;
+        if (elapsedSecsX > 0)
+            vx = (float)dx / elapsedSecsX;
+        if (elapsedSecsY > 0)
+            vy = (float)dy / elapsedSecsY;
+
+        _velocity.set(vx, vy);
+
+        _scrolling = false;
+        break;
+    }
+
+    return false;
+}
+
+Container::ScrollState Container::getScrollState(const char* scrollState)
+{
+    if (!scrollState)
+        return Container::SCROLL_NONE;
+
+    if (strcmp(scrollState, "SCROLL_NONE") == 0)
+    {
+        return Container::SCROLL_NONE;
+    }
+    else if (strcmp(scrollState, "SCROLL_HORIZONTAL") == 0)
+    {
+        return Container::SCROLL_HORIZONTAL;
+    }
+    else if (strcmp(scrollState, "SCROLL_VERTICAL") == 0)
+    {
+        return Container::SCROLL_VERTICAL;
+    }
+    else if (strcmp(scrollState, "SCROLL_BOTH") == 0)
+    {
+        return Container::SCROLL_BOTH;
+    }
+    else
+    {
+        GP_ERROR("Failed to get corresponding scroll state for unsupported value '%s'.", scrollState);
+    }
+
+    return Container::SCROLL_NONE;
+}
+
 }

+ 83 - 25
gameplay/src/Container.h

@@ -45,6 +45,14 @@ class Container : public Control
 {
 public:
 
+    enum ScrollState
+    {
+        SCROLL_NONE        = 0,
+        SCROLL_HORIZONTAL  = 0x01,
+        SCROLL_VERTICAL    = 0x02,
+        SCROLL_BOTH = SCROLL_HORIZONTAL | SCROLL_VERTICAL
+    };
+
     /**
      * Get this container's layout.
      *
@@ -114,6 +122,10 @@ public:
      */
     const std::vector<Control*>& getControls() const;
 
+    void setScrollState(ScrollState scrollState);
+
+    ScrollState getScrollState() const;
+
     /**
      * Gets the first animation in the control with the specified ID.
      *
@@ -160,29 +172,6 @@ protected:
      */
     virtual void update(const Rectangle& clip, const Vector2& offset);
 
-    /**
-     * Draws the themed border and background of this container and all its controls.
-     *
-     * @param spriteBatch The sprite batch containing this container's border images.
-     * @param clip The clipping rectangle of this container's parent container.
-     */
-    //void drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip, const Vector2& offset = Vector2::zero());
-
-    /**
-     * Draws the icons of all controls within this container.
-     *
-     * @param spriteBatch The sprite batch containing this control's icons.
-     * @param clip The clipping rectangle of this container's parent container.
-     */
-    //virtual void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
-
-    /**
-     * Draws the text of all controls within this container.
-     *
-     * @param clip The clipping rectangle of this container's parent container.
-     */
-    //virtual void drawText(const Rectangle& clip);
-
     /**
      * Touch callback on touch events.  Controls return true if they consume the touch event.
      *
@@ -231,6 +220,17 @@ protected:
      */
     void addControls(Theme* theme, Properties* properties);
 
+    virtual void draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, float targetHeight);
+
+    /**
+     * Update scroll position and velocity.
+     */
+    void updateScroll(const Container* container);
+
+    bool touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
+
+    static ScrollState getScrollState(const char* scrollState);
+
     /**
      * The container's layout.
      */
@@ -241,11 +241,69 @@ protected:
      */
     std::vector<Control*> _controls;
 
+    /**
+     * Scrollbar top cap image.
+     */
+    Theme::ThemeImage* _scrollBarTopCap;
+
+    /**
+     * Vertical scrollbar image.
+     */
+    Theme::ThemeImage* _scrollBarVertical;
+
+    /**
+     * Scrollbar bottom cap image.
+     */
+    Theme::ThemeImage* _scrollBarBottomCap;
+
+    Theme::ThemeImage* _scrollBarLeftCap;
+    Theme::ThemeImage* _scrollBarHorizontal;
+    Theme::ThemeImage* _scrollBarRightCap;
+
+    // Flag representing whether scrolling is enabled, and in which directions.
+    ScrollState _scrollState;
+
+    // Data required when scrolling is enabled.
+
+    /**
+     * x, width: Horizontal scrollbar position and size.
+     * y, height: Vertical scrollbar position and size.
+     */
+    Rectangle _scrollBarBounds;
+
+    // How far this layout has been scrolled in each direction.
+    Vector2 _scrollPosition;
+
+    // Whether the user is currently touching / holding the mouse down
+    // within this layout's container.
+    bool _scrolling;
+
+    // First touch point.
+    int _firstX;
+    int _firstY;
+
+    // Latest touch point.
+    int _lastX;
+    int _lastY;
+
+    // Time recorded on touch down.
+    long _startTimeX;
+    long _startTimeY;
+    long _lastTime;
+
+    // Speed to continue scrolling at after touch release.
+    Vector2 _velocity;
+
+    // Friction dampens velocity.
+    float _friction;
+
+    // Detect a change in scroll direction.
+    bool _goingRight;
+    bool _goingDown;
+
 private:
 
     Container(const Container& copy);
-
-    virtual void draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, float targetHeight);
 };
 
 }

+ 2 - 2
gameplay/src/Control.h

@@ -757,6 +757,8 @@ protected:
      */
     virtual void drawText(const Rectangle& clip);
 
+    virtual void draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, float targetHeight);
+
     /**
      * Initialize properties common to STATE_ALL Controls.
      */
@@ -919,8 +921,6 @@ private:
      * @param clip The clipping rectangle of this control's parent container.
      */
     virtual void drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip);
-
-    virtual void draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, float targetHeight);
     
     bool _styleOverridden;
     Theme::Skin* _skin;

+ 25 - 22
gameplay/src/Form.cpp

@@ -2,7 +2,6 @@
 #include "Form.h"
 #include "AbsoluteLayout.h"
 #include "FlowLayout.h"
-#include "ScrollLayout.h"
 #include "VerticalLayout.h"
 #include "Game.h"
 #include "Theme.h"
@@ -76,9 +75,6 @@ Form* Form::create(const char* url)
     case Layout::LAYOUT_VERTICAL:
         layout = VerticalLayout::create();
         break;
-    case Layout::LAYOUT_SCROLL:
-        layout = ScrollLayout::create();
-        break;
     default:
         GP_ERROR("Unsupported layout type '%d'.", getLayoutType(layoutString));
     }
@@ -112,6 +108,8 @@ Form* Form::create(const char* url)
         form->_bounds.x = Game::getInstance()->getWidth() * 0.5f - form->_bounds.width * 0.5f;
     }
 
+    form->_scrollState = getScrollState(formProperties->getString("scroll"));
+
     // Add all the controls to the form.
     form->addControls(theme, formProperties);
 
@@ -385,8 +383,30 @@ void Form::update()
         _skin = getSkin(_state);
         _opacity = getOpacity(_state);
 
+        // Get scrollbar images and diminish clipping bounds to make room for scrollbars.
+        if ((_scrollState & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL)
+        {
+            _scrollBarLeftCap = getImage("scrollBarLeftCap", _state);
+            _scrollBarHorizontal = getImage("horizontalScrollBar", _state);
+            _scrollBarRightCap = getImage("scrollBarRightCap", _state);
+
+            _viewportClipBounds.height -= _scrollBarHorizontal->getRegion().height;
+        }
+
+        if ((_scrollState & SCROLL_VERTICAL) == SCROLL_VERTICAL)
+        {
+            _scrollBarTopCap = getImage("scrollBarTopCap", _state);
+            _scrollBarVertical = getImage("verticalScrollBar", _state);
+            _scrollBarBottomCap = getImage("scrollBarBottomCap", _state);
+        
+            _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
+        }
+
         GP_ASSERT(_layout);
         _layout->update(this);
+
+        if (_scrollState != SCROLL_NONE)
+            this->updateScroll(this);
     }
 }
 
@@ -466,22 +486,7 @@ void Form::draw(SpriteBatch* spriteBatch, const Rectangle& clip)
     // Batch for all themed border and background sprites.
     spriteBatch->begin();
 
-    // Draw the form's border and background.
-    // We don't pass the form's position to itself or it will be applied twice!
-    Control::drawBorder(spriteBatch, Rectangle(0, 0, _bounds.width, _bounds.height));
-
-    Rectangle boundsUnion = Rectangle::empty();
-    for (it = _controls.begin(); it < _controls.end(); it++)
-    {
-        Control* control = *it;
-        GP_ASSERT(control);
-
-        if (_skin || control->isDirty() || control->_clearBounds.intersects(boundsUnion))
-        {
-            control->draw(spriteBatch, clip, _skin == NULL, _bounds.height);
-            Rectangle::combine(control->_clearBounds, boundsUnion, &boundsUnion);
-        }
-    }
+    Container::draw(spriteBatch, Rectangle(0, 0, _bounds.width, _bounds.height), _skin == NULL, _bounds.height);
 
     // Done all batching.
     spriteBatch->end();
@@ -494,8 +499,6 @@ void Form::draw(SpriteBatch* spriteBatch, const Rectangle& clip)
             font->end();
         }
     }
-
-    _dirty = false;
 }
 
 void Form::initializeQuad(Mesh* mesh)

+ 2 - 2
gameplay/src/Layout.cpp

@@ -25,12 +25,12 @@ void Layout::align(Control* control, const Container* container)
 
         if (control->_autoWidth)
         {
-            controlBounds.width = clipWidth;
+            controlBounds.width = clipWidth - controlMargin.left - controlMargin.right;
         }
 
         if (control->_autoHeight)
         {
-            controlBounds.height = clipHeight;
+            controlBounds.height = clipHeight - controlMargin.top - controlMargin.bottom;
         }
 
         // Vertical alignment

+ 1 - 1
gameplay/src/ParticleEmitter.cpp

@@ -795,7 +795,7 @@ void ParticleEmitter::update(long elapsedTime)
     }
 
     // Calculate the time passed since last update.
-    float elapsedSecs = (float)elapsedTime / 1000.0f;
+    float elapsedSecs = (float)elapsedTime * 0.001f;
 
     if (_started && _emissionRate)
     {

+ 0 - 140
gameplay/src/ScrollLayout.cpp

@@ -1,140 +0,0 @@
-#include "Base.h"
-#include "Control.h"
-#include "ScrollLayout.h"
-#include "Container.h"
-
-namespace gameplay
-{
-
-ScrollLayout::ScrollLayout() 
-    : _scrollPosition(Vector2::zero()), _lastX(0), _lastY(0), _scrolling(false),
-      _positionVertically(false), _positionHorizontally(false)
-{
-}
-
-ScrollLayout::ScrollLayout(const ScrollLayout& copy)
-{
-}
-
-ScrollLayout::~ScrollLayout()
-{
-}
-
-ScrollLayout* ScrollLayout::create()
-{
-    return new ScrollLayout();
-}
-
-Layout::Type ScrollLayout::getType()
-{
-    return Layout::LAYOUT_SCROLL;
-}
-
-void ScrollLayout::update(const Container* container)
-{
-    // Position controls if automatic positioning is enabled.
-    if (_positionVertically && _positionHorizontally)
-    {
-        // Treat as scrollable flow layout.
-    }
-    else if (_positionVertically)
-    {
-        // Scrollable vertical layout.
-    }
-    else if (_positionHorizontally)
-    {
-        // Scrollable horizontal layout.
-    }
-
-    // Calculate total width and height.
-    float totalWidth = 0;
-    float totalHeight = 0;
-    std::vector<Control*> controls = container->getControls();
-    unsigned int controlsCount = controls.size();
-    for (unsigned int i = 0; 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.left + margin.right;
-        if (newWidth > totalWidth)
-        {
-            totalWidth = newWidth;
-        }
-
-        float newHeight = bounds.y + bounds.height + margin.top + margin.bottom;
-        if (newHeight > totalHeight)
-        {
-            totalHeight = newHeight;
-        }
-    }
-
-    const Rectangle& containerBounds = container->getBounds();
-    const Theme::Border& containerBorder = container->getBorder(container->getState());
-    const Theme::Padding& containerPadding = container->getPadding();
-
-    float clipWidth = containerBounds.width - containerBorder.left - containerBorder.right - containerPadding.left - containerPadding.right;
-    float clipHeight = containerBounds.height - containerBorder.top - containerBorder.bottom - containerPadding.top - containerPadding.bottom;
-
-    // Stop scrolling when the far edge is reached.
-    if (-_scrollPosition.x > totalWidth - clipWidth)
-    {
-        _scrollPosition.x = -(totalWidth - clipWidth);
-    }
-    
-    if (-_scrollPosition.y > totalHeight - clipHeight)
-    {
-        _scrollPosition.y = -(totalHeight - clipHeight);
-    }
-
-    if (_scrollPosition.x > 0)
-    {
-        _scrollPosition.x = 0;
-    }
-
-    if (_scrollPosition.y > 0)
-    {
-        _scrollPosition.y = 0;
-    }
-
-    // Position controls within scroll area.
-    for (unsigned int i = 0; i < controlsCount; i++)
-    {
-        Control* control = controls.at(i);
-        control->update(container->getClip(), _scrollPosition);
-    }
-}
-
-bool ScrollLayout::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
-{
-    switch(evt)
-    {
-    case Touch::TOUCH_PRESS:
-        _lastX = x;
-        _lastY = y;
-        _scrolling = true;
-        break;
-
-    case Touch::TOUCH_MOVE:
-        if (_scrolling)
-        {
-            // Calculate the latest movement delta for the next update to use.
-            _scrollPosition.x += x - _lastX;
-            _scrollPosition.y += y - _lastY;
-            _lastX = x;
-            _lastY = y;
-            return true;
-        }
-        break;
-
-    case Touch::TOUCH_RELEASE:
-        _scrolling = false;
-        break;
-    }
-
-    return false;
-}
-
-}

+ 0 - 72
gameplay/src/ScrollLayout.h

@@ -1,72 +0,0 @@
-#ifndef SCROLLLAYOUT_H_
-#define SCROLLLAYOUT_H_
-
-#include "Layout.h"
-
-namespace gameplay
-{
-
-class ScrollLayout : public Layout
-{
-    friend class Form;
-    friend class Container;
-
-public:
-
-    /**
-     * Get the type of this Layout.
-     *
-     * @return Layout::LAYOUT_SCROLL
-     */
-    Layout::Type getType();
-
-protected:
-
-    /**
-     * Create a ScrollLayout.
-     *
-     * @return A ScrollLayout object.
-     */
-    static ScrollLayout* create();
-
-    /**
-     * Update the controls contained by the specified container.
-     *
-     * @param container The container to update.
-     */
-    void update(const Container* container);
-
-    bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
-
-private:
-
-    /**
-     * Constructor.
-     */
-    ScrollLayout();
-
-    /**
-     * Constructor.
-     */
-    ScrollLayout(const ScrollLayout& copy);
-
-    /**
-     * Destructor.
-     */
-    virtual ~ScrollLayout();
-
-    Vector2 _scrollPosition;
-
-    // Previous touch point.
-    int _lastX;
-    int _lastY;
-
-    bool _scrolling;
-
-    bool _positionVertically;
-    bool _positionHorizontally;
-};
-
-}
-
-#endif

+ 4 - 4
gameplay/src/Slider.cpp

@@ -172,10 +172,10 @@ void Slider::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
     const Rectangle& markerRegion = _markerImage->getRegion();
     const Rectangle& trackRegion = _trackImage->getRegion();
 
-    const Theme::UVs minCap = _minImage->getUVs();
-    const Theme::UVs maxCap = _maxImage->getUVs();
-    const Theme::UVs marker = _markerImage->getUVs();
-    const Theme::UVs track = _trackImage->getUVs();
+    const Theme::UVs& minCap = _minImage->getUVs();
+    const Theme::UVs& maxCap = _maxImage->getUVs();
+    const Theme::UVs& marker = _markerImage->getUVs();
+    const Theme::UVs& track = _trackImage->getUVs();
 
     Vector4 minCapColor = _minImage->getColor();
     Vector4 maxCapColor = _maxImage->getColor();

+ 1 - 1
gameplay/src/Slider.h

@@ -160,7 +160,7 @@ protected:
 
     /**
      * The minimum value for the Slider.
-     */
+     */
     float _min;
     
     /**

+ 23 - 43
gameplay/src/SpriteBatch.cpp

@@ -83,9 +83,7 @@ SpriteBatch* SpriteBatch::create(const char* texturePath, Effect* effect, unsign
 
 SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int initialCapacity)
 {
-    GP_ASSERT(texture);
-    GP_ASSERT(texture->getWidth());
-    GP_ASSERT(texture->getHeight());
+    GP_ASSERT(texture != NULL);
 
     bool customEffect = (effect != NULL);
     if (!customEffect)
@@ -96,7 +94,7 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
             __spriteEffect = Effect::createFromSource(SPRITE_VSH, SPRITE_FSH);
             if (__spriteEffect == NULL)
             {
-                GP_ERROR("Unable to create default sprite effect.");
+                GP_ERROR("Unable to load sprite effect.");
                 return NULL;
             }
 
@@ -122,33 +120,25 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
     }
     if (!samplerUniform)
     {
-        GP_ERROR("Failed to create sprite batch; required uniform of type GL_SAMPLER_2D not found in sprite effect.");
+        GP_ERROR("No uniform of type GL_SAMPLER_2D found in sprite effect.");
         SAFE_RELEASE(effect);
         return NULL;
     }
 
-    // Wrap the effect in a material.
+    // Wrap the effect in a material
     Material* material = Material::create(effect); // +ref effect
-    if (!material)
-    {
-        GP_ERROR("Failed to create material for sprite batch.");
-        SAFE_RELEASE(effect);
-        return NULL;
-    }
 
-    // Set initial material state.
-    GP_ASSERT(material->getStateBlock());
+    // Set initial material state
     material->getStateBlock()->setBlend(true);
     material->getStateBlock()->setBlendSrc(RenderState::BLEND_SRC_ALPHA);
     material->getStateBlock()->setBlendDst(RenderState::BLEND_ONE_MINUS_SRC_ALPHA);
 
-    // Bind the texture to the material as a sampler.
+    // Bind the texture to the material as a sampler
     Texture::Sampler* sampler = Texture::Sampler::create(texture); // +ref texture
-    GP_ASSERT(material->getParameter(samplerUniform->getName()));
     material->getParameter(samplerUniform->getName())->setValue(sampler);
     SAFE_RELEASE(sampler);
 
-    // Define the vertex format for the batch.
+    // Define the vertex format for the batch
     VertexFormat::Element vertexElements[] =
     {
         VertexFormat::Element(VertexFormat::POSITION, 3),
@@ -157,11 +147,11 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
     };
     VertexFormat vertexFormat(vertexElements, 3);
 
-    // Create the mesh batch.
+    // Create the mesh batch
     MeshBatch* meshBatch = MeshBatch::create(vertexFormat, Mesh::TRIANGLE_STRIP, material, true, initialCapacity > 0 ? initialCapacity : SPRITE_BATCH_DEFAULT_SIZE);
     material->release(); // don't call SAFE_RELEASE since material is used below
 
-    // Create the batch.
+    // Create the batch
     SpriteBatch* batch = new SpriteBatch();
     batch->_customEffect = customEffect;
     batch->_batch = meshBatch;
@@ -169,7 +159,6 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
     batch->_textureHeightRatio = 1.0f / (float)texture->getHeight();
 
     // Bind an ortho projection to the material by default (user can override with setProjectionMatrix)
-    GP_ASSERT(material->getParameter("u_projectionMatrix"));
     material->getParameter("u_projectionMatrix")->bindValue(batch, &SpriteBatch::getOrthoMatrix);
 
     return batch;
@@ -177,7 +166,6 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
 
 void SpriteBatch::begin()
 {
-    GP_ASSERT(_batch);
     _batch->begin();
 }
 
@@ -218,11 +206,12 @@ void SpriteBatch::draw(const Vector3& dst, const Rectangle& src, const Vector2&
 void SpriteBatch::draw(const Vector3& dst, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color,
                        const Vector2& rotationPoint, float rotationAngle, bool positionIsCenter)
 {
-    GP_ASSERT(_batch);
-
-    float x = dst.x;
-    float y = dst.y;
+    draw(dst.x, dst.y, dst.z, width, height, u1, v1, u2, v2, color, rotationPoint, rotationAngle, positionIsCenter);
+}
 
+void SpriteBatch::draw(float x, float y, float z, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color,
+          const Vector2& rotationPoint, float rotationAngle, bool positionIsCenter)
+{
     // Treat the given position as the center if the user specified it as such.
     if (positionIsCenter)
     {
@@ -249,13 +238,13 @@ void SpriteBatch::draw(const Vector3& dst, float width, float height, float u1,
     upRight.rotate(pivotPoint, rotationAngle);
     downLeft.rotate(pivotPoint, rotationAngle);
     downRight.rotate(pivotPoint, rotationAngle);
-    
+
     // Write sprite vertex data.
     static SpriteVertex v[4];
-    ADD_SPRITE_VERTEX(v[0], upLeft.x, upLeft.y, dst.z, u1, v1, color.x, color.y, color.z, color.w);
-    ADD_SPRITE_VERTEX(v[1], upRight.x, upRight.y, dst.z, u1, v2, color.x, color.y, color.z, color.w);
-    ADD_SPRITE_VERTEX(v[2], downLeft.x, downLeft.y, dst.z, u2, v1, color.x, color.y, color.z, color.w);
-    ADD_SPRITE_VERTEX(v[3], downRight.x, downRight.y, dst.z, u2, v2, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[0], downLeft.x, downLeft.y, z, u1, v1, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[1], upLeft.x, upLeft.y, z, u1, v2, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[2], downRight.x, downRight.y, z, u2, v1, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[3], upRight.x, upRight.y, z, u2, v2, color.x, color.y, color.z, color.w);
     
     static unsigned short indices[4] = { 0, 1, 2, 3 };
 
@@ -265,8 +254,6 @@ void SpriteBatch::draw(const Vector3& dst, float width, float height, float u1,
 void SpriteBatch::draw(const Vector3& position, const Vector3& right, const Vector3& forward, float width, float height,
     float u1, float v1, float u2, float v2, const Vector4& color, const Vector2& rotationPoint, float rotationAngle)
 {
-    GP_ASSERT(_batch);
-
     // Calculate the vertex positions.
     //static Vector3 p[4];
 
@@ -396,14 +383,14 @@ void SpriteBatch::addSprite(float x, float y, float width, float height, float u
 
 void SpriteBatch::draw(SpriteBatch::SpriteVertex* vertices, unsigned int vertexCount, unsigned short* indices, unsigned int indexCount)
 {
-    GP_ASSERT(_batch);
+    GP_ASSERT(vertices);
+    GP_ASSERT(indices);
+
     _batch->add(vertices, vertexCount, indices, indexCount);
 }
 
 void SpriteBatch::draw(float x, float y, float z, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, bool positionIsCenter)
 {
-    GP_ASSERT(_batch);
-
     // Treat the given position as the center if the user specified it as such.
     if (positionIsCenter)
     {
@@ -427,28 +414,24 @@ void SpriteBatch::draw(float x, float y, float z, float width, float height, flo
 
 void SpriteBatch::end()
 {
-    // Finish and draw the batch.
-    GP_ASSERT(_batch);
+    // Finish and draw the batch
     _batch->end();
     _batch->draw();
 }
 
 RenderState::StateBlock* SpriteBatch::getStateBlock() const
 {
-    GP_ASSERT(_batch && _batch->getMaterial());
     return _batch->getMaterial()->getStateBlock();
 }
 
 Material* SpriteBatch::getMaterial() const
 {
-    GP_ASSERT(_batch);
     return _batch->getMaterial();
 }
 
 void SpriteBatch::setProjectionMatrix(const Matrix& matrix)
 {
     // Bind the specified matrix to a parameter named 'u_projectionMatrix' (assumed to exist).
-    GP_ASSERT(_batch && _batch->getMaterial() && _batch->getMaterial()->getParameter("u_projectionMatrix"));
     _batch->getMaterial()->getParameter("u_projectionMatrix")->setValue(matrix);
 }
 
@@ -462,9 +445,6 @@ const Matrix& SpriteBatch::getOrthoMatrix() const
 
 bool SpriteBatch::clipSprite(const Rectangle& clip, float& x, float& y, float& width, float& height, float& u1, float& v1, float& u2, float& v2)
 {
-    GP_ASSERT(width);
-    GP_ASSERT(height);
-
     // Clip the rectangle given by { x, y, width, height } into clip.
     // We need to scale the uvs accordingly as we do this.
 

+ 3 - 0
gameplay/src/SpriteBatch.h

@@ -146,6 +146,9 @@ public:
     void draw(const Vector3& dst, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color,
               const Vector2& rotationPoint, float rotationAngle, bool positionIsCenter = false);
     
+    void draw(float x, float y, float z, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color,
+              const Vector2& rotationPoint, float rotationAngle, bool positionIsCenter = false);
+
     /**
      * Draws a single sprite, rotated about the implied up vector.
      * 

+ 1 - 1
gameplay/src/VerticalLayout.cpp

@@ -81,7 +81,7 @@ void VerticalLayout::update(const Container* container)
 
         yPosition += margin.top;
 
-        control->setPosition(0, yPosition);
+        control->setPosition(margin.left, yPosition);
         control->update(container->getClip(), Vector2::zero());
 
         yPosition += bounds.height + margin.bottom;