Jelajahi Sumber

Scrolling using the scroll wheel works

Marko Pintera 12 tahun lalu
induk
melakukan
e7f8b52c0b

+ 0 - 3
BansheeEngine/Include/BsGUIElement.h

@@ -90,7 +90,6 @@ namespace BansheeEngine
 		void _setWidth(CM::UINT32 width);
 		void _setHeight(CM::UINT32 height);
 		void _setClipRect(const CM::Rect& clipRect);
-		void _setParentLayout(GUILayout* layout) { mParentLayout = layout; }
 		virtual void _setFocus(bool focus) {}
 		
 		CM::UINT32 _getWidth() const { return mWidth; }
@@ -98,7 +97,6 @@ namespace BansheeEngine
 		CM::Int2 _getOffset() const { return mOffset; }
 		virtual CM::UINT32 _getRenderElementDepth(CM::UINT32 renderElementIdx) const { return _getDepth(); }
 		Type _getType() const { return GUIElementBase::Type::Element; }
-		GUILayout* _getParentLayout() const { return mParentLayout; }
 
 		const CM::Rect& _getBounds() const { return mBounds; }
 		CM::UINT32 _getDepth() const { return mDepth; }
@@ -120,7 +118,6 @@ namespace BansheeEngine
 		static GUILayoutOptions getDefaultLayoutOptions(const GUIElementStyle* style);
 
 		GUIWidget& mParent;
-		GUILayout* mParentLayout;
 		GUILayoutOptions mLayoutOptions;
 		CM::Rect mBounds;
 

+ 8 - 4
BansheeEngine/Include/BsGUIElementBase.h

@@ -35,9 +35,12 @@ namespace BansheeEngine
 		virtual void _updateLayoutInternal(CM::INT32 x, CM::INT32 y, CM::UINT32 width, CM::UINT32 height,
 			CM::Rect clipRect, CM::UINT8 widgetDepth, CM::UINT16 areaDepth);
 
+		void _setParent(GUIElementBase* parent) { mParentElement = parent; }
+
 		virtual CM::UINT32 _getOptimalWidth() const = 0;
 		virtual CM::UINT32 _getOptimalHeight() const = 0;
 		virtual Type _getType() const = 0;
+		GUIElementBase* _getParent() const { return mParentElement; }
 
 		void _markAsClean() { mIsDirty = 0; }
 		virtual void _setFocus(bool focus) {}
@@ -57,12 +60,13 @@ namespace BansheeEngine
 		 */
 		void markMeshAsDirty();
 
-		GUILayout& addLayoutXInternal();
-		GUILayout& addLayoutYInternal();
+		GUILayout& addLayoutXInternal(GUIElementBase* parent);
+		GUILayout& addLayoutYInternal(GUIElementBase* parent);
 		void removeLayoutInternal(GUILayout& layout);
-		GUILayout& insertLayoutXInternal(CM::UINT32 idx);
-		GUILayout& insertLayoutYInternal(CM::UINT32 idx);
+		GUILayout& insertLayoutXInternal(GUIElementBase* parent, CM::UINT32 idx);
+		GUILayout& insertLayoutYInternal(GUIElementBase* parent, CM::UINT32 idx);
 
+		GUIElementBase* mParentElement;
 		CM::Vector<GUIElementBase*>::type mChildren;	
 		CM::UINT8 mIsDirty;
 	};

+ 4 - 4
BansheeEngine/Include/BsGUILayout.h

@@ -16,11 +16,11 @@ namespace BansheeEngine
 		void removeElement(GUIElement* element);
 		void insertElement(CM::UINT32 idx, GUIElement* element);
 
-		GUILayout& addLayoutX() { return addLayoutXInternal(); }
-		GUILayout& addLayoutY() { return addLayoutYInternal(); }
+		GUILayout& addLayoutX() { return addLayoutXInternal(this); }
+		GUILayout& addLayoutY() { return addLayoutYInternal(this); }
 		void removeLayout(GUILayout& layout) { removeLayoutInternal(layout); }
-		GUILayout& insertLayoutX(CM::UINT32 idx) { return insertLayoutXInternal(idx); }
-		GUILayout& insertLayoutY(CM::UINT32 idx) { return insertLayoutYInternal(idx); }
+		GUILayout& insertLayoutX(CM::UINT32 idx) { return insertLayoutXInternal(this, idx); }
+		GUILayout& insertLayoutY(CM::UINT32 idx) { return insertLayoutYInternal(this, idx); }
 
 		GUIFixedSpace& addSpace(CM::UINT32 size);
 		void removeSpace(GUIFixedSpace& space);

+ 1 - 0
BansheeEngine/Include/BsGUIScrollArea.h

@@ -66,6 +66,7 @@ namespace BansheeEngine
 
 		static const CM::UINT32 ScrollBarWidth;
 		static const CM::UINT32 MinHandleSize;
+		static const CM::UINT32 WheelScrollAmount;
 
 		virtual bool mouseEvent(const GUIMouseEvent& ev);
 

+ 6 - 0
BansheeEngine/Include/BsGUIScrollBar.h

@@ -15,6 +15,12 @@ namespace BansheeEngine
 		void setHandleSize(CM::UINT32 size);
 		void setScrollPos(float pct);
 
+		/**
+		 * @brief	Scrolls the contents by some amount. Amount is specified in the percentage
+		 * 			of the entire scrollable area. Values out of range will be clamped.
+		 */
+		void scroll(float amount);
+
 		CM::UINT32 getMaxHandleSize() const;
 		CM::UINT32 getScrollableSize() const;
 	protected:

+ 6 - 3
BansheeEngine/Source/BsGUIElement.cpp

@@ -10,15 +10,18 @@ namespace BansheeEngine
 {
 	GUIElement::GUIElement(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions, bool acceptsKeyboardFocus)
 		:mParent(parent), mLayoutOptions(layoutOptions), mWidth(0), mHeight(0), mDepth(0), mStyle(style),
-		mAcceptsKeyboardFocus(acceptsKeyboardFocus), mParentLayout(nullptr)
+		mAcceptsKeyboardFocus(acceptsKeyboardFocus)
 	{
 		mParent.registerElement(this);
 	}
 
 	GUIElement::~GUIElement()
 	{
-		if(mParentLayout != nullptr)
-			mParentLayout->removeElement(this);
+		if(mParentElement != nullptr && mParentElement->_getType() == GUIElementBase::Type::Layout)
+		{
+			GUILayout* layoutParent = static_cast<GUILayout*>(mParentElement);
+			layoutParent->removeElement(this);
+		}
 	}
 
 	void GUIElement::updateRenderElements()

+ 10 - 6
BansheeEngine/Source/BsGUIElementBase.cpp

@@ -10,7 +10,7 @@ using namespace CamelotFramework;
 namespace BansheeEngine
 {
 	GUIElementBase::GUIElementBase()
-		:mIsDirty(true)
+		:mIsDirty(true), mParentElement(nullptr)
 	{
 
 	}
@@ -25,7 +25,7 @@ namespace BansheeEngine
 			else
 			{
 				GUIElement* element = static_cast<GUIElement*>(child);
-				element->_setParentLayout(nullptr);
+				element->_setParent(nullptr);
 			}
 		}
 	}
@@ -81,9 +81,10 @@ namespace BansheeEngine
 		}
 	}
 
-	GUILayout& GUIElementBase::addLayoutXInternal()
+	GUILayout& GUIElementBase::addLayoutXInternal(GUIElementBase* parent)
 	{
 		GUILayoutX* entry = cm_new<GUILayoutX, PoolAlloc>();
+		entry->_setParent(parent);
 
 		mChildren.push_back(entry);
 		markContentAsDirty();
@@ -91,9 +92,10 @@ namespace BansheeEngine
 		return *entry;
 	}
 
-	GUILayout& GUIElementBase::addLayoutYInternal()
+	GUILayout& GUIElementBase::addLayoutYInternal(GUIElementBase* parent)
 	{
 		GUILayoutY* entry = cm_new<GUILayoutY, PoolAlloc>();
+		entry->_setParent(parent);
 
 		mChildren.push_back(entry);
 		markContentAsDirty();
@@ -123,12 +125,13 @@ namespace BansheeEngine
 			CM_EXCEPT(InvalidParametersException, "Provided element is not a part of this layout.");
 	}
 
-	GUILayout& GUIElementBase::insertLayoutXInternal(UINT32 idx)
+	GUILayout& GUIElementBase::insertLayoutXInternal(GUIElementBase* parent, UINT32 idx)
 	{
 		if(idx < 0 || idx >= (UINT32)mChildren.size())
 			CM_EXCEPT(InvalidParametersException, "Index out of range: " + toString(idx) + ". Valid range: 0 .. " + toString((UINT32)mChildren.size()));
 
 		GUILayoutX* entry = cm_new<GUILayoutX, PoolAlloc>();
+		entry->_setParent(parent);
 
 		mChildren.insert(mChildren.begin() + idx, entry);
 		markContentAsDirty();
@@ -136,12 +139,13 @@ namespace BansheeEngine
 		return *entry;
 	}
 
-	GUILayout& GUIElementBase::insertLayoutYInternal(UINT32 idx)
+	GUILayout& GUIElementBase::insertLayoutYInternal(GUIElementBase* parent, UINT32 idx)
 	{
 		if(idx < 0 || idx >= (UINT32)mChildren.size())
 			CM_EXCEPT(InvalidParametersException, "Index out of range: " + toString(idx) + ". Valid range: 0 .. " + toString((UINT32)mChildren.size()));
 
 		GUILayoutY* entry = cm_new<GUILayoutY, PoolAlloc>();
+		entry->_setParent(parent);
 
 		mChildren.insert(mChildren.begin() + idx, entry);
 		markContentAsDirty();

+ 14 - 6
BansheeEngine/Source/BsGUILayout.cpp

@@ -22,10 +22,14 @@ namespace BansheeEngine
 
 	void GUILayout::addElement(GUIElement* element)
 	{
-		if(element->_getParentLayout() != nullptr)
-			element->_getParentLayout()->removeElement(element);
+		GUIElementBase* parentElement = element->_getParent();
+		if(parentElement != nullptr && parentElement->_getType() == GUIElementBase::Type::Layout)
+		{
+			GUILayout* layoutParent = static_cast<GUILayout*>(parentElement);
+			layoutParent->removeElement(element);
+		}
 
-		element->_setParentLayout(this);
+		element->_setParent(this);
 		mChildren.push_back(element);
 
 		markContentAsDirty();
@@ -57,10 +61,14 @@ namespace BansheeEngine
 		if(idx < 0 || idx >= (UINT32)mChildren.size())
 			CM_EXCEPT(InvalidParametersException, "Index out of range: " + toString(idx) + ". Valid range: 0 .. " + toString((UINT32)mChildren.size()));
 
-		if(element->_getParentLayout() != nullptr)
-			element->_getParentLayout()->removeElement(element);
+		GUIElementBase* parentElement = element->_getParent();
+		if(parentElement != nullptr && parentElement->_getType() == GUIElementBase::Type::Layout)
+		{
+			GUILayout* layoutParent = static_cast<GUILayout*>(parentElement);
+			layoutParent->removeElement(element);
+		}
 
-		element->_setParentLayout(this);
+		element->_setParent(this);
 		mChildren.insert(mChildren.begin() + idx, element);
 		
 		markContentAsDirty();

+ 11 - 3
BansheeEngine/Source/BsGUIScrollArea.cpp

@@ -16,12 +16,13 @@ namespace BansheeEngine
 {
 	const UINT32 GUIScrollArea::ScrollBarWidth = 8;
 	const UINT32 GUIScrollArea::MinHandleSize = 4;
+	const UINT32 GUIScrollArea::WheelScrollAmount = 50;
 
 	GUIScrollArea::GUIScrollArea(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions)
 		:GUIElement(parent, style, layoutOptions), mVertScroll(nullptr), mHorzScroll(nullptr), mVertOffset(0), mHorzOffset(0),
 		mContentWidth(0), mContentHeight(0), mClippedContentWidth(0), mClippedContentHeight(0)
 	{
-		mContentLayout = &addLayoutYInternal();
+		mContentLayout = &addLayoutYInternal(this);
 	}
 
 	GUIScrollArea::~GUIScrollArea()
@@ -229,8 +230,15 @@ namespace BansheeEngine
 	{
 		if(ev.getType() == GUIMouseEventType::MouseWheelScroll)
 		{
-			// TODO
-			int a = 5;
+			// Mouse wheel only scrolls on the Y axis
+			if(mVertScroll != nullptr)
+			{
+				UINT32 scrollableHeight = (UINT32)std::max(0, INT32(mContentWidth) - INT32(mClippedContentHeight));
+				float additionalScroll = (float)WheelScrollAmount / scrollableHeight;
+
+				mVertScroll->scroll(additionalScroll * ev.getWheelScrollAmount());
+				return true;
+			}
 		}
 
 		return false;

+ 9 - 9
BansheeEngine/Source/BsGUIScrollBar.cpp

@@ -23,7 +23,7 @@ namespace BansheeEngine
 
 		if(mHorizontal)
 		{
-			mLayout = &addLayoutXInternal();
+			mLayout = &addLayoutXInternal(this);
 
 			mUpBtn = GUIButton::create(parent, L"", parent.getSkin()->getStyle("ScrollLeftBtn"));
 			mDownBtn = GUIButton::create(parent, L"", parent.getSkin()->getStyle("ScrollRightBtn"));
@@ -32,7 +32,7 @@ namespace BansheeEngine
 		}
 		else
 		{
-			mLayout = &addLayoutYInternal();
+			mLayout = &addLayoutYInternal(this);
 
 			mUpBtn = GUIButton::create(parent, L"", parent.getSkin()->getStyle("ScrollUpBtn"));
 			mDownBtn = GUIButton::create(parent, L"", parent.getSkin()->getStyle("ScrollDownBtn"));
@@ -130,12 +130,7 @@ namespace BansheeEngine
 		if(scrollableSize > 0.0f)
 			handleOffset = ButtonScrollAmount / scrollableSize;
 
-		float newHandlePos = std::max(0.0f, mHandleBtn->getHandlePos() - handleOffset);
-
-		mHandleBtn->setHandlePos(newHandlePos);
-
-		if(!onScrollPositionChanged.empty())
-			onScrollPositionChanged(newHandlePos);
+		scroll(handleOffset);
 	}
 
 	void GUIScrollBar::downButtonClicked()
@@ -146,7 +141,12 @@ namespace BansheeEngine
 		if(scrollableSize > 0.0f)
 			handleOffset = ButtonScrollAmount / scrollableSize;
 
-		float newHandlePos = std::min(1.0f, mHandleBtn->getHandlePos() + handleOffset);
+		scroll(-handleOffset);
+	}
+
+	void GUIScrollBar::scroll(float amount)
+	{
+		float newHandlePos = Math::Clamp01(mHandleBtn->getHandlePos() - amount);
 
 		mHandleBtn->setHandlePos(newHandlePos);
 

+ 50 - 2
BansheeEngine/Source/BsGUIWidget.cpp

@@ -116,12 +116,60 @@ namespace BansheeEngine
 
 	bool GUIWidget::_mouseEvent(GUIElement* element, const GUIMouseEvent& ev)
 	{
-		return element->mouseEvent(ev);
+		// If an element has any parents we send the events to all parents first and only then to the children unless
+		// the parents process them
+		Stack<GUIElement*>::type todo;
+		GUIElementBase* curElement = element;
+		do
+		{
+			if(curElement->_getType() == GUIElementBase::Type::Element)
+				todo.push(static_cast<GUIElement*>(curElement));
+
+			curElement = curElement->_getParent();
+		} while(curElement != nullptr);
+		
+		while(true)
+		{
+			GUIElement* elem = todo.top();
+			todo.pop();
+
+			if(elem->mouseEvent(ev))
+				return true;
+
+			if(todo.size() == 0)
+				return false;
+		}
+
+		return false;
 	}
 
 	bool GUIWidget::_keyEvent(GUIElement* element, const GUIKeyEvent& ev)
 	{
-		return element->keyEvent(ev);
+		// If an element has any parents we send the events to all parents first and only then to the children unless
+		// the parents process them
+		Stack<GUIElement*>::type todo;
+		GUIElementBase* curElement = element;
+		do
+		{
+			if(curElement->_getType() == GUIElementBase::Type::Element)
+				todo.push(static_cast<GUIElement*>(curElement));
+
+			curElement = curElement->_getParent();
+		} while(curElement != nullptr);
+
+		while(true)
+		{
+			GUIElement* elem = todo.top();
+			todo.pop();
+
+			if(elem->keyEvent(ev))
+				return true;
+
+			if(todo.size() == 0)
+				return false;
+		}
+
+		return false;
 	}
 
 	void GUIWidget::registerElement(GUIElement* elem)

+ 2 - 2
TODO.txt

@@ -33,11 +33,11 @@ IMMEDIATE:
     - Make sure GUI system uses a dummy texture if one isn't available
 	- SpriteTexture keeps a static reference to DUmmyTexture which I need to release before shutdown
 
-- When the scroll contents dont fit vertically because of the horz scrollbar, the right side of the window frame gets cut off
-  - ScrollArea has no way of receiving mouse events
 - Scrolling the window shouldn't remove text selection
+ - In general focus shouldn't be lost at all
 - Hover colors of the scroll bar are wrong
 - Ability to scroll by just mousing over and moving the scroll wheel
+ - ScrollArea has no way of receiving mouse events
 
 TextBox needed elements:
  - Key-repeat? Pressing left/right/up/down arrows doesn't repeat the keys (also delete/backspace)