瀏覽代碼

Initial ListView implementation.
UI bugfixes.

Lasse Öörni 15 年之前
父節點
當前提交
cae8301062

+ 25 - 25
Bin/Data/Scripts/GraphicsTest.as

@@ -64,7 +64,7 @@ void initScene()
     engine.setDefaultScene(testScene);
     engine.setDefaultScene(testScene);
 
 
     PhysicsWorld@ world = testScene.getPhysicsWorld();
     PhysicsWorld@ world = testScene.getPhysicsWorld();
-    
+
     // Set the physics world parameters
     // Set the physics world parameters
     world.setGravity(Vector3(0.0, -9.81, 0.0));
     world.setGravity(Vector3(0.0, -9.81, 0.0));
     world.setFps(100);
     world.setFps(100);
@@ -81,7 +81,7 @@ void initScene()
     sunLight.setSpecularIntensity(1.0);
     sunLight.setSpecularIntensity(1.0);
     //sunLight.setCastShadows(true);
     //sunLight.setCastShadows(true);
     //sunLight.setShadowCascade(CascadeParameters(3, 0.95, 0.2, 500.0));
     //sunLight.setShadowCascade(CascadeParameters(3, 0.95, 0.2, 500.0));
-    
+
     // Create a zone to control the ambient lighting
     // Create a zone to control the ambient lighting
     Entity@ zoneEntity = testScene.createEntity();
     Entity@ zoneEntity = testScene.createEntity();
     Zone@ zone = zoneEntity.createComponent("Zone");
     Zone@ zone = zoneEntity.createComponent("Zone");
@@ -100,14 +100,14 @@ void initScene()
             body.setCollisionShape(cache.getResource("CollisionShape", "Physics/Box.xml"));
             body.setCollisionShape(cache.getResource("CollisionShape", "Physics/Box.xml"));
             body.setCollisionGroup(2);
             body.setCollisionGroup(2);
             body.setCollisionMask(1);
             body.setCollisionMask(1);
-            
+
             StaticModel@ object = newEntity.createComponent("StaticModel");
             StaticModel@ object = newEntity.createComponent("StaticModel");
             object.setModel(cache.getResource("Model", "Models/Box.mdl"));
             object.setModel(cache.getResource("Model", "Models/Box.mdl"));
             object.setMaterial(cache.getResource("Material", "Materials/Test.xml"));
             object.setMaterial(cache.getResource("Material", "Materials/Test.xml"));
             body.addChild(object);
             body.addChild(object);
         }
         }
     }
     }
-    
+
     // Create 2 occluder walls
     // Create 2 occluder walls
     for (int x = 0; x < 2; x++)
     for (int x = 0; x < 2; x++)
     {
     {
@@ -119,7 +119,7 @@ void initScene()
         body.setCollisionShape(cache.getResource("CollisionShape", "Physics/Box.xml"));
         body.setCollisionShape(cache.getResource("CollisionShape", "Physics/Box.xml"));
         body.setCollisionGroup(2);
         body.setCollisionGroup(2);
         body.setCollisionMask(1);
         body.setCollisionMask(1);
-        
+
         StaticModel@ object = newEntity.createComponent("StaticModel");
         StaticModel@ object = newEntity.createComponent("StaticModel");
         object.setModel(cache.getResource("Model", "Models/Box.mdl"));
         object.setModel(cache.getResource("Model", "Models/Box.mdl"));
         object.setMaterial(cache.getResource("Material", "Materials/Test.xml"));
         object.setMaterial(cache.getResource("Material", "Materials/Test.xml"));
@@ -137,7 +137,7 @@ void initScene()
         body.setCollisionShape(cache.getResource("CollisionShape", "Physics/Mushroom.xml"));
         body.setCollisionShape(cache.getResource("CollisionShape", "Physics/Mushroom.xml"));
         body.setCollisionGroup(2);
         body.setCollisionGroup(2);
         body.setCollisionMask(1);
         body.setCollisionMask(1);
-        
+
         StaticModel@ object = newEntity.createComponent("StaticModel");
         StaticModel@ object = newEntity.createComponent("StaticModel");
         object.setModel(cache.getResource("Model", "Models/Mushroom.mdl"));
         object.setModel(cache.getResource("Model", "Models/Mushroom.mdl"));
         object.setMaterial(cache.getResource("Material", "Materials/Mushroom.xml"));
         object.setMaterial(cache.getResource("Material", "Materials/Mushroom.xml"));
@@ -336,7 +336,7 @@ void createCamera()
     @camera = cameraEntity.createComponent("Camera");
     @camera = cameraEntity.createComponent("Camera");
     camera.setAspectRatio(float(renderer.getWidth()) / float(renderer.getHeight()));
     camera.setAspectRatio(float(renderer.getWidth()) / float(renderer.getHeight()));
     camera.setPosition(Vector3(-50.0, 2.0, -50.0));
     camera.setPosition(Vector3(-50.0, 2.0, -50.0));
-    
+
     @cameraLight = cameraEntity.createComponent("Light");
     @cameraLight = cameraEntity.createComponent("Light");
     cameraLight.setLightType(LIGHT_SPOT);
     cameraLight.setLightType(LIGHT_SPOT);
     cameraLight.setDirection(Vector3(0.0, 0.0, 1.0));
     cameraLight.setDirection(Vector3(0.0, 0.0, 1.0));
@@ -374,7 +374,7 @@ void handleUpdate(StringHash eventType, VariantMap& eventData)
             camera.translateRelative(Vector3(-10, 0, 0) * timeStep * speedMultiplier);
             camera.translateRelative(Vector3(-10, 0, 0) * timeStep * speedMultiplier);
         if (input.getKeyDown('D'))
         if (input.getKeyDown('D'))
             camera.translateRelative(Vector3(10, 0, 0) * timeStep * speedMultiplier);
             camera.translateRelative(Vector3(10, 0, 0) * timeStep * speedMultiplier);
-        
+
         if (input.getKeyPress('1'))
         if (input.getKeyPress('1'))
         {
         {
             int nextRenderMode = renderer.getRenderMode();
             int nextRenderMode = renderer.getRenderMode();
@@ -390,11 +390,11 @@ void handleUpdate(StringHash eventType, VariantMap& eventData)
                 if (nextRenderMode > 2)
                 if (nextRenderMode > 2)
                     nextRenderMode = 0;
                     nextRenderMode = 0;
             }
             }
-            
+
             renderer.setMode(RenderMode(nextRenderMode), renderer.getWidth(), renderer.getHeight(), renderer.getFullscreen(),
             renderer.setMode(RenderMode(nextRenderMode), renderer.getWidth(), renderer.getHeight(), renderer.getFullscreen(),
                 renderer.getVsync(), renderer.getMultiSample());
                 renderer.getVsync(), renderer.getMultiSample());
         }
         }
-        
+
         if (input.getKeyPress('2'))
         if (input.getKeyPress('2'))
         {
         {
             texturequality++;
             texturequality++;
@@ -410,34 +410,34 @@ void handleUpdate(StringHash eventType, VariantMap& eventData)
                 materialquality = 0;
                 materialquality = 0;
             pipeline.setMaterialQuality(materialquality);
             pipeline.setMaterialQuality(materialquality);
         }
         }
-        
+
         if (input.getKeyPress('4'))
         if (input.getKeyPress('4'))
         {
         {
             usespecular = !usespecular;
             usespecular = !usespecular;
             pipeline.setSpecularLighting(usespecular);
             pipeline.setSpecularLighting(usespecular);
         }
         }
-        
+
         if (input.getKeyPress('5'))
         if (input.getKeyPress('5'))
         {
         {
             drawshadows = !drawshadows;
             drawshadows = !drawshadows;
             pipeline.setDrawShadows(drawshadows);
             pipeline.setDrawShadows(drawshadows);
         }
         }
-        
+
         if (input.getKeyPress('6'))
         if (input.getKeyPress('6'))
         {
         {
             shadowmapsize *= 2;
             shadowmapsize *= 2;
             if (shadowmapsize > 2048)
             if (shadowmapsize > 2048)
                 shadowmapsize = 512;
                 shadowmapsize = 512;
-            
+
             pipeline.setShadowMapSize(shadowmapsize);
             pipeline.setShadowMapSize(shadowmapsize);
         }
         }
-        
+
         if (input.getKeyPress('7'))
         if (input.getKeyPress('7'))
         {
         {
             hiresshadowmap = !hiresshadowmap;
             hiresshadowmap = !hiresshadowmap;
             pipeline.setShadowMapHiresDepth(hiresshadowmap);
             pipeline.setShadowMapHiresDepth(hiresshadowmap);
         }
         }
-        
+
         if (input.getKeyPress('8'))
         if (input.getKeyPress('8'))
         {
         {
             useocclusion = !useocclusion;
             useocclusion = !useocclusion;
@@ -459,14 +459,14 @@ void handleUpdate(StringHash eventType, VariantMap& eventData)
                 camera.removeChild(cameraLight, true);
                 camera.removeChild(cameraLight, true);
             }
             }
         }
         }
-        
+
         if (input.getKeyPress(' '))
         if (input.getKeyPress(' '))
         {
         {
             drawdebug++;
             drawdebug++;
             if (drawdebug > 2) drawdebug = 0;
             if (drawdebug > 2) drawdebug = 0;
             engine.setDebugDrawMode(drawdebug);
             engine.setDebugDrawMode(drawdebug);
         }
         }
-        
+
         if (input.getKeyPress('P'))
         if (input.getKeyPress('P'))
         {
         {
             paused = !paused;
             paused = !paused;
@@ -488,14 +488,14 @@ void handleUpdate(StringHash eventType, VariantMap& eventData)
             pipeline.setEdgeFilter(params);
             pipeline.setEdgeFilter(params);
         }
         }
     }
     }
-    
+
     if (input.getKeyPress(KEY_ESC))
     if (input.getKeyPress(KEY_ESC))
-	{
-		if (ui.getFocusElement() is null)
-	        engine.exit();
-	    else
-		    console.setVisible(false);
-	}
+    {
+        if (ui.getFocusElement() is null)
+            engine.exit();
+        else
+            console.setVisible(false);
+    }
 }
 }
 
 
 void handleKeyDown(StringHash eventType, VariantMap& eventData)
 void handleKeyDown(StringHash eventType, VariantMap& eventData)

+ 6 - 6
Bin/Data/Scripts/NinjaSnowWar.as

@@ -214,12 +214,12 @@ void handleUpdate(StringHash eventType, VariantMap& eventData)
     }
     }
 
 
     if (input.getKeyPress(KEY_ESC))
     if (input.getKeyPress(KEY_ESC))
-	{
-		if (!console.isVisible())
-	        engine.exit();
-	    else
-		    console.setVisible(false);
-	}
+    {
+        if (!console.isVisible())
+            engine.exit();
+        else
+        console.setVisible(false);
+    }
 
 
     if (!paused)
     if (!paused)
         updateControls();
         updateControls();

+ 65 - 0
Bin/Data/UI/DefaultStyle.xml

@@ -46,6 +46,71 @@
         <pressedoffset value="16 0" />
         <pressedoffset value="16 0" />
         <hoveroffset value="0 16" />
         <hoveroffset value="0 16" />
     </element>
     </element>
+    <element type="ListView">
+        <horizontalscrollbar>
+            <height value="12" />
+            <backbutton>
+                <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" />
+            </backbutton>
+            <forwardbutton>
+                <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" />
+            </forwardbutton>
+            <slider>
+                <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>
+            <width value="12" />
+            <backbutton>
+                <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" />
+            </backbutton>
+            <forwardbutton>
+                <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" />
+            </forwardbutton>
+            <slider>
+                <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="ScrollBar">
     <element type="ScrollBar">
         <backbutton>
         <backbutton>
             <size value="12 12" />
             <size value="12 12" />

+ 69 - 9
Bin/Data/UI/TestLayout.xml

@@ -23,15 +23,75 @@
     <element type="CheckBox">
     <element type="CheckBox">
         <position value="40 130" />
         <position value="40 130" />
     </element>
     </element>
-    <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">
-        <contentelement name="ScrollViewText" />
+	<element type="Text" name="Item1">
+	    <font name="times.ttf" size="15" />
+	    <text value="Audio" />
+        <selectioncolor value="0.5 0.75 0.5" />
+	</element>
+	<element type="Text" name="Item2">
+	    <font name="times.ttf" size="15" />
+	    <text value="Common" />
+        <selectioncolor value="0.5 0.75 0.5" />
+	</element>
+	<element type="Text" name="Item3">
+	    <font name="times.ttf" size="15" />
+	    <text value="Engine" />
+        <selectioncolor value="0.5 0.75 0.5" />
+	</element>
+	<element type="Text" name="Item4">
+	    <font name="times.ttf" size="15" />
+	    <text value="Input" />
+        <selectioncolor value="0.5 0.75 0.5" />
+	</element>
+	<element type="Text" name="Item5">
+	    <font name="times.ttf" size="15" />
+	    <text value="Math" />
+        <selectioncolor value="0.5 0.75 0.5" />
+	</element>
+	<element type="Text" name="Item6">
+	    <font name="times.ttf" size="15" />
+	    <text value="Network" />
+        <selectioncolor value="0.5 0.75 0.5" />
+	</element>
+	<element type="Text" name="Item7">
+	    <font name="times.ttf" size="15" />
+	    <text value="Physics" />
+        <selectioncolor value="0.5 0.75 0.5" />
+	</element>
+	<element type="Text" name="Item8">
+	    <font name="times.ttf" size="15" />
+	    <text value="Renderer" />
+        <selectioncolor value="0.5 0.75 0.5" />
+	</element>
+	<element type="Text" name="Item9">
+	    <font name="times.ttf" size="15" />
+	    <text value="Resource" />
+        <selectioncolor value="0.5 0.75 0.5" />
+	</element>
+	<element type="Text" name="Item10">
+	    <font name="times.ttf" size="15" />
+	    <text value="Scene" />
+        <selectioncolor value="0.5 0.75 0.5" />
+	</element>
+	<element type="Text" name="Item11">
+	    <font name="times.ttf" size="15" />
+	    <text value="UI" />
+        <selectioncolor value="0.5 0.75 0.5" />
+	</element>
+    <element type="ListView">
+        <position value="150 100" />
+        <size value="212 112" />
+        <listitem name="Item1" />
+        <listitem name="Item2" />
+        <listitem name="Item3" />
+        <listitem name="Item4" />
+        <listitem name="Item5" />
+        <listitem name="Item6" />
+        <listitem name="Item7" />
+        <listitem name="Item8" />
+        <listitem name="Item9" />
+        <listitem name="Item10" />
+        <listitem name="Item11" />
     </element>
     </element>
     <element type="LineEdit">
     <element type="LineEdit">
         <position value="150 230" />
         <position value="150 230" />

+ 1 - 2
Bin/Data/UI/TestLayout3.xml

@@ -32,9 +32,8 @@
     </element>
     </element>
     <element type="Element">
     <element type="Element">
         <layout orientation="horizontal" horizontal="resizechildren" vertical="resizechildren" spacing="8" />
         <layout orientation="horizontal" horizontal="resizechildren" vertical="resizechildren" spacing="8" />
-    	<element type="Text" name="ScrollViewText" >
+    	<element type="Text" name="ScrollViewText">
 	        <position value="1 0" />
 	        <position value="1 0" />
-	        <minsize value="300 400" />
 	        <font name="times.ttf" size="15" />
 	        <font name="times.ttf" size="15" />
             <text value="At some point the fire had started.\n  There was total chaos. The locks of the wooden barracks had been blown apart by gunfire from shotguns and assault rifles, and the trainees were pouring out in confusion. But the primary target still lay ahead.\n  The administrative building. Where the identities and evaluations of the trainees were stored.\n  That was first class knowledge. If the Agents could bring that knowledge to the public, and cross-reference the records - mostly boys of young age - with lists of known missing persons, then they could prove the existence of SCEPTRE." />
             <text value="At some point the fire had started.\n  There was total chaos. The locks of the wooden barracks had been blown apart by gunfire from shotguns and assault rifles, and the trainees were pouring out in confusion. But the primary target still lay ahead.\n  The administrative building. Where the identities and evaluations of the trainees were stored.\n  That was first class knowledge. If the Agents could bring that knowledge to the public, and cross-reference the records - mostly boys of young age - with lists of known missing persons, then they could prove the existence of SCEPTRE." />
 	        <wordwrap enable="true" />
 	        <wordwrap enable="true" />

+ 2 - 1
Engine/Engine/RegisterUI.cpp

@@ -156,6 +156,7 @@ static void registerScrollBar(asIScriptEngine* engine)
 static void registerScrollView(asIScriptEngine* engine)
 static void registerScrollView(asIScriptEngine* engine)
 {
 {
     registerUIElement<ScrollView>(engine, "ScrollView");
     registerUIElement<ScrollView>(engine, "ScrollView");
+    engine->RegisterObjectMethod("ScrollView", "void setContentElement(UIElement@+)", asMETHOD(ScrollView, setContentElement), asCALL_THISCALL);
     engine->RegisterObjectMethod("ScrollView", "void setViewPosition(const IntVector2& in)", asMETHODPR(ScrollView, setViewPosition, (const IntVector2&), void), asCALL_THISCALL);
     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 setViewPosition(int, int)", asMETHODPR(ScrollView, setViewPosition, (int, int), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("ScrollView", "void setScrollBarsVisible(bool, bool)", asMETHOD(ScrollView, setScrollBarsVisible), asCALL_THISCALL);
     engine->RegisterObjectMethod("ScrollView", "void setScrollBarsVisible(bool, bool)", asMETHOD(ScrollView, setScrollBarsVisible), asCALL_THISCALL);
@@ -163,7 +164,7 @@ static void registerScrollView(asIScriptEngine* engine)
     engine->RegisterObjectMethod("ScrollView", "void setPageStep(float)", asMETHOD(ScrollView, setPageStep), asCALL_THISCALL);
     engine->RegisterObjectMethod("ScrollView", "void setPageStep(float)", asMETHOD(ScrollView, setPageStep), asCALL_THISCALL);
     engine->RegisterObjectMethod("ScrollView", "void setNormalizeScrollStep(bool)", asMETHOD(ScrollView, setNormalizeScrollStep), asCALL_THISCALL);
     engine->RegisterObjectMethod("ScrollView", "void setNormalizeScrollStep(bool)", asMETHOD(ScrollView, setNormalizeScrollStep), asCALL_THISCALL);
     engine->RegisterObjectMethod("ScrollView", "const IntVector2& getViewPosition() const", asMETHOD(ScrollView, getViewPosition), asCALL_THISCALL);
     engine->RegisterObjectMethod("ScrollView", "const IntVector2& getViewPosition() const", asMETHOD(ScrollView, getViewPosition), asCALL_THISCALL);
-    engine->RegisterObjectMethod("ScrollView", "UIElement@+ getElement() const", asMETHOD(ScrollView, getElement), asCALL_THISCALL);
+    engine->RegisterObjectMethod("ScrollView", "UIElement@+ getContentElement() const", asMETHOD(ScrollView, getContentElement), asCALL_THISCALL);
     engine->RegisterObjectMethod("ScrollView", "ScrollBar@+ getHorizontalScrollBar() const", asMETHOD(ScrollView, getHorizontalScrollBar), 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", "ScrollBar@+ getVerticalScrollBar() const", asMETHOD(ScrollView, getVerticalScrollBar), asCALL_THISCALL);
     engine->RegisterObjectMethod("ScrollView", "BorderImage@+ getScrollPanel() const", asMETHOD(ScrollView, getScrollPanel), asCALL_THISCALL);
     engine->RegisterObjectMethod("ScrollView", "BorderImage@+ getScrollPanel() const", asMETHOD(ScrollView, getScrollPanel), asCALL_THISCALL);

+ 3 - 0
Engine/UI/BaseUIElementFactory.cpp

@@ -27,6 +27,7 @@
 #include "CheckBox.h"
 #include "CheckBox.h"
 #include "Cursor.h"
 #include "Cursor.h"
 #include "LineEdit.h"
 #include "LineEdit.h"
+#include "ListView.h"
 #include "MenuItem.h"
 #include "MenuItem.h"
 #include "ScrollBar.h"
 #include "ScrollBar.h"
 #include "ScrollView.h"
 #include "ScrollView.h"
@@ -46,6 +47,8 @@ UIElement* BaseUIElementFactory::createElement(ShortStringHash type, const std::
         return new Cursor(name);
         return new Cursor(name);
     if (type == LineEdit::getTypeStatic())
     if (type == LineEdit::getTypeStatic())
         return new LineEdit(name);
         return new LineEdit(name);
+    if (type == ListView::getTypeStatic())
+        return new ListView(name);
     if (type == MenuItem::getTypeStatic())
     if (type == MenuItem::getTypeStatic())
         return new MenuItem(name);
         return new MenuItem(name);
     if (type == ScrollBar::getTypeStatic())
     if (type == ScrollBar::getTypeStatic())

+ 2 - 2
Engine/UI/Button.cpp

@@ -84,9 +84,9 @@ void Button::update(float timeStep)
 void Button::getBatches(std::vector<UIBatch>& batches, std::vector<UIQuad>& quads, const IntRect& currentScissor)
 void Button::getBatches(std::vector<UIBatch>& batches, std::vector<UIQuad>& quads, const IntRect& currentScissor)
 {
 {
     IntVector2 offset(IntVector2::sZero);
     IntVector2 offset(IntVector2::sZero);
-    if ((mHovering) || (mSelected))
+    if (mHovering)
         offset += mHoverOffset;
         offset += mHoverOffset;
-    if (mPressed)
+    if ((mPressed) || (mSelected))
         offset += mPressedOffset;
         offset += mPressedOffset;
     
     
     BorderImage::getBatches(batches, quads, currentScissor, offset);
     BorderImage::getBatches(batches, quads, currentScissor, offset);

+ 286 - 0
Engine/UI/ListView.cpp

@@ -0,0 +1,286 @@
+//
+// Urho3D Engine
+// Copyright (c) 2008-2011 Lasse Öörni
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "Precompiled.h"
+#include "BorderImage.h"
+#include "InputEvents.h"
+#include "ListView.h"
+#include "Log.h"
+#include "UIEvents.h"
+
+ListView::ListView(const std::string& name) :
+    ScrollView(name),
+    mSelection(M_MAX_UNSIGNED)
+{
+    UIElement* container = new UIElement();
+    container->setEnabled(true);
+    container->setLayout(O_VERTICAL, LM_RESIZECHILDREN, LM_RESIZEELEMENT);
+    mScrollPanel->setLayout(O_HORIZONTAL, LM_RESIZECHILDREN, LM_FREE);
+    setContentElement(container);
+    subscribeToEvent(EVENT_TRYFOCUS, EVENT_HANDLER(ListView, handleTryFocus));
+}
+
+ListView::~ListView()
+{
+}
+
+void ListView::setStyle(const XMLElement& element, ResourceCache* cache)
+{
+    ScrollView::setStyle(element, cache);
+    
+    UIElement* root = getRootElement();
+    XMLElement itemElem = element.getChildElement("listitem");
+    if (root)
+    {
+        while (itemElem)
+        {
+            addItem(root->getChild(itemElem.getString("name"), true));
+            itemElem = itemElem.getNextElement("listitem");
+        }
+    }
+}
+
+void ListView::onKey(int key, int buttons, int qualifiers)
+{
+    // If no selection, can not move with keys
+    if ((mSelection == M_MAX_UNSIGNED) || (mItems.empty()))
+        return;
+    
+    switch (key)
+    {
+    case KEY_UP:
+        if (qualifiers & QUAL_CTRL)
+            setSelection(0);
+        else
+            changeSelection(-1);
+        break;
+        
+    case KEY_DOWN:
+        if (qualifiers & QUAL_CTRL)
+            setSelection(mItems.size() - 1);
+        else
+            changeSelection(1);
+        break;
+        
+    case KEY_PAGEUP:
+        changeSelection((int)-mPageStep);
+        break;
+        
+    case KEY_PAGEDOWN:
+        changeSelection((int)mPageStep);
+        break;
+    
+    case KEY_HOME:
+        setSelection(0);
+        break;
+    
+    case KEY_END:
+        setSelection(mItems.size() - 1);
+        break;
+    }
+}
+
+void ListView::addItem(UIElement* item)
+{
+    if ((!item) || (hasItem(item)))
+        return;
+    
+    // Enable input so that clicking the item can be detected
+    item->setEnabled(true);
+    mItems.push_back(SharedPtr<UIElement>(item));
+    mContentElement->addChild(item);
+}
+
+void ListView::addItem(unsigned index, UIElement* item)
+{
+    if ((!item) || (hasItem(item)))
+        return;
+    if (index > mItems.size())
+        index = mItems.size();
+    
+    // Enable input so that clicking the item can be detected
+    item->setEnabled(true);
+    mItems.insert(mItems.begin() + index, SharedPtr<UIElement>(item));
+    updateList();
+    
+    if (mSelection >= index)
+        setSelection(mSelection + 1);
+}
+
+void ListView::setItem(unsigned index, UIElement* item)
+{
+    if (index >= mItems.size())
+    {
+        LOGERROR("Illegal ListView item index");
+        return;
+    }
+    if ((!item) || (hasItem(item)))
+        return;
+    
+    // Enable input so that clicking the item can be detected
+    item->setEnabled(true);
+    mItems[index] = item;
+    updateList();
+}
+
+void ListView::removeItem(UIElement* item)
+{
+    if (!item)
+        return;
+    
+    for (unsigned i = 0; i < mItems.size(); ++i)
+    {
+        if (mItems[i] == item)
+        {
+            mItems.erase(mItems.begin() + i);
+            mContentElement->removeChild(item);
+            
+            if (mSelection == i)
+                setSelection(M_MAX_UNSIGNED);
+            else if (mSelection > i)
+                setSelection(mSelection - 1);
+            
+            return;
+        }
+    }
+}
+
+void ListView::removeItem(unsigned index)
+{
+    if (index >= mItems.size())
+        return;
+    
+    UIElement* item = mItems[index];
+    mItems.erase(mItems.begin() + index);
+    mContentElement->removeChild(item);
+    
+    if (mSelection == index)
+        setSelection(M_MAX_UNSIGNED);
+    else if (mSelection > index)
+        setSelection(mSelection - 1);
+}
+
+void ListView::removeAllItems()
+{
+    setSelection(M_MAX_UNSIGNED);
+    mItems.clear();
+    updateList();
+}
+
+void ListView::setSelection(unsigned index)
+{
+    if (index >= mItems.size())
+        index = M_MAX_UNSIGNED;
+    
+    for (unsigned i = 0; i < mItems.size(); ++i)
+        mItems[i]->setSelected(i == index);
+    mSelection = index;
+    
+    ensureItemVisibility();
+}
+
+void ListView::changeSelection(int delta)
+{
+    if (mSelection == M_MAX_UNSIGNED)
+        return;
+    
+    int newSelection = clamp((int)mSelection + delta, 0, (int)mItems.size() - 1);
+    setSelection(newSelection);
+}
+
+UIElement* ListView::getItem(unsigned index) const
+{
+    if (index >= mItems.size())
+        return 0;
+    
+    return mItems[index];
+}
+
+bool ListView::hasItem(UIElement* item) const
+{
+    for (unsigned i = 0; i < mItems.size(); ++i)
+    {
+        if (mItems[i] == item)
+            return true;
+    }
+    return false;
+}
+
+UIElement* ListView::getSelectedItem() const
+{
+    return getItem(mSelection);
+}
+
+void ListView::updateList()
+{
+    mContentElement->removeAllChildren();
+    for (unsigned i = 0; i < mItems.size(); ++i)
+        mContentElement->addChild(mItems[i]);
+}
+
+void ListView::ensureItemVisibility()
+{
+    UIElement* selected = getSelectedItem();
+    if (!selected)
+        return;
+    
+    IntVector2 currentOffset = selected->getScreenPosition() - mScrollPanel->getScreenPosition() - mContentElement->getPosition();
+    IntVector2 newView = getViewPosition();
+    const IntRect& clipBorder = mScrollPanel->getClipBorder();
+    IntVector2 windowSize(mScrollPanel->getWidth() - clipBorder.mLeft - clipBorder.mRight, mScrollPanel->getHeight() -
+        clipBorder.mTop - clipBorder.mBottom);
+    
+    if (currentOffset.mX < 0)
+        newView.mX += currentOffset.mX;
+    if (currentOffset.mX + selected->getWidth() > windowSize.mX)
+        newView.mX += currentOffset.mX + selected->getWidth() - windowSize.mX;
+    if (currentOffset.mY < 0)
+        newView.mY += currentOffset.mY;
+    if (currentOffset.mY + selected->getHeight() > windowSize.mY)
+        newView.mY += currentOffset.mY + selected->getHeight() - windowSize.mY;
+    
+    setViewPosition(newView);
+}
+
+void ListView::handleTryFocus(StringHash eventType, VariantMap& eventData)
+{
+    using namespace TryFocus;
+    
+    UIElement* focusElement = static_cast<UIElement*>(eventData[P_ELEMENT].getPtr());
+    
+    // If the scrollpanel itself was clicked, and not the container / items, clear selection
+    if (focusElement == mScrollPanel)
+    {
+        setSelection(M_MAX_UNSIGNED);
+        return;
+    }
+    
+    for (unsigned i = 0; i < mItems.size(); ++i)
+    {
+        if (focusElement == mItems[i])
+        {
+            setSelection(i);
+            return;
+        }
+    }
+}

+ 86 - 0
Engine/UI/ListView.h

@@ -0,0 +1,86 @@
+//
+// Urho3D Engine
+// Copyright (c) 2008-2011 Lasse Öörni
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#ifndef UI_LISTVIEW_H
+#define UI_LISTVIEW_H
+
+#include "ScrollView.h"
+
+class ListView : public ScrollView
+{
+    DEFINE_TYPE(ListView);
+    
+public:
+    //! Construct with name
+    ListView(const std::string& name = std::string());
+    //! Destruct
+    virtual ~ListView();
+    
+    //! Set UI element style from XML data
+    virtual void setStyle(const XMLElement& element, ResourceCache* cache);
+    //! React to a key press
+    virtual void onKey(int key, int buttons, int qualifiers);
+    
+    //! Add item to the end of the list
+    void addItem(UIElement* item);
+    //! Add item at index
+    void addItem(unsigned index, UIElement* item);
+    //! Set item at index, replacing the previous
+    void setItem(unsigned index, UIElement* item);
+    //! Remove specific item
+    void removeItem(UIElement* item);
+    //! Remove item at index
+    void removeItem(unsigned index);
+    //! Remove all elements
+    void removeAllItems();
+    //! Set selected element
+    void setSelection(unsigned index);
+    //! Move selection by a delta. Clamp at list ends
+    void changeSelection(int delta);
+    
+    //! Return item at index
+    UIElement* getItem(unsigned index) const;
+    //! Return whether has a certain item
+    bool hasItem(UIElement* item) const;
+    //! Return selection index, or M_MAX_UNSIGNED if none selected
+    unsigned getSelection() const { return mSelection; }
+    //! Return selected item, or null if none selected
+    UIElement* getSelectedItem() const;
+    
+protected:
+    //! Update content element when list has changed
+    void updateList();
+    //! Ensure full visibility of the selected item
+    void ensureItemVisibility();
+    
+    //! List items
+    std::vector<SharedPtr<UIElement> > mItems;
+    //! Current selection
+    unsigned mSelection;
+    
+private:
+    //! Handle focus change to check for selection change
+    void handleTryFocus(StringHash eventType, VariantMap& eventData);
+};
+
+#endif // UI_LISTVIEW_H

+ 1 - 14
Engine/UI/MenuItem.cpp

@@ -56,20 +56,6 @@ void MenuItem::setStyle(const XMLElement& element, ResourceCache* cache)
         setPopupOffset(element.getChildElement("popupoffset").getIntVector2("value"));
         setPopupOffset(element.getChildElement("popupoffset").getIntVector2("value"));
 }
 }
 
 
-void MenuItem::update(float timeStep)
-{
-    // Keep pressed state if showing the popup
-    if ((!mHovering) && (!mShowPopup))
-        setPressed(false);
-}
-
-void MenuItem::onHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
-{
-    // Keep pressed state if showing the popup
-    setPressed(((buttons & MOUSEB_LEFT) != 0) || (mShowPopup));
-    mHovering = true;
-}
-
 void MenuItem::onClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
 void MenuItem::onClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers)
 {
 {
     setPressed(true);
     setPressed(true);
@@ -141,6 +127,7 @@ void MenuItem::showPopup(bool enable)
     }
     }
     
     
     mShowPopup = enable;
     mShowPopup = enable;
+    mSelected = enable;
 }
 }
 
 
 void MenuItem::handleTryFocus(StringHash eventType, VariantMap& eventData)
 void MenuItem::handleTryFocus(StringHash eventType, VariantMap& eventData)

+ 0 - 4
Engine/UI/MenuItem.h

@@ -38,10 +38,6 @@ class MenuItem : public Button
     
     
     //! Set UI element style from XML data
     //! Set UI element style from XML data
     virtual void setStyle(const XMLElement& element, ResourceCache* cache);
     virtual void setStyle(const XMLElement& element, ResourceCache* cache);
-    //! Perform UI element update
-    virtual void update(float timeStep);
-    //! React to mouse hover
-    virtual void onHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers);
     //! React to mouse click
     //! React to mouse click
     virtual void onClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers);
     virtual void onClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers);
     
     

+ 20 - 20
Engine/UI/ScrollView.cpp

@@ -89,7 +89,7 @@ void ScrollView::setStyle(const XMLElement& element, ResourceCache* cache)
     
     
     UIElement* root = getRootElement();
     UIElement* root = getRootElement();
     if ((root) && (element.hasChildElement("contentelement")))
     if ((root) && (element.hasChildElement("contentelement")))
-        setElement(root->getChild(element.getChildElement("contentelement").getString("name"), true));
+        setContentElement(root->getChild(element.getChildElement("contentelement").getString("name"), true));
     
     
     // Set the scrollbar orientations again and perform size update now that the style is known
     // Set the scrollbar orientations again and perform size update now that the style is known
     mHorizontalScrollBar->setOrientation(O_HORIZONTAL);
     mHorizontalScrollBar->setOrientation(O_HORIZONTAL);
@@ -102,7 +102,7 @@ void ScrollView::onKey(int key, int buttons, int qualifiers)
     switch (key)
     switch (key)
     {
     {
     case KEY_LEFT:
     case KEY_LEFT:
-        if (mHorizontalScrollBar)
+        if (mHorizontalScrollBar->isVisible())
         {
         {
             if (qualifiers & QUAL_CTRL)
             if (qualifiers & QUAL_CTRL)
                 mHorizontalScrollBar->setValue(0.0f);
                 mHorizontalScrollBar->setValue(0.0f);
@@ -112,7 +112,7 @@ void ScrollView::onKey(int key, int buttons, int qualifiers)
         break;
         break;
         
         
     case KEY_RIGHT:
     case KEY_RIGHT:
-        if (mHorizontalScrollBar)
+        if (mHorizontalScrollBar->isVisible())
         {
         {
             if (qualifiers & QUAL_CTRL)
             if (qualifiers & QUAL_CTRL)
                 mHorizontalScrollBar->setValue(mHorizontalScrollBar->getRange());
                 mHorizontalScrollBar->setValue(mHorizontalScrollBar->getRange());
@@ -122,7 +122,7 @@ void ScrollView::onKey(int key, int buttons, int qualifiers)
         break;
         break;
         
         
     case KEY_UP:
     case KEY_UP:
-        if (mVerticalScrollBar)
+        if (mVerticalScrollBar->isVisible())
         {
         {
             if (qualifiers & QUAL_CTRL)
             if (qualifiers & QUAL_CTRL)
                 mVerticalScrollBar->setValue(0.0f);
                 mVerticalScrollBar->setValue(0.0f);
@@ -132,7 +132,7 @@ void ScrollView::onKey(int key, int buttons, int qualifiers)
         break;
         break;
         
         
     case KEY_DOWN:
     case KEY_DOWN:
-        if (mVerticalScrollBar)
+        if (mVerticalScrollBar->isVisible())
         {
         {
             if (qualifiers & QUAL_CTRL)
             if (qualifiers & QUAL_CTRL)
                 mVerticalScrollBar->setValue(mVerticalScrollBar->getRange());
                 mVerticalScrollBar->setValue(mVerticalScrollBar->getRange());
@@ -142,22 +142,22 @@ void ScrollView::onKey(int key, int buttons, int qualifiers)
         break;
         break;
         
         
     case KEY_PAGEUP:
     case KEY_PAGEUP:
-        if (mVerticalScrollBar)
+        if (mVerticalScrollBar->isVisible())
             mVerticalScrollBar->changeValue(-mPageStep);
             mVerticalScrollBar->changeValue(-mPageStep);
         break;
         break;
         
         
     case KEY_PAGEDOWN:
     case KEY_PAGEDOWN:
-        if (mVerticalScrollBar)
+        if (mVerticalScrollBar->isVisible())
             mVerticalScrollBar->changeValue(mPageStep);
             mVerticalScrollBar->changeValue(mPageStep);
         break;
         break;
     
     
     case KEY_HOME:
     case KEY_HOME:
-        if (mVerticalScrollBar)
+        if (mVerticalScrollBar->isVisible())
             mVerticalScrollBar->setValue(0.0f);
             mVerticalScrollBar->setValue(0.0f);
         break;
         break;
     
     
     case KEY_END:
     case KEY_END:
-        if (mVerticalScrollBar)
+        if (mVerticalScrollBar->isVisible())
             mVerticalScrollBar->setValue(mVerticalScrollBar->getRange());
             mVerticalScrollBar->setValue(mVerticalScrollBar->getRange());
         break;
         break;
     }
     }
@@ -178,21 +178,21 @@ void ScrollView::onResize()
     updateViewSize();
     updateViewSize();
 }
 }
 
 
-void ScrollView::setElement(UIElement* element)
+void ScrollView::setContentElement(UIElement* element)
 {
 {
-    if (element == mElement)
+    if (element == mContentElement)
         return;
         return;
     
     
-    if (mElement)
+    if (mContentElement)
     {
     {
-        mScrollPanel->removeChild(mElement);
-        unsubscribeFromEvent(mElement, EVENT_RESIZED);
+        mScrollPanel->removeChild(mContentElement);
+        unsubscribeFromEvent(mContentElement, EVENT_RESIZED);
     }
     }
-    mElement = element;
-    if (mElement)
+    mContentElement = element;
+    if (mContentElement)
     {
     {
-        mScrollPanel->addChild(mElement);
-        subscribeToEvent(mElement, EVENT_RESIZED, EVENT_HANDLER(ScrollView, handleElementResized));
+        mScrollPanel->addChild(mContentElement);
+        subscribeToEvent(mContentElement, EVENT_RESIZED, EVENT_HANDLER(ScrollView, handleElementResized));
     }
     }
     
     
     updateViewSize();
     updateViewSize();
@@ -255,8 +255,8 @@ bool ScrollView::getNormalizeScrollStep() const
 void ScrollView::updateViewSize()
 void ScrollView::updateViewSize()
 {
 {
     IntVector2 size(IntVector2::sZero);
     IntVector2 size(IntVector2::sZero);
-    if (mElement)
-        size = mElement->getSize();
+    if (mContentElement)
+        size = mContentElement->getSize();
     
     
     mViewSize.mX = max(size.mX, mScrollPanel->getWidth());
     mViewSize.mX = max(size.mX, mScrollPanel->getWidth());
     mViewSize.mY = max(size.mY, mScrollPanel->getHeight());
     mViewSize.mY = max(size.mY, mScrollPanel->getHeight());

+ 3 - 3
Engine/UI/ScrollView.h

@@ -48,7 +48,7 @@ public:
     virtual void onResize();
     virtual void onResize();
     
     
     //! Set content element
     //! Set content element
-    void setElement(UIElement* element);
+    void setContentElement(UIElement* element);
     //! Set view offset from the top-left corner
     //! Set view offset from the top-left corner
     void setViewPosition(const IntVector2& position);
     void setViewPosition(const IntVector2& position);
     //! Set view offset from the top-left corner
     //! Set view offset from the top-left corner
@@ -65,7 +65,7 @@ public:
     //! Return view offset from the top-left corner
     //! Return view offset from the top-left corner
     const IntVector2& getViewPosition() const { return mViewPosition; }
     const IntVector2& getViewPosition() const { return mViewPosition; }
     //! Return content element
     //! Return content element
-    UIElement* getElement() const { return mElement; }
+    UIElement* getContentElement() const { return mContentElement; }
     //! Return horizontal scroll bar
     //! Return horizontal scroll bar
     ScrollBar* getHorizontalScrollBar() const { return mHorizontalScrollBar; }
     ScrollBar* getHorizontalScrollBar() const { return mHorizontalScrollBar; }
     //! Return vertical scroll bar
     //! Return vertical scroll bar
@@ -92,7 +92,7 @@ protected:
     void updateView(const IntVector2& position);
     void updateView(const IntVector2& position);
     
     
     //! Content element
     //! Content element
-    SharedPtr<UIElement> mElement;
+    SharedPtr<UIElement> mContentElement;
     //! Horizontal scroll bar
     //! Horizontal scroll bar
     SharedPtr<ScrollBar> mHorizontalScrollBar;
     SharedPtr<ScrollBar> mHorizontalScrollBar;
     //! Vertical scroll bar
     //! Vertical scroll bar

+ 9 - 7
Engine/UI/Text.cpp

@@ -94,20 +94,20 @@ void Text::setStyle(const XMLElement& element, ResourceCache* cache)
 
 
 void Text::getBatches(std::vector<UIBatch>& batches, std::vector<UIQuad>& quads, const IntRect& currentScissor)
 void Text::getBatches(std::vector<UIBatch>& batches, std::vector<UIQuad>& quads, const IntRect& currentScissor)
 {
 {
-    // Hovering batch
-    if (((mHovering) || (mSelected)) && (mHoverColor.mA > 0.0f))
+    // Hovering or whole selection batch
+    if ((mHovering && (mHoverColor.mA > 0.0f)) || (mSelected && (mSelectionColor.mA > 0.0f)))
     {
     {
         UIBatch batch;
         UIBatch batch;
         batch.begin(&quads);
         batch.begin(&quads);
         batch.mBlendMode = BLEND_ALPHA;
         batch.mBlendMode = BLEND_ALPHA;
         batch.mScissor = currentScissor;
         batch.mScissor = currentScissor;
         batch.mTexture = 0;
         batch.mTexture = 0;
-        batch.addQuad(*this, 0, 0, getWidth(), getHeight(), 0, 0, 0, 0, mHoverColor);
+        batch.addQuad(*this, 0, 0, getWidth(), getHeight(), 0, 0, 0, 0, mSelected ? mSelectionColor : mHoverColor);
         UIBatch::addOrMerge(batch, batches);
         UIBatch::addOrMerge(batch, batches);
     }
     }
     
     
-    // Selection batch
-    if ((mSelectionLength) && (mCharSizes.size() >= mSelectionStart + mSelectionLength) && (mSelectionColor.mA > 0.0f))
+    // Partial Selection batch
+    if ((!mSelected) && (mSelectionLength) && (mCharSizes.size() >= mSelectionStart + mSelectionLength) && (mSelectionColor.mA > 0.0f))
     {
     {
         UIBatch batch;
         UIBatch batch;
         batch.begin(&quads);
         batch.begin(&quads);
@@ -430,9 +430,11 @@ void Text::updateText(bool inResize)
         mCharPositions[mText.length()] = IntVector2(x, y);
         mCharPositions[mText.length()] = IntVector2(x, y);
     }
     }
     
     
-    // Resize self only when not using wordwrap
+    // Set minimum size to correspond to the text size
     if (!mWordwrap)
     if (!mWordwrap)
-        setSize(width, height);
+        setMinSize(width, height);
+    else
+        setMinSize(0, height);
 }
 }
 
 
 void Text::validateSelection()
 void Text::validateSelection()

+ 1 - 1
Engine/UI/UI.cpp

@@ -118,7 +118,7 @@ void UI::setFocusElement(UIElement* element)
     eventData[P_ELEMENT] = (void*)element;
     eventData[P_ELEMENT] = (void*)element;
     sendEvent(EVENT_TRYFOCUS, eventData);
     sendEvent(EVENT_TRYFOCUS, eventData);
     
     
-    // The event receivers may divert the focus
+    // The event receivers may optionally divert the focus
     element = static_cast<UIElement*>(eventData[P_ELEMENT].getPtr());
     element = static_cast<UIElement*>(eventData[P_ELEMENT].getPtr());
     
     
     if (element)
     if (element)

+ 31 - 14
Engine/UI/UIElement.cpp

@@ -376,14 +376,15 @@ void UIElement::setSize(const IntVector2& size)
             
             
             markDirty();
             markDirty();
             onResize();
             onResize();
+            updateLayout();
             
             
             using namespace Resized;
             using namespace Resized;
             
             
             VariantMap eventData;
             VariantMap eventData;
             eventData[P_ELEMENT] = (void*)this;
             eventData[P_ELEMENT] = (void*)this;
-            sendEvent(mFocus ? EVENT_FOCUSED : EVENT_DEFOCUSED, eventData);
-            
-            updateLayout();
+            eventData[P_WIDTH] = mSize.mX;
+            eventData[P_HEIGHT] = mSize.mY;
+            sendEvent(EVENT_RESIZED, eventData);
         }
         }
     }
     }
     
     
@@ -422,12 +423,12 @@ void UIElement::setMinSize(int width, int height)
 
 
 void UIElement::setMinWidth(int width)
 void UIElement::setMinWidth(int width)
 {
 {
-    setMaxSize(IntVector2(width, mMinSize.mY));
+    setMinSize(IntVector2(width, mMinSize.mY));
 }
 }
 
 
 void UIElement::setMinHeight(int height)
 void UIElement::setMinHeight(int height)
 {
 {
-    setMaxSize(IntVector2(mMinSize.mX, height));
+    setMinSize(IntVector2(mMinSize.mX, height));
 }
 }
 
 
 void UIElement::setMaxSize(const IntVector2& maxSize)
 void UIElement::setMaxSize(const IntVector2& maxSize)
@@ -645,6 +646,7 @@ void UIElement::updateLayout()
     if (mLayoutOrientation == O_HORIZONTAL)
     if (mLayoutOrientation == O_HORIZONTAL)
     {
     {
         int maxChildHeight = 0;
         int maxChildHeight = 0;
+        int maxChildMinHeight = 0;
         
         
         if (mHorizontalLayoutMode == LM_RESIZEELEMENT)
         if (mHorizontalLayoutMode == LM_RESIZEELEMENT)
         {
         {
@@ -654,11 +656,14 @@ void UIElement::updateLayout()
                     continue;
                     continue;
                 sizes.push_back(mChildren[i]->getWidth());
                 sizes.push_back(mChildren[i]->getWidth());
                 maxChildHeight = max(maxChildHeight, mChildren[i]->getHeight());
                 maxChildHeight = max(maxChildHeight, mChildren[i]->getHeight());
+                maxChildMinHeight = max(maxChildMinHeight, mChildren[i]->getMinHeight());
             }
             }
             
             
+            if (mVerticalLayoutMode == LM_RESIZEELEMENT)
+                setMinHeight(maxChildMinHeight + mLayoutBorder.mTop + mLayoutBorder.mBottom);
             setSize(calculateLayoutParentSize(sizes, mLayoutBorder.mLeft, mLayoutBorder.mRight, mLayoutSpacing), mVerticalLayoutMode ==
             setSize(calculateLayoutParentSize(sizes, mLayoutBorder.mLeft, mLayoutBorder.mRight, mLayoutSpacing), mVerticalLayoutMode ==
                 LM_RESIZEELEMENT ? maxChildHeight + mLayoutBorder.mTop + mLayoutBorder.mBottom : getHeight());
                 LM_RESIZEELEMENT ? maxChildHeight + mLayoutBorder.mTop + mLayoutBorder.mBottom : getHeight());
-           
+            
             int position = mLayoutBorder.mLeft;
             int position = mLayoutBorder.mLeft;
             for (unsigned i = 0; i < mChildren.size(); ++i)
             for (unsigned i = 0; i < mChildren.size(); ++i)
             {
             {
@@ -684,24 +689,28 @@ void UIElement::updateLayout()
                 minSizes.push_back(mChildren[i]->getMinWidth());
                 minSizes.push_back(mChildren[i]->getMinWidth());
                 maxSizes.push_back(mChildren[i]->getMaxWidth());
                 maxSizes.push_back(mChildren[i]->getMaxWidth());
                 maxChildHeight = max(maxChildHeight, mChildren[i]->getHeight());
                 maxChildHeight = max(maxChildHeight, mChildren[i]->getHeight());
+                maxChildMinHeight = max(maxChildMinHeight, mChildren[i]->getMinHeight());
             }
             }
             
             
             if (mVerticalLayoutMode == LM_RESIZEELEMENT)
             if (mVerticalLayoutMode == LM_RESIZEELEMENT)
+            {
+                setMinHeight(maxChildMinHeight + mLayoutBorder.mTop + mLayoutBorder.mBottom);
                 setHeight(maxChildHeight + mLayoutBorder.mTop + mLayoutBorder.mBottom);
                 setHeight(maxChildHeight + mLayoutBorder.mTop + mLayoutBorder.mBottom);
+            }
             
             
             calculateLayout(positions, sizes, minSizes, maxSizes, getWidth(), mLayoutBorder.mLeft, mLayoutBorder.mRight,
             calculateLayout(positions, sizes, minSizes, maxSizes, getWidth(), mLayoutBorder.mLeft, mLayoutBorder.mRight,
                 mLayoutSpacing);
                 mLayoutSpacing);
             
             
-            unsigned idx = 0;
+            unsigned j = 0;
             for (unsigned i = 0; i < mChildren.size(); ++i)
             for (unsigned i = 0; i < mChildren.size(); ++i)
             {
             {
                 if (!mChildren[i]->isVisible())
                 if (!mChildren[i]->isVisible())
                     continue;
                     continue;
                 mChildren[i]->setHorizontalAlignment(HA_LEFT);
                 mChildren[i]->setHorizontalAlignment(HA_LEFT);
-                mChildren[i]->setPosition(positions[idx], getLayoutChildPosition(mChildren[i]).mY);
-                mChildren[i]->setSize(sizes[idx], mVerticalLayoutMode == LM_RESIZECHILDREN ? getHeight() - mLayoutBorder.mTop -
+                mChildren[i]->setPosition(positions[j], getLayoutChildPosition(mChildren[i]).mY);
+                mChildren[i]->setSize(sizes[j], mVerticalLayoutMode == LM_RESIZECHILDREN ? getHeight() - mLayoutBorder.mTop -
                     mLayoutBorder.mBottom : mChildren[i]->getHeight());
                     mLayoutBorder.mBottom : mChildren[i]->getHeight());
-                ++idx;
+                ++j;
             }
             }
         }
         }
     }
     }
@@ -709,6 +718,7 @@ void UIElement::updateLayout()
     if (mLayoutOrientation == O_VERTICAL)
     if (mLayoutOrientation == O_VERTICAL)
     {
     {
         int maxChildWidth = 0;
         int maxChildWidth = 0;
+        int maxChildMinWidth = 0;
         
         
         if (mVerticalLayoutMode == LM_RESIZEELEMENT)
         if (mVerticalLayoutMode == LM_RESIZEELEMENT)
         {
         {
@@ -718,8 +728,11 @@ void UIElement::updateLayout()
                     continue;
                     continue;
                 sizes.push_back(mChildren[i]->getHeight());
                 sizes.push_back(mChildren[i]->getHeight());
                 maxChildWidth = max(maxChildWidth, mChildren[i]->getWidth());
                 maxChildWidth = max(maxChildWidth, mChildren[i]->getWidth());
+                maxChildMinWidth = max(maxChildMinWidth, mChildren[i]->getMinWidth());
             }
             }
             
             
+            if (mHorizontalLayoutMode == LM_RESIZEELEMENT)
+                setMinWidth(maxChildMinWidth + mLayoutBorder.mLeft + mLayoutBorder.mRight);
             setSize(mHorizontalLayoutMode == LM_RESIZEELEMENT ? maxChildWidth + mLayoutBorder.mLeft + mLayoutBorder.mRight : getWidth(),
             setSize(mHorizontalLayoutMode == LM_RESIZEELEMENT ? maxChildWidth + mLayoutBorder.mLeft + mLayoutBorder.mRight : getWidth(),
                 calculateLayoutParentSize(sizes, mLayoutBorder.mTop, mLayoutBorder.mBottom, mLayoutSpacing));
                 calculateLayoutParentSize(sizes, mLayoutBorder.mTop, mLayoutBorder.mBottom, mLayoutSpacing));
             
             
@@ -747,24 +760,28 @@ void UIElement::updateLayout()
                 minSizes.push_back(mChildren[i]->getMinHeight());
                 minSizes.push_back(mChildren[i]->getMinHeight());
                 maxSizes.push_back(mChildren[i]->getMaxHeight());
                 maxSizes.push_back(mChildren[i]->getMaxHeight());
                 maxChildWidth = max(maxChildWidth, mChildren[i]->getWidth());
                 maxChildWidth = max(maxChildWidth, mChildren[i]->getWidth());
+                maxChildMinWidth = max(maxChildMinWidth, mChildren[i]->getMinWidth());
             }
             }
             
             
             if (mHorizontalLayoutMode == LM_RESIZEELEMENT)
             if (mHorizontalLayoutMode == LM_RESIZEELEMENT)
+            {
+                setMinWidth(maxChildMinWidth + mLayoutBorder.mLeft + mLayoutBorder.mRight);
                 setWidth(maxChildWidth + mLayoutBorder.mLeft + mLayoutBorder.mRight);
                 setWidth(maxChildWidth + mLayoutBorder.mLeft + mLayoutBorder.mRight);
+            }
             
             
             calculateLayout(positions, sizes, minSizes, maxSizes, getHeight(), mLayoutBorder.mTop, mLayoutBorder.mBottom,
             calculateLayout(positions, sizes, minSizes, maxSizes, getHeight(), mLayoutBorder.mTop, mLayoutBorder.mBottom,
                 mLayoutSpacing);
                 mLayoutSpacing);
             
             
-            unsigned idx = 0;
+            unsigned j = 0;
             for (unsigned i = 0; i < mChildren.size(); ++i)
             for (unsigned i = 0; i < mChildren.size(); ++i)
             {
             {
                 if (!mChildren[i]->isVisible())
                 if (!mChildren[i]->isVisible())
                     continue;
                     continue;
                 mChildren[i]->setVerticalAlignment(VA_TOP);
                 mChildren[i]->setVerticalAlignment(VA_TOP);
-                mChildren[i]->setPosition(getLayoutChildPosition(mChildren[i]).mX, positions[idx]);
+                mChildren[i]->setPosition(getLayoutChildPosition(mChildren[i]).mX, positions[j]);
                 mChildren[i]->setSize(mHorizontalLayoutMode == LM_RESIZECHILDREN ? getWidth() - mLayoutBorder.mLeft -
                 mChildren[i]->setSize(mHorizontalLayoutMode == LM_RESIZECHILDREN ? getWidth() - mLayoutBorder.mLeft -
-                    mLayoutBorder.mRight : mChildren[i]->getWidth(), sizes[idx]);
-                ++idx;
+                    mLayoutBorder.mRight : mChildren[i]->getWidth(), sizes[j]);
+                ++j;
             }
             }
         }
         }
     }
     }

+ 1 - 1
Engine/UI/UIElement.h

@@ -198,7 +198,7 @@ public:
     void setFocusMode(FocusMode mode);
     void setFocusMode(FocusMode mode);
     //! Set whether is focused. Usually called by UI
     //! Set whether is focused. Usually called by UI
     void setFocus(bool enable);
     void setFocus(bool enable);
-    //! Set selected mode. Actual meaning is element dependent, but is visually same as a constant hover
+    //! Set selected mode. Actual meaning is element dependent, for example constant hover or pressed effect
     void setSelected(bool enable);
     void setSelected(bool enable);
     //! Set whether is visible
     //! Set whether is visible
     void setVisible(bool enable);
     void setVisible(bool enable);

+ 1 - 1
Engine/UI/UIEvents.h

@@ -31,7 +31,7 @@ DEFINE_EVENT(EVENT_RESIZED, Resized)
 {
 {
     EVENT_PARAM(P_ELEMENT, Element);            // UIElement pointer
     EVENT_PARAM(P_ELEMENT, Element);            // UIElement pointer
     EVENT_PARAM(P_WIDTH, Width);                // int
     EVENT_PARAM(P_WIDTH, Width);                // int
-    EVENT_PARAM(P_Height, Height);              // int
+    EVENT_PARAM(P_HEIGHT, Height);              // int
 }
 }
 
 
 //! UI element visibility changed
 //! UI element visibility changed