Browse Source

ScrollView refactoring. Now ScrollView creates its own scrollbars.
Added UI element visibility change event.
UI bugfixes.

Lasse Öörni 15 years ago
parent
commit
0bea69e546

+ 70 - 5
Bin/Data/UI/DefaultStyle.xml

@@ -79,11 +79,76 @@
         </slider>
     </element>
     <element type="ScrollView">
-        <size value="16 16" />
-        <texture name="Textures/UI.png" />
-        <imagerect value="112 0 128 16" />
-        <border value="2 2 2 2" />
-        <clipborder value="1 1 1 1" />
+        <horizontalscrollbar>
+            <backbutton>
+                <size value="16 16" />
+                <texture name="Textures/UI.png" />
+                <imagerect horizontal="32 32 48 48" vertical="0 32 16 48" />
+                <border value="3 3 3 3" />
+                <pressedoffset value="64 0" />
+                <hoveroffset value="0 16" />
+                <repeat delay="0.4" rate="20" />
+            </backbutton>
+            <forwardbutton>
+                <size value="16 16" />
+                <texture name="Textures/UI.png" />
+                <imagerect horizontal="48 32 64 48" vertical="16 32 32 48" />
+                <border value="3 3 3 3" />
+                <pressedoffset value="64 0" />
+                <hoveroffset value="0 16" />
+                <repeat delay="0.4" rate="20" />
+            </forwardbutton>
+            <slider>
+                <size value="16 16" />
+                <texture name="Textures/UI.png" />
+                <imagerect value="48 0 64 16" />
+                <border value="3 3 3 3" />
+                <knob>
+                    <texture name="Textures/UI.png" />
+                    <imagerect value="16 0 32 16" />
+                    <border value="4 4 4 4" />
+                    <hoveroffset value="0 16" />
+                </knob>
+            </slider>
+        </horizontalscrollbar>
+        <verticalscrollbar>
+            <backbutton>
+                <size value="16 16" />
+                <texture name="Textures/UI.png" />
+                <imagerect horizontal="32 32 48 48" vertical="0 32 16 48" />
+                <border value="3 3 3 3" />
+                <pressedoffset value="64 0" />
+                <hoveroffset value="0 16" />
+                <repeat delay="0.4" rate="20" />
+            </backbutton>
+            <forwardbutton>
+                <size value="16 16" />
+                <texture name="Textures/UI.png" />
+                <imagerect horizontal="48 32 64 48" vertical="16 32 32 48" />
+                <border value="3 3 3 3" />
+                <pressedoffset value="64 0" />
+                <hoveroffset value="0 16" />
+                <repeat delay="0.4" rate="20" />
+            </forwardbutton>
+            <slider>
+                <size value="16 16" />
+                <texture name="Textures/UI.png" />
+                <imagerect value="48 0 64 16" />
+                <border value="3 3 3 3" />
+                <knob>
+                    <texture name="Textures/UI.png" />
+                    <imagerect value="16 0 32 16" />
+                    <border value="4 4 4 4" />
+                    <hoveroffset value="0 16" />
+                </knob>
+            </slider>
+        </verticalscrollbar>
+        <scrollpanel>
+            <texture name="Textures/UI.png" />
+            <imagerect value="112 0 128 16" />
+            <border value="2 2 2 2" />
+            <clipborder value="1 1 1 1" />
+        </scrollpanel>
     </element>
     <element type="Slider">
         <size value="16 16" />

+ 14 - 20
Bin/Data/UI/TestLayout.xml

@@ -23,29 +23,23 @@
     <element type="CheckBox">
         <position value="40 130" />
     </element>
-    <element type="ScrollBar" name="ScrollV" >
-        <position value="350 100" />
-        <size value="10 100" />
-        <orientation value="vertical" />
-    </element>
-    <element type="ScrollBar" name="ScrollH" >
-        <position value="150 200" />
-        <size value="200 10" />
-        <orientation value="horizontal" />
+    <element type="Text" name="ScrollViewText" >
+        <position value="1 0" />
+        <size value="200 150" />
+        <font name="times.ttf" size="15" />
+        <text value="Urho3D - a Win32/Direct3D9 rendering and game engine\nhttp://urho3d.googlecode.com\nLicensed under the MIT license, see License.txt for details." />
+        <wordwrap enable="true" />
     </element>
     <element type="ScrollView">
         <position value="150 100" />
-        <size value="200 100" />
-        <viewsize value="200 150" />
-        <horizontalscrollbar name="ScrollH" />
-        <verticalscrollbar name="ScrollV" />
-        <element type="Text">
-            <position value="1 1" />
-            <size value="200 150" />
-            <font name="times.ttf" size="15" />
-            <text value="Urho3D - a Win32/Direct3D9 rendering and game engine\nhttp://urho3d.googlecode.com\nLicensed under the MIT license, see License.txt for details." />
-            <wordwrap enable="true" />
-        </element>
+        <size value="210 110" />
+        <horizontalscrollbar>
+            <height value="10" />
+        </horizontalscrollbar>
+        <verticalscrollbar>
+            <width value="10" />
+        </verticalscrollbar>
+        <contentelement name="ScrollViewText" />
     </element>
     <element type="LineEdit">
         <position value="150 230" />

+ 6 - 6
Engine/Engine/RegisterUI.cpp

@@ -149,19 +149,19 @@ static void registerScrollBar(asIScriptEngine* engine)
 
 static void registerScrollView(asIScriptEngine* engine)
 {
-    registerBorderImage<ScrollView>(engine, "ScrollView");
+    registerUIElement<ScrollView>(engine, "ScrollView");
     engine->RegisterObjectMethod("ScrollView", "void setViewPosition(const IntVector2& in)", asMETHODPR(ScrollView, setViewPosition, (const IntVector2&), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("ScrollView", "void setViewPosition(int, int)", asMETHODPR(ScrollView, setViewPosition, (int, int), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("ScrollView", "void setViewSize(const IntVector2& in)", asMETHODPR(ScrollView, setViewSize, (const IntVector2&), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("ScrollView", "void setViewSize(int, int)", asMETHODPR(ScrollView, setViewSize, (int, int), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("ScrollView", "void setHorizontalScrollBar(ScrollBar@+)", asMETHOD(ScrollView, setHorizontalScrollBar), asCALL_THISCALL);
-    engine->RegisterObjectMethod("ScrollView", "void setVerticalSlider(ScrollBar@+)", asMETHOD(ScrollView, setVerticalScrollBar), asCALL_THISCALL);
+    engine->RegisterObjectMethod("ScrollView", "void setScrollBarsVisible(bool, bool)", asMETHOD(ScrollView, setScrollBarsVisible), asCALL_THISCALL);
     engine->RegisterObjectMethod("ScrollView", "void setScrollStep(float)", asMETHOD(ScrollView, setScrollStep), asCALL_THISCALL);
     engine->RegisterObjectMethod("ScrollView", "void setPageStep(float)", asMETHOD(ScrollView, setPageStep), asCALL_THISCALL);
     engine->RegisterObjectMethod("ScrollView", "const IntVector2& getViewPosition() const", asMETHOD(ScrollView, getViewPosition), asCALL_THISCALL);
-    engine->RegisterObjectMethod("ScrollView", "const IntVector2& getViewSize() const", asMETHOD(ScrollView, getViewSize), asCALL_THISCALL);
+    engine->RegisterObjectMethod("ScrollView", "UIElement@+ getElement() const", asMETHOD(ScrollView, getElement), asCALL_THISCALL);
     engine->RegisterObjectMethod("ScrollView", "ScrollBar@+ getHorizontalScrollBar() const", asMETHOD(ScrollView, getHorizontalScrollBar), asCALL_THISCALL);
     engine->RegisterObjectMethod("ScrollView", "ScrollBar@+ getVerticalScrollBar() const", asMETHOD(ScrollView, getVerticalScrollBar), asCALL_THISCALL);
+    engine->RegisterObjectMethod("ScrollView", "BorderImage@+ getScrollPanel() const", asMETHOD(ScrollView, getScrollPanel), asCALL_THISCALL);
+    engine->RegisterObjectMethod("ScrollView", "bool getHorizontalScrollBarVisible() const", asMETHOD(ScrollView, getHorizontalScrollBarVisible), asCALL_THISCALL);
+    engine->RegisterObjectMethod("ScrollView", "bool getVerticalScrollBarVisible() const", asMETHOD(ScrollView, getVerticalScrollBarVisible), asCALL_THISCALL);
     engine->RegisterObjectMethod("ScrollView", "float getScrollStep() const", asMETHOD(ScrollView, getScrollStep), asCALL_THISCALL);
     engine->RegisterObjectMethod("ScrollView", "float getPageStep() const", asMETHOD(ScrollView, getPageStep), asCALL_THISCALL);
     registerRefCasts<UIElement, ScrollView>(engine, "UIElement", "ScrollView");

+ 2 - 0
Engine/UI/ScrollBar.cpp

@@ -112,6 +112,7 @@ void ScrollBar::onResize()
 {
     // Disable layout operations while setting the button sizes is incomplete
     mUpdateLayoutNestingLevel++;
+    
     if (mSlider->getOrientation() == O_HORIZONTAL)
     {
         int height = getHeight();
@@ -124,6 +125,7 @@ void ScrollBar::onResize()
         mBackButton->setFixedSize(width, width);
         mForwardButton->setFixedSize(width, width);
     }
+    
     mUpdateLayoutNestingLevel--;
 }
 

+ 132 - 79
Engine/UI/ScrollView.cpp

@@ -22,6 +22,7 @@
 //
 
 #include "Precompiled.h"
+#include "BorderImage.h"
 #include "InputEvents.h"
 #include "ScrollBar.h"
 #include "ScrollView.h"
@@ -30,15 +31,34 @@
 #include "DebugNew.h"
 
 ScrollView::ScrollView(const std::string& name) :
-    BorderImage(name),
+    UIElement(name),
     mViewPosition(IntVector2::sZero),
     mViewSize(IntVector2::sZero),
     mScrollStep(0.1f),
     mPageStep(1.0f)
 {
-    mClipChildren = true;
     mEnabled = true;
     mFocusMode = FM_FOCUSABLE_DEFOCUSABLE;
+    
+    mHorizontalScrollBar = new ScrollBar();
+    mHorizontalScrollBar->setAlignment(HA_LEFT, VA_BOTTOM);
+    mHorizontalScrollBar->setOrientation(O_HORIZONTAL);
+    mVerticalScrollBar = new ScrollBar();
+    mVerticalScrollBar->setAlignment(HA_RIGHT, VA_TOP);
+    mVerticalScrollBar->setOrientation(O_VERTICAL);
+    mScrollPanel = new BorderImage();
+    mScrollPanel->setEnabled(true);
+    mScrollPanel->setClipChildren(true);
+    
+    addChild(mHorizontalScrollBar);
+    addChild(mVerticalScrollBar);
+    addChild(mScrollPanel);
+    
+    subscribeToEvent(mHorizontalScrollBar, EVENT_SCROLLBARCHANGED, EVENT_HANDLER(ScrollView, handleScrollBarChanged));
+    subscribeToEvent(mHorizontalScrollBar, EVENT_VISIBLECHANGED, EVENT_HANDLER(ScrollView, handleScrollBarVisibleChanged));
+    subscribeToEvent(mVerticalScrollBar, EVENT_SCROLLBARCHANGED, EVENT_HANDLER(ScrollView, handleScrollBarChanged));
+    subscribeToEvent(mVerticalScrollBar, EVENT_VISIBLECHANGED, EVENT_HANDLER(ScrollView, handleScrollBarVisibleChanged));
+    subscribeToEvent(EVENT_TRYFOCUS, EVENT_HANDLER(ScrollView, handleTryFocus));
 }
 
 ScrollView::~ScrollView()
@@ -47,25 +67,33 @@ ScrollView::~ScrollView()
 
 void ScrollView::setStyle(const XMLElement& element, ResourceCache* cache)
 {
-    BorderImage::setStyle(element, cache);
+    UIElement::setStyle(element, cache);
     
     if (element.hasChildElement("viewposition"))
         setViewPosition(element.getChildElement("viewposition").getIntVector2("value"));
-    if (element.hasChildElement("viewsize"))
-        setViewSize(element.getChildElement("viewsize").getIntVector2("value"));
     if (element.hasChildElement("scrollstep"))
         setScrollStep(element.getChildElement("scrollstep").getFloat("value"));
     if (element.hasChildElement("pagestep"))
         setScrollStep(element.getChildElement("pagestep").getFloat("value"));
     
+    XMLElement horizElem = element.getChildElement("horizontalscrollbar");
+    if (horizElem)
+        mHorizontalScrollBar->setStyle(horizElem, cache);
+    XMLElement vertElem = element.getChildElement("verticalscrollbar");
+    if (vertElem)
+        mVerticalScrollBar->setStyle(vertElem, cache);
+    XMLElement panelElem = element.getChildElement("scrollpanel");
+    if (panelElem)
+        mScrollPanel->setStyle(panelElem, cache);
+    
     UIElement* root = getRootElement();
-    if (root)
-    {
-        if (element.hasChildElement("horizontalscrollbar"))
-            setHorizontalScrollBar(dynamic_cast<ScrollBar*>(root->getChild(element.getChildElement("horizontalscrollbar").getString("name"), true)));
-        if (element.hasChildElement("verticalscrollbar"))
-            setVerticalScrollBar(dynamic_cast<ScrollBar*>(root->getChild(element.getChildElement("verticalscrollbar").getString("name"), true)));
-    }
+    if ((root) && (element.hasChildElement("contentelement")))
+        setElement(root->getChild(element.getChildElement("contentelement").getString("name"), true));
+    
+    // Set the scrollbar orientations again and perform size update now that the style is known
+    mHorizontalScrollBar->setOrientation(O_HORIZONTAL);
+    mVerticalScrollBar->setOrientation(O_VERTICAL);
+    onResize();
 }
 
 void ScrollView::onKey(int key, int buttons, int qualifiers)
@@ -134,27 +162,39 @@ void ScrollView::onKey(int key, int buttons, int qualifiers)
     }
 }
 
-void ScrollView::onFocus()
-{
-    // Set selected state (constant hover) on the scroll bars to show they are now under key control
-    if (mHorizontalScrollBar)
-        mHorizontalScrollBar->setSelected(true);
-    if (mVerticalScrollBar)
-        mVerticalScrollBar->setSelected(true);
-}
-
-void ScrollView::onDefocus()
+void ScrollView::onResize()
 {
-    if (mHorizontalScrollBar)
-        mHorizontalScrollBar->setSelected(false);
-    if (mVerticalScrollBar)
-        mVerticalScrollBar->setSelected(false);
+    IntVector2 panelSize = getSize();
+    if (mVerticalScrollBar->isVisible())
+        panelSize.mX -= mVerticalScrollBar->getWidth();
+    if (mHorizontalScrollBar->isVisible())
+        panelSize.mY -= mHorizontalScrollBar->getHeight();
+    
+    mScrollPanel->setSize(panelSize);
+    mHorizontalScrollBar->setWidth(mScrollPanel->getWidth());
+    mVerticalScrollBar->setHeight(mScrollPanel->getHeight());
+    
+    updateViewSize();
 }
 
-void ScrollView::onResize()
+void ScrollView::setElement(UIElement* element)
 {
-    // This will grow the view size if it is smaller than current size
-    setViewSize(mViewSize);
+    if (element == mElement)
+        return;
+    
+    if (mElement)
+    {
+        mScrollPanel->removeChild(mElement);
+        unsubscribeFromEvent(mElement, EVENT_RESIZED);
+    }
+    mElement = element;
+    if (mElement)
+    {
+        mScrollPanel->addChild(mElement);
+        subscribeToEvent(mElement, EVENT_RESIZED, EVENT_HANDLER(ScrollView, handleElementResized));
+    }
+    
+    updateViewSize();
 }
 
 void ScrollView::setViewPosition(const IntVector2& position)
@@ -168,75 +208,49 @@ void ScrollView::setViewPosition(int x, int y)
     setViewPosition(IntVector2(x, y));
 }
 
-void ScrollView::setViewSize(const IntVector2& size)
-{
-    mViewSize.mX = max(size.mX, getWidth());
-    mViewSize.mY = max(size.mY, getHeight());
-    updateView(mViewPosition);
-    updateScrollBars();
-}
-
-void ScrollView::setViewSize(int x, int y)
+void ScrollView::setScrollBarsVisible(bool horizontal, bool vertical)
 {
-    setViewSize(IntVector2(x, y));
+    mHorizontalScrollBar->setVisible(horizontal);
+    mVerticalScrollBar->setVisible(vertical);
 }
 
-void ScrollView::setHorizontalScrollBar(ScrollBar* scrollBar)
+void ScrollView::setScrollStep(float step)
 {
-    if (mHorizontalScrollBar)
-        unsubscribeFromEvent(mHorizontalScrollBar, EVENT_SCROLLBARCHANGED);
-    
-    mHorizontalScrollBar = scrollBar;
-    if (mHorizontalScrollBar)
-        subscribeToEvent(mHorizontalScrollBar, EVENT_SCROLLBARCHANGED, EVENT_HANDLER(ScrollView, handleScrollBarChanged));
-    
-    updateScrollBars();
+    mScrollStep = max(step, 0.0f);
 }
 
-void ScrollView::setVerticalScrollBar(ScrollBar* scrollBar)
+void ScrollView::setPageStep(float step)
 {
-    if (mVerticalScrollBar)
-        unsubscribeFromEvent(mVerticalScrollBar, EVENT_SCROLLBARCHANGED);
-    
-    mVerticalScrollBar = scrollBar;
-    if (mVerticalScrollBar)
-        subscribeToEvent(mVerticalScrollBar, EVENT_SCROLLBARCHANGED, EVENT_HANDLER(ScrollView, handleScrollBarChanged));
-    
-    updateScrollBars();
+    mPageStep = max(step, 0.0f);
 }
 
-void ScrollView::setScrollStep(float step)
+bool ScrollView::getHorizontalScrollBarVisible() const
 {
-    mScrollStep = max(step, 0.0f);
+    return mHorizontalScrollBar->isVisible();
 }
 
-void ScrollView::setPageStep(float step)
+bool ScrollView::getVerticalScrollBarVisible() const
 {
-    mPageStep = max(step, 0.0f);
+    return mVerticalScrollBar->isVisible();
 }
 
-void ScrollView::updateViewFromScrollBars()
+void ScrollView::updateViewSize()
 {
-    if ((!mHorizontalScrollBar) && (!mVerticalScrollBar))
-        return;
-    
-    IntVector2 oldPosition = mViewPosition;
-    IntVector2 newPosition = mViewPosition;
-    const IntVector2& size = getSize();
-    
-    if (mHorizontalScrollBar)
-        newPosition.mX = (int)(mHorizontalScrollBar->getValue() * (float)size.mX);
-    if (mVerticalScrollBar)
-        newPosition.mY = (int)(mVerticalScrollBar->getValue() * (float)size.mY);
+    IntVector2 size(IntVector2::sZero);
+    if (mElement)
+        size = mElement->getSize();
     
-    updateView(newPosition);
+    mViewSize.mX = max(size.mX, mScrollPanel->getWidth());
+    mViewSize.mY = max(size.mY, mScrollPanel->getHeight());
+    updateView(mViewPosition);
+    updateScrollBars();
 }
 
 void ScrollView::updateScrollBars()
 {
     mIgnoreEvents = true;
     
-    const IntVector2& size = getSize();
+    const IntVector2& size = mScrollPanel->getSize();
     
     if ((mHorizontalScrollBar) && (size.mX > 0) && (mViewSize.mX > 0))
     {
@@ -256,9 +270,9 @@ void ScrollView::updateView(const IntVector2& position)
 {
     IntVector2 oldPosition = mViewPosition;
     
-    mViewPosition.mX = clamp(position.mX, 0, mViewSize.mX - getWidth());
-    mViewPosition.mY = clamp(position.mY, 0, mViewSize.mY - getHeight());
-    setChildOffset(-mViewPosition);
+    mViewPosition.mX = clamp(position.mX, 0, mViewSize.mX - mScrollPanel->getWidth());
+    mViewPosition.mY = clamp(position.mY, 0, mViewSize.mY - mScrollPanel->getHeight());
+    mScrollPanel->setChildOffset(-mViewPosition);
     
     if (mViewPosition != oldPosition)
     {
@@ -275,5 +289,44 @@ void ScrollView::updateView(const IntVector2& position)
 void ScrollView::handleScrollBarChanged(StringHash eventType, VariantMap& eventData)
 {
     if (!mIgnoreEvents)
-        updateViewFromScrollBars();
+    {
+        updateView(IntVector2(
+            (int)(mHorizontalScrollBar->getValue() * (float)mScrollPanel->getWidth()),
+            (int)(mVerticalScrollBar->getValue() * (float)mScrollPanel->getHeight())
+        ));
+    }
+}
+
+void ScrollView::handleScrollBarVisibleChanged(StringHash eventType, VariantMap& eventData)
+{
+    // Need to recalculate panel size when scrollbar visibility changes
+    onResize();
+}
+
+void ScrollView::handleElementResized(StringHash eventType, VariantMap& eventData)
+{
+    updateViewSize();
+}
+
+void ScrollView::handleTryFocus(StringHash eventType, VariantMap& eventData)
+{
+    using namespace TryFocus;
+    
+    UIElement* focusElement = static_cast<UIElement*>(eventData[P_ELEMENT].getPtr());
+    if ((!focusElement) || (focusElement == this))
+        return;
+    
+    // If the element is a non-focusable child of the ScrollView, divert focus to the ScrollView
+    if (focusElement->getFocusMode() < FM_FOCUSABLE)
+    {
+        while (focusElement)
+        {
+            focusElement = focusElement->getParent();
+            if (focusElement == this)
+            {
+                eventData[P_ELEMENT] = (void*)this;
+                return;
+            }
+        }
+    }
 }

+ 30 - 21
Engine/UI/ScrollView.h

@@ -24,12 +24,13 @@
 #ifndef UI_SCROLLVIEW_H
 #define UI_SCROLLVIEW_H
 
-#include "BorderImage.h"
+#include "UIElement.h"
 
+class BorderImage;
 class ScrollBar;
 
 //! A scrollable view for showing child widgets
-class ScrollView : public BorderImage
+class ScrollView : public UIElement
 {
     DEFINE_TYPE(ScrollView);
     
@@ -43,25 +44,17 @@ public:
     virtual void setStyle(const XMLElement& element, ResourceCache* cache);
     //! React to a key press
     virtual void onKey(int key, int buttons, int qualifiers);
-    //! React to gaining focus
-    virtual void onFocus();
-    //! React to losing focus
-    virtual void onDefocus();
     //! React to resize
     virtual void onResize();
     
+    //! Set content element
+    void setElement(UIElement* element);
     //! Set view offset from the top-left corner
     void setViewPosition(const IntVector2& position);
     //! Set view offset from the top-left corner
     void setViewPosition(int x, int y);
-    //! Set total view size
-    void setViewSize(const IntVector2& position);
-    //! Set total view size
-    void setViewSize(int x, int y);
-    //! Set horizontal scroll bar
-    void setHorizontalScrollBar(ScrollBar* scrollBar);
-    //! Set vertical scroll bar
-    void setVerticalScrollBar(ScrollBar* scrollBar);
+    //! Set scrollbars' visibility
+    void setScrollBarsVisible(bool horizontal, bool vertical);
     //! Set arrow key scroll step
     void setScrollStep(float step);
     //! Set arrow key page step
@@ -69,29 +62,39 @@ public:
     
     //! Return view offset from the top-left corner
     const IntVector2& getViewPosition() const { return mViewPosition; }
-    //! Return total view size
-    const IntVector2& getViewSize() const { return mViewSize; }
+    //! Return content element
+    UIElement* getElement() const { return mElement; }
     //! Return horizontal scroll bar
     ScrollBar* getHorizontalScrollBar() const { return mHorizontalScrollBar; }
     //! Return vertical scroll bar
     ScrollBar* getVerticalScrollBar() const { return mVerticalScrollBar; }
+    //! Return scroll panel
+    BorderImage* getScrollPanel() const { return mScrollPanel; }
+    //! Return horizontal scrollbar visibility
+    bool getHorizontalScrollBarVisible() const;
+    //! Return vertical scrollbar visibility
+    bool getVerticalScrollBarVisible() const;
     //! Return arrow key scroll step
     float getScrollStep() const { return mScrollStep; }
     //! Return arrow key page step
     float getPageStep() const { return mPageStep; }
     
 protected:
-    //! Update the view from scrollbars if available
-    void updateViewFromScrollBars();
+    //! Update view size from the content element
+    void updateViewSize();
     //! Update the scrollbars' ranges and positions
     void updateScrollBars();
-    //! Limit and update view with a new position
+    //! Limit and update the view with a new position
     void updateView(const IntVector2& position);
     
+    //! Content element
+    SharedPtr<UIElement> mElement;
     //! Horizontal scroll bar
-    WeakPtr<ScrollBar> mHorizontalScrollBar;
+    SharedPtr<ScrollBar> mHorizontalScrollBar;
     //! Vertical scroll bar
-    WeakPtr<ScrollBar> mVerticalScrollBar;
+    SharedPtr<ScrollBar> mVerticalScrollBar;
+    //! Scroll panel element
+    SharedPtr<BorderImage> mScrollPanel;
     //! Current view offset from the top-left corner
     IntVector2 mViewPosition;
     //! Total view size
@@ -106,6 +109,12 @@ protected:
 private:
     //! Handle scrollbar value changed
     void handleScrollBarChanged(StringHash eventType, VariantMap& eventData);
+    //! Handle scrollbar visibility changed
+    void handleScrollBarVisibleChanged(StringHash eventType, VariantMap& eventData);
+    //! Handle content element resized
+    void handleElementResized(StringHash eventType, VariantMap& eventData);
+    //! Handle focus change attempt (check if ScrollView needs to be focused)
+    void handleTryFocus(StringHash eventType, VariantMap& eventData);
 };
 
 #endif // UI_SCROLLVIEW_H

+ 2 - 3
Engine/UI/Text.cpp

@@ -430,9 +430,8 @@ void Text::updateText(bool inResize)
         mCharPositions[mText.length()] = IntVector2(x, y);
     }
     
-    if (mWordwrap)
-        setHeight(height);
-    else
+    // Resize self only when not using wordwrap
+    if (!mWordwrap)
         setSize(width, height);
 }
 

+ 3 - 0
Engine/UI/UI.cpp

@@ -118,6 +118,9 @@ void UI::setFocusElement(UIElement* element)
     eventData[P_ELEMENT] = (void*)element;
     sendEvent(EVENT_TRYFOCUS, eventData);
     
+    // The event receivers may divert the focus
+    element = static_cast<UIElement*>(eventData[P_ELEMENT].getPtr());
+    
     // Return if already has focus
     if ((element) && (element->hasFocus()))
         return;

+ 26 - 12
Engine/UI/UIElement.cpp

@@ -22,7 +22,6 @@
 //
 
 #include "Precompiled.h"
-#include "Log.h"
 #include "ResourceCache.h"
 #include "UIElement.h"
 #include "UIEvents.h"
@@ -93,6 +92,10 @@ void UIElement::setStyle(const XMLElement& element, ResourceCache* cache)
         setPosition(element.getChildElement("position").getIntVector2("value"));
     if (element.hasChildElement("size"))
         setSize(element.getChildElement("size").getIntVector2("value"));
+    if (element.hasChildElement("width"))
+        setWidth(element.getChildElement("width").getInt("value"));
+    if (element.hasChildElement("height"))
+        setHeight(element.getChildElement("height").getInt("value"));
     if (element.hasChildElement("minsize"))
         setMinSize(element.getChildElement("minsize").getIntVector2("value"));
     if (element.hasChildElement("maxsize"))
@@ -173,7 +176,7 @@ void UIElement::setStyle(const XMLElement& element, ResourceCache* cache)
     if (element.hasChildElement("layout"))
     {
         XMLElement layoutElem = element.getChildElement("layout");
-        std::string orientation = layoutElem.getStringLower(orientation);
+        std::string orientation = layoutElem.getStringLower("orientation");
         if ((orientation == "horizontal") || (orientation == "h"))
             mLayoutOrientation = O_HORIZONTAL;
         if ((orientation == "vertical") || (orientation == "v"))
@@ -595,6 +598,13 @@ void UIElement::setVisible(bool enable)
         // Parent's layout may change as a result of visibility change
         if (mParent)
             mParent->updateLayout();
+        
+        using namespace VisibleChanged;
+        
+        VariantMap eventData;
+        eventData[P_ELEMENT] = (void*)this;
+        eventData[P_VISIBLE] = mVisible;
+        sendEvent(EVENT_VISIBLECHANGED, eventData);
     }
 }
 
@@ -970,6 +980,16 @@ IntRect UIElement::getCombinedScreenRect()
     return combined;
 }
 
+void UIElement::setChildOffset(const IntVector2& offset)
+{
+    if (offset != mChildOffset)
+    {
+        mChildOffset = offset;
+        for (std::vector<SharedPtr<UIElement> >::const_iterator i = mChildren.begin(); i != mChildren.end(); ++i)
+            (*i)->markDirty();
+    }
+}
+
 void UIElement::setHovering(bool enable)
 {
     mHovering = enable;
@@ -1023,16 +1043,6 @@ void UIElement::markDirty()
         (*i)->markDirty();
 }
 
-void UIElement::setChildOffset(const IntVector2& offset)
-{
-    if (offset != mChildOffset)
-    {
-        mChildOffset = offset;
-        for (std::vector<SharedPtr<UIElement> >::const_iterator i = mChildren.begin(); i != mChildren.end(); ++i)
-            (*i)->markDirty();
-    }
-}
-
 void UIElement::getChildrenRecursive(std::vector<UIElement*>& dest) const
 {
     for (std::vector<SharedPtr<UIElement> >::const_iterator i = mChildren.begin(); i != mChildren.end(); ++i)
@@ -1152,9 +1162,11 @@ IntVector2 UIElement::getLayoutChildPosition(UIElement* child)
     {
     case HA_LEFT:
         ret.mX = mLayoutBorder.mLeft;
+        break;
         
     case HA_RIGHT:
         ret.mX = -mLayoutBorder.mRight;
+        break;
     }
     
     VerticalAlignment va = child->getVerticalAlignment();
@@ -1162,9 +1174,11 @@ IntVector2 UIElement::getLayoutChildPosition(UIElement* child)
     {
     case VA_TOP:
         ret.mY = mLayoutBorder.mTop;
+        break;
         
     case VA_BOTTOM:
         ret.mY = -mLayoutBorder.mBottom;
+        break;
     }
     
     return ret;

+ 2 - 2
Engine/UI/UIElement.h

@@ -316,6 +316,8 @@ public:
     //! Return combined screen coordinate rect of element and its children
     IntRect getCombinedScreenRect();
     
+    //! Set child offset
+    void setChildOffset(const IntVector2& offset);
     //! Set hovering state
     void setHovering(bool enable);
     //! Set origin element
@@ -329,8 +331,6 @@ public:
 protected:
     //! Mark screen position as needing an update
     void markDirty();
-    //! Set child offset
-    void setChildOffset(const IntVector2& offset);
     
     //! Name
     std::string mName;

+ 7 - 0
Engine/UI/UIEvents.h

@@ -34,6 +34,13 @@ DEFINE_EVENT(EVENT_RESIZED, Resized)
     EVENT_PARAM(P_Height, Height);              // int
 }
 
+//! UI element visibility changed
+DEFINE_EVENT(EVENT_VISIBLECHANGED, VisibleChanged)
+{
+    EVENT_PARAM(P_ELEMENT, Element);            // UIElement pointer
+    EVENT_PARAM(P_VISIBLE, Visible);            // bool
+}
+
 //! Trying to focus an UI element. Sent before checking for success. Also sent with 0 pointer for global defocus
 DEFINE_EVENT(EVENT_TRYFOCUS, TryFocus)
 {