| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- #include "BsGUIScrollArea.h"
- #include "BsGUIElementStyle.h"
- #include "BsGUISkin.h"
- #include "BsGUIWidget.h"
- #include "BsGUILayoutOptions.h"
- #include "BsGUILayout.h"
- #include "BsGUISkin.h"
- #include "BsGUIScrollBarVert.h"
- #include "BsGUIScrollBarHorz.h"
- #include "BsGUIMouseEvent.h"
- #include "CmException.h"
- using namespace CamelotFramework;
- namespace BansheeEngine
- {
- const UINT32 GUIScrollArea::ScrollBarWidth = 8;
- const UINT32 GUIScrollArea::MinHandleSize = 4;
- const UINT32 GUIScrollArea::WheelScrollAmount = 50;
- GUIScrollArea::GUIScrollArea(GUIWidget& parent, ScrollBarType vertBarType, ScrollBarType horzBarType,
- const GUIElementStyle* scrollBarStyle, const GUIElementStyle* scrollAreaStyle, const GUILayoutOptions& layoutOptions)
- :GUIElement(parent, scrollAreaStyle, layoutOptions, false), mVertScroll(nullptr), mHorzScroll(nullptr), mVertOffset(0), mHorzOffset(0),
- mContentWidth(0), mContentHeight(0), mClippedContentWidth(0), mClippedContentHeight(0), mVertBarType(vertBarType), mHorzBarType(horzBarType),
- mScrollBarStyle(scrollBarStyle)
- {
- mContentLayout = &addLayoutYInternal(this);
- }
- GUIScrollArea::~GUIScrollArea()
- {
- }
- UINT32 GUIScrollArea::getNumRenderElements() const
- {
- return 0;
- }
- const GUIMaterialInfo& GUIScrollArea::getMaterial(UINT32 renderElementIdx) const
- {
- CM_EXCEPT(InvalidStateException, "Trying to retrieve a material from an element with no render elements.");
- }
- UINT32 GUIScrollArea::getNumQuads(UINT32 renderElementIdx) const
- {
- return 0;
- }
- void GUIScrollArea::updateClippedBounds()
- {
- mClippedBounds = RectI(0, 0, 0, 0); // We don't want any mouse input for this element. This is just a container.
- }
- Vector2I GUIScrollArea::_getOptimalSize() const
- {
- return mContentLayout->_getOptimalSize();
- }
- void GUIScrollArea::fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads,
- UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const
- { }
- void GUIScrollArea::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
- RectI clipRect, UINT8 widgetDepth, UINT16 areaDepth)
- {
- mContentLayout->_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, areaDepth);
- UINT32 contentWidth = mContentLayout->_getActualWidth();
- UINT32 contentHeight = mContentLayout->_getActualHeight();
- mClippedContentWidth = width;
- mClippedContentHeight = height;
- RectI layoutClipRect = clipRect;
- bool addHorzScrollbar = (mHorzBarType == ScrollBarType::ShowIfDoesntFit && contentWidth > mWidth) ||
- mHorzBarType == ScrollBarType::AlwaysShow && mHorzBarType != ScrollBarType::NeverShow;
- bool hasHorzScrollbar = false;
- bool hasVertScrollbar = false;
- if(addHorzScrollbar)
- {
- // Make room for scrollbar
- mClippedContentHeight = (UINT32)std::max(0, (INT32)height - (INT32)ScrollBarWidth);
- layoutClipRect.height = mClippedContentHeight;
- hasHorzScrollbar = true;
- mContentLayout->_updateLayoutInternal(x - Math::floorToInt(mHorzOffset), y,
- mClippedContentWidth + Math::floorToInt(mHorzOffset), mClippedContentHeight, layoutClipRect, widgetDepth, areaDepth);
- contentWidth = mContentLayout->_getActualWidth();
- contentHeight = mContentLayout->_getActualHeight();
- }
- bool addVertScrollbar = (mVertBarType == ScrollBarType::ShowIfDoesntFit && contentHeight > mClippedContentHeight) ||
- mVertBarType == ScrollBarType::AlwaysShow && mVertBarType != ScrollBarType::NeverShow;
- if(addVertScrollbar)
- {
- // Make room for scrollbar
- mClippedContentWidth = (UINT32)std::max(0, (INT32)width - (INT32)ScrollBarWidth);
- layoutClipRect.width = mClippedContentWidth;
- hasVertScrollbar = true;
- if(hasHorzScrollbar)
- {
- mContentLayout->_updateLayoutInternal(x - Math::floorToInt(mHorzOffset), y - Math::floorToInt(mVertOffset),
- mClippedContentWidth + Math::floorToInt(mHorzOffset), mClippedContentHeight + Math::floorToInt(mVertOffset),
- layoutClipRect, widgetDepth, areaDepth);
- }
- else
- {
- mContentLayout->_updateLayoutInternal(x, y - Math::floorToInt(mVertOffset),
- mClippedContentWidth, mClippedContentHeight + Math::floorToInt(mVertOffset),
- layoutClipRect, widgetDepth, areaDepth);
- }
- contentWidth = mContentLayout->_getActualWidth();
- contentHeight = mContentLayout->_getActualHeight();
- if(!hasHorzScrollbar) // Since width has been reduced, we need to check if we require the horizontal scrollbar
- {
- addHorzScrollbar = (mHorzBarType == ScrollBarType::ShowIfDoesntFit && contentWidth > mClippedContentWidth) && mHorzBarType != ScrollBarType::NeverShow;
- if(addHorzScrollbar)
- {
- // Make room for scrollbar
- mClippedContentHeight = (UINT32)std::max(0, (INT32)height - (INT32)ScrollBarWidth);
- layoutClipRect.height = mClippedContentHeight;
- mContentLayout->_updateLayoutInternal(x - Math::floorToInt(mHorzOffset), y - Math::floorToInt(mVertOffset),
- mClippedContentWidth + Math::floorToInt(mHorzOffset), mClippedContentHeight + Math::floorToInt(mVertOffset),
- layoutClipRect, widgetDepth, areaDepth);
- }
- }
- }
- // Add/remove/update vertical scrollbar as needed
- if((mVertBarType == ScrollBarType::ShowIfDoesntFit && contentHeight > mClippedContentHeight) || mVertBarType == ScrollBarType::AlwaysShow &&
- mVertBarType != ScrollBarType::NeverShow)
- {
- if(mVertScroll == nullptr)
- {
- if(mScrollBarStyle != nullptr)
- mVertScroll = GUIScrollBarVert::create(_getParentWidget(), mScrollBarStyle);
- else
- mVertScroll = GUIScrollBarVert::create(_getParentWidget());
- mVertScroll->onScrollPositionChanged.connect(boost::bind(&GUIScrollArea::vertScrollUpdate, this, _1));
- mVertScroll->_setAcceptsKeyboardFocus(false);
- }
- INT32 scrollBarOffset = (UINT32)std::max(0, (INT32)width - (INT32)ScrollBarWidth);
- UINT32 scrollBarHeight = height;
- if(hasHorzScrollbar)
- scrollBarHeight = (UINT32)std::max(0, (INT32)scrollBarHeight - (INT32)ScrollBarWidth);
- Vector2I offset(x + scrollBarOffset, y);
- mVertScroll->_setOffset(offset);
- mVertScroll->_setWidth(ScrollBarWidth);
- mVertScroll->_setHeight(scrollBarHeight);
- mVertScroll->_setAreaDepth(areaDepth);
- mVertScroll->_setWidgetDepth(widgetDepth);
- UINT32 clippedScrollbarWidth = std::min(width, ScrollBarWidth);
- RectI elemClipRect(0, 0, clippedScrollbarWidth, clipRect.height);
- mVertScroll->_setClipRect(elemClipRect);
- // This element is not a child of any layout so we treat it as a root element
- RectI scrollBarLayoutClipRect(clipRect.x + scrollBarOffset, clipRect.y, clippedScrollbarWidth, clipRect.height);
- mVertScroll->_updateLayout(offset.x, offset.y, ScrollBarWidth, scrollBarHeight, scrollBarLayoutClipRect, widgetDepth, areaDepth);
- // Set new handle size and update position to match the new size
- UINT32 newHandleSize = (UINT32)Math::floorToInt(mVertScroll->getMaxHandleSize() * (scrollBarHeight / (float)contentHeight));
- newHandleSize = std::max(newHandleSize, MinHandleSize);
- UINT32 scrollableHeight = (UINT32)std::max(0, INT32(contentHeight) - INT32(scrollBarHeight));
- float newScrollPct = 0.0f;
- if(scrollableHeight > 0)
- newScrollPct = mVertOffset / scrollableHeight;
- mVertScroll->setHandleSize(newHandleSize);
- mVertScroll->setScrollPos(newScrollPct);
- }
- else
- {
- if(mVertScroll != nullptr)
- {
- GUIElement::destroy(mVertScroll);
- mVertScroll = nullptr;
- }
- mVertOffset = 0.0f;
- }
- // Add/remove/update horizontal scrollbar as needed
- if((mHorzBarType == ScrollBarType::ShowIfDoesntFit && contentWidth > mClippedContentWidth) || mHorzBarType == ScrollBarType::AlwaysShow &&
- mHorzBarType != ScrollBarType::NeverShow)
- {
- if(mHorzScroll == nullptr)
- {
- if(mScrollBarStyle != nullptr)
- mHorzScroll = GUIScrollBarHorz::create(_getParentWidget(), mScrollBarStyle);
- else
- mHorzScroll = GUIScrollBarHorz::create(_getParentWidget());
- mHorzScroll->onScrollPositionChanged.connect(boost::bind(&GUIScrollArea::horzScrollUpdate, this, _1));
- mHorzScroll->_setAcceptsKeyboardFocus(false);
- }
- INT32 scrollBarOffset = (UINT32)std::max(0, (INT32)height - (INT32)ScrollBarWidth);
- UINT32 scrollBarWidth = width;
- if(hasVertScrollbar)
- scrollBarWidth = (UINT32)std::max(0, (INT32)scrollBarWidth - (INT32)ScrollBarWidth);
- Vector2I offset(x, y + scrollBarOffset);
- mHorzScroll->_setOffset(offset);
- mHorzScroll->_setWidth(scrollBarWidth);
- mHorzScroll->_setHeight(ScrollBarWidth);
- mHorzScroll->_setAreaDepth(areaDepth);
- mHorzScroll->_setWidgetDepth(widgetDepth);
- UINT32 clippedScrollbarHeight = std::min(height, ScrollBarWidth);
- RectI elemClipRect(0, 0, clipRect.width, clippedScrollbarHeight);
- mHorzScroll->_setClipRect(elemClipRect);
- // This element is not a child of any layout so we treat it as a root element
- RectI scrollBarLayoutClipRect(clipRect.x, clipRect.y + scrollBarOffset, clipRect.width, clippedScrollbarHeight);
- mHorzScroll->_updateLayout(offset.x, offset.y, scrollBarWidth, ScrollBarWidth, scrollBarLayoutClipRect, widgetDepth, areaDepth);
- // Set new handle size and update position to match the new size
- UINT32 newHandleSize = (UINT32)Math::floorToInt(mHorzScroll->getMaxHandleSize() * (scrollBarWidth / (float)contentWidth));
- newHandleSize = std::max(newHandleSize, MinHandleSize);
- UINT32 scrollableWidth = (UINT32)std::max(0, INT32(contentWidth) - INT32(scrollBarWidth));
- float newScrollPct = 0.0f;
-
- if(scrollableWidth > 0)
- newScrollPct = mHorzOffset / scrollableWidth;
- mHorzScroll->setHandleSize(newHandleSize);
- mHorzScroll->setScrollPos(newScrollPct);
- }
- else
- {
- if(mHorzScroll != nullptr)
- {
- GUIElement::destroy(mHorzScroll);
- mHorzScroll = nullptr;
- }
- mHorzOffset = 0.0f;
- }
- mContentWidth = contentWidth;
- mContentHeight = contentHeight;
- }
- void GUIScrollArea::vertScrollUpdate(float scrollPos)
- {
- UINT32 scrollableHeight = (UINT32)std::max(0, INT32(mContentHeight) - INT32(mClippedContentHeight));
- mVertOffset = scrollableHeight * scrollPos;
- markContentAsDirty();
- }
- void GUIScrollArea::horzScrollUpdate(float scrollPos)
- {
- UINT32 scrollableWidth = (UINT32)std::max(0, INT32(mContentWidth) - INT32(mClippedContentWidth));
- mHorzOffset = scrollableWidth * scrollPos;
- markContentAsDirty();
- }
- bool GUIScrollArea::mouseEvent(const GUIMouseEvent& ev)
- {
- if(ev.getType() == GUIMouseEventType::MouseWheelScroll)
- {
- // 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;
- }
- void GUIScrollArea::_changeParentWidget(GUIWidget* widget)
- {
- GUIElement::_changeParentWidget(widget);
- // These two elements are not part of a layout so I need to make sure to
- // update them manually
- if(mVertScroll != nullptr)
- mVertScroll->_changeParentWidget(widget);
- if(mHorzScroll != nullptr)
- mHorzScroll->_changeParentWidget(widget);
- }
- GUIScrollArea* GUIScrollArea::create(GUIWidget& parent, ScrollBarType vertBarType, ScrollBarType horzBarType,
- const GUIElementStyle* scrollBarStyle, const GUIElementStyle* scrollAreaStyle)
- {
- if(scrollAreaStyle == nullptr)
- {
- const GUISkin& skin = parent.getSkin();
- scrollAreaStyle = skin.getStyle(getGUITypeName());
- }
- return new (cm_alloc<GUIScrollArea, PoolAlloc>()) GUIScrollArea(parent, vertBarType, horzBarType, scrollBarStyle,
- scrollAreaStyle, GUILayoutOptions::create(scrollAreaStyle));
- }
- GUIScrollArea* GUIScrollArea::create(GUIWidget& parent, const GUIOptions& layoutOptions, const GUIElementStyle* scrollBarStyle,
- const GUIElementStyle* scrollAreaStyle)
- {
- if(scrollAreaStyle == nullptr)
- {
- const GUISkin& skin = parent.getSkin();
- scrollAreaStyle = skin.getStyle(getGUITypeName());
- }
- return new (cm_alloc<GUIScrollArea, PoolAlloc>()) GUIScrollArea(parent, ScrollBarType::ShowIfDoesntFit,
- ScrollBarType::ShowIfDoesntFit, scrollBarStyle, scrollAreaStyle, GUILayoutOptions::create(layoutOptions, scrollAreaStyle));
- }
- GUIScrollArea* GUIScrollArea::create(GUIWidget& parent, const GUIElementStyle* scrollBarStyle, const GUIElementStyle* scrollAreaStyle)
- {
- if(scrollAreaStyle == nullptr)
- {
- const GUISkin& skin = parent.getSkin();
- scrollAreaStyle = skin.getStyle(getGUITypeName());
- }
- return new (cm_alloc<GUIScrollArea, PoolAlloc>()) GUIScrollArea(parent, ScrollBarType::ShowIfDoesntFit, ScrollBarType::ShowIfDoesntFit, scrollBarStyle,
- scrollAreaStyle, GUILayoutOptions::create(scrollAreaStyle));
- }
- GUIScrollArea* GUIScrollArea::create(GUIWidget& parent, ScrollBarType vertBarType,
- ScrollBarType horzBarType, const GUIOptions& layoutOptions, const GUIElementStyle* scrollBarStyle,
- const GUIElementStyle* scrollAreaStyle)
- {
- if(scrollAreaStyle == nullptr)
- {
- const GUISkin& skin = parent.getSkin();
- scrollAreaStyle = skin.getStyle(getGUITypeName());
- }
- return new (cm_alloc<GUIScrollArea, PoolAlloc>()) GUIScrollArea(parent, vertBarType, horzBarType, scrollBarStyle, scrollAreaStyle, GUILayoutOptions::create(layoutOptions, scrollAreaStyle));
- }
- const String& GUIScrollArea::getGUITypeName()
- {
- static String typeName = "ScrollArea";
- return typeName;
- }
- }
|