| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489 |
- #include "BsGUIScrollArea.h"
- #include "BsGUIElementStyle.h"
- #include "BsGUISkin.h"
- #include "BsGUIWidget.h"
- #include "BsGUIDimensions.h"
- #include "BsGUILayoutY.h"
- #include "BsGUISkin.h"
- #include "BsGUIScrollBarVert.h"
- #include "BsGUIScrollBarHorz.h"
- #include "BsGUIMouseEvent.h"
- #include "BsGUILayoutUtility.h"
- #include "BsException.h"
- using namespace std::placeholders;
- namespace BansheeEngine
- {
- const UINT32 GUIScrollArea::ScrollBarWidth = 8;
- const UINT32 GUIScrollArea::MinHandleSize = 4;
- const UINT32 GUIScrollArea::WheelScrollAmount = 50;
- GUIScrollArea::GUIScrollArea(ScrollBarType vertBarType, ScrollBarType horzBarType,
- const String& scrollBarStyle, const String& scrollAreaStyle, const GUIDimensions& dimensions)
- :GUIElementContainer(dimensions), mVertScroll(nullptr), mHorzScroll(nullptr), mVertOffset(0), mHorzOffset(0),
- mVertBarType(vertBarType), mHorzBarType(horzBarType), mScrollBarStyle(scrollBarStyle)
- {
- mContentLayout = GUILayoutY::create();
- _registerChildElement(mContentLayout);
- mHorzScroll = GUIScrollBarHorz::create(mScrollBarStyle);
- mVertScroll = GUIScrollBarVert::create(mScrollBarStyle);
- _registerChildElement(mHorzScroll);
- _registerChildElement(mVertScroll);
- mHorzScroll->onScrollPositionChanged.connect(std::bind(&GUIScrollArea::horzScrollUpdate, this, _1));
- mVertScroll->onScrollPositionChanged.connect(std::bind(&GUIScrollArea::vertScrollUpdate, this, _1));
- }
- GUIScrollArea::~GUIScrollArea()
- {
- }
- void GUIScrollArea::updateClippedBounds()
- {
- Rect2I bounds(0, 0, mLayoutData.area.width, mLayoutData.area.height);
- bounds.clip(mLayoutData.clipRect);
- bounds.x += mLayoutData.area.x;
- bounds.y += mLayoutData.area.y;
- mClippedBounds = bounds;
- }
- void GUIScrollArea::_getElementAreas(const Rect2I& layoutArea, Rect2I* elementAreas, UINT32 numElements,
- const Vector<LayoutSizeRange>& sizeRanges, const LayoutSizeRange& mySizeRange) const
- {
- Vector2I visibleSize, contentSize;
- _getElementAreas(layoutArea, elementAreas, numElements, sizeRanges, visibleSize, contentSize);
- }
- void GUIScrollArea::_getElementAreas(const Rect2I& layoutArea, Rect2I* elementAreas, UINT32 numElements,
- const Vector<LayoutSizeRange>& sizeRanges, Vector2I& visibleSize, Vector2I& contentSize) const
- {
- assert(mChildren.size() == numElements && numElements == 3);
- UINT32 layoutIdx = 0;
- UINT32 horzScrollIdx = 0;
- UINT32 vertScrollIdx = 0;
- UINT32 idx = 0;
- for (auto& child : mChildren)
- {
- if (child == mContentLayout)
- layoutIdx = idx;
- if (child == mHorzScroll)
- horzScrollIdx = idx;
- if (child == mVertScroll)
- vertScrollIdx = idx;
- idx++;
- }
- // Calculate content layout bounds
- //// We want elements to use their optimal height, since scroll area
- //// technically provides "infinite" space
- UINT32 optimalContentWidth = layoutArea.width;
- if (mHorzBarType != ScrollBarType::NeverShow)
- optimalContentWidth = sizeRanges[layoutIdx].optimal.x;
- UINT32 optimalContentHeight = layoutArea.height;
- if (mVertBarType != ScrollBarType::NeverShow)
- optimalContentHeight = sizeRanges[layoutIdx].optimal.y;
- UINT32 layoutWidth = std::max(optimalContentWidth, (UINT32)layoutArea.width);
- UINT32 layoutHeight = std::max(optimalContentHeight, (UINT32)layoutArea.height);
- contentSize = GUILayoutUtility::calcActualSize(layoutWidth, layoutHeight, mContentLayout);
- visibleSize = Vector2I(layoutArea.width, layoutArea.height);
- bool addHorzScrollbar = (mHorzBarType == ScrollBarType::ShowIfDoesntFit && contentSize.x > visibleSize.x) ||
- mHorzBarType == ScrollBarType::AlwaysShow && mHorzBarType != ScrollBarType::NeverShow;
- bool hasHorzScrollbar = false;
- bool hasVertScrollbar = false;
- if (addHorzScrollbar)
- {
- // Make room for scrollbar
- visibleSize.y = (UINT32)std::max(0, (INT32)layoutArea.height - (INT32)ScrollBarWidth);
- if (mVertBarType == ScrollBarType::NeverShow)
- layoutHeight = (UINT32)visibleSize.y;
- else
- layoutHeight = std::max(optimalContentHeight, (UINT32)visibleSize.y); // Never go below optimal size
- contentSize = GUILayoutUtility::calcActualSize(layoutWidth, layoutHeight, mContentLayout);
- hasHorzScrollbar = true;
- }
- bool addVertScrollbar = (mVertBarType == ScrollBarType::ShowIfDoesntFit && contentSize.y > visibleSize.y) ||
- mVertBarType == ScrollBarType::AlwaysShow && mVertBarType != ScrollBarType::NeverShow;
- if (addVertScrollbar)
- {
- // Make room for scrollbar
- visibleSize.x = (UINT32)std::max(0, (INT32)layoutArea.width - (INT32)ScrollBarWidth);
-
- if (mHorzBarType == ScrollBarType::NeverShow)
- layoutWidth = (UINT32)visibleSize.x;
- else
- layoutWidth = std::max(optimalContentWidth, (UINT32)visibleSize.x); // Never go below optimal size
- contentSize = GUILayoutUtility::calcActualSize(layoutWidth, layoutHeight, mContentLayout);
- hasVertScrollbar = true;
- if (!hasHorzScrollbar) // Since width has been reduced, we need to check if we require the horizontal scrollbar
- {
- addHorzScrollbar = (mHorzBarType == ScrollBarType::ShowIfDoesntFit && contentSize.x > visibleSize.x) && mHorzBarType != ScrollBarType::NeverShow;
- if (addHorzScrollbar)
- {
- // Make room for scrollbar
- visibleSize.y = (UINT32)std::max(0, (INT32)layoutArea.height - (INT32)ScrollBarWidth);
- if (mVertBarType == ScrollBarType::NeverShow)
- layoutHeight = (UINT32)visibleSize.y;
- else
- layoutHeight = std::max(optimalContentHeight, (UINT32)visibleSize.y); // Never go below optimal size
- contentSize = GUILayoutUtility::calcActualSize(layoutWidth, layoutHeight, mContentLayout);
- hasHorzScrollbar = true;
- }
- }
- }
- elementAreas[layoutIdx] = Rect2I(layoutArea.x - Math::floorToInt(mHorzOffset), layoutArea.y - Math::floorToInt(mVertOffset), layoutWidth, layoutHeight);
- // Calculate vertical scrollbar bounds
- if (hasVertScrollbar)
- {
- INT32 scrollBarOffset = (UINT32)std::max(0, (INT32)layoutArea.width - (INT32)ScrollBarWidth);
- UINT32 scrollBarHeight = layoutArea.height;
- if (hasHorzScrollbar)
- scrollBarHeight = (UINT32)std::max(0, (INT32)scrollBarHeight - (INT32)ScrollBarWidth);
- elementAreas[vertScrollIdx] = Rect2I(layoutArea.x + scrollBarOffset, layoutArea.y, ScrollBarWidth, scrollBarHeight);
- }
- else
- {
- elementAreas[vertScrollIdx] = Rect2I(layoutArea.x + layoutWidth, layoutArea.y, 0, 0);
- }
- // Calculate horizontal scrollbar bounds
- if (hasHorzScrollbar)
- {
- INT32 scrollBarOffset = (UINT32)std::max(0, (INT32)layoutArea.height - (INT32)ScrollBarWidth);
- UINT32 scrollBarWidth = layoutArea.width;
- if (hasVertScrollbar)
- scrollBarWidth = (UINT32)std::max(0, (INT32)scrollBarWidth - (INT32)ScrollBarWidth);
- elementAreas[horzScrollIdx] = Rect2I(layoutArea.x, layoutArea.y + scrollBarOffset, scrollBarWidth, ScrollBarWidth);
- }
- else
- {
- elementAreas[horzScrollIdx] = Rect2I(layoutArea.x, layoutArea.y + layoutHeight, 0, 0);
- }
- }
- void GUIScrollArea::_updateLayoutInternal(const GUILayoutData& data)
- {
- UINT32 numElements = (UINT32)mChildren.size();
- Rect2I* elementAreas = nullptr;
- if (numElements > 0)
- elementAreas = stackConstructN<Rect2I>(numElements);
- Vector<LayoutSizeRange> sizeRanges;
- UINT32 layoutIdx = 0;
- UINT32 horzScrollIdx = 0;
- UINT32 vertScrollIdx = 0;
- for (UINT32 i = 0; i < numElements; i++)
- {
- GUIElementBase* child = _getChild(i);
- sizeRanges.push_back(child->_calculateLayoutSizeRange());
- if (child == mContentLayout)
- layoutIdx = i;
- if (child == mHorzScroll)
- horzScrollIdx = i;
- if (child == mVertScroll)
- vertScrollIdx = i;
- }
- _getElementAreas(data.area, elementAreas, numElements, sizeRanges, mVisibleSize, mContentSize);
- Rect2I& layoutBounds = elementAreas[layoutIdx];
- Rect2I& horzScrollBounds = elementAreas[horzScrollIdx];
- Rect2I& vertScrollBounds = elementAreas[vertScrollIdx];
- // Layout
- Rect2I layoutClipRect = data.clipRect;
- layoutClipRect.width = (UINT32)mVisibleSize.x;
- layoutClipRect.height = (UINT32)mVisibleSize.y;
- GUILayoutData layoutData = data;
- layoutData.area = layoutBounds;
- layoutData.clipRect = layoutClipRect;
- layoutData.clipRect.x -= layoutBounds.x;
- layoutData.clipRect.y -= layoutBounds.y;
- mContentLayout->_setLayoutData(layoutData);
- layoutData.clipRect = layoutClipRect;
- mContentLayout->_updateLayoutInternal(layoutData);
- // Vertical scrollbar
- {
- GUILayoutData vertScrollData = data;
- vertScrollData.area = vertScrollBounds;
- UINT32 clippedScrollbarWidth = std::min((UINT32)data.area.width, ScrollBarWidth);
- vertScrollData.clipRect = Rect2I(0, 0, clippedScrollbarWidth, data.clipRect.height);
- mVertScroll->_setLayoutData(layoutData);
- vertScrollData.clipRect = Rect2I(data.clipRect.x + (vertScrollBounds.x - data.area.x),
- data.clipRect.y + (vertScrollBounds.y - data.area.y), clippedScrollbarWidth, data.clipRect.height);
- // This element is not a child of any layout so we treat it as a root element
- mVertScroll->_updateLayout(vertScrollData);
- // Set new handle size and update position to match the new size
- UINT32 newHandleSize = (UINT32)Math::floorToInt(mVertScroll->getMaxHandleSize() * (vertScrollBounds.height / (float)mContentSize.y));
- newHandleSize = std::max(newHandleSize, MinHandleSize);
- UINT32 scrollableHeight = (UINT32)std::max(0, INT32(mContentSize.y) - INT32(vertScrollBounds.height));
- float newScrollPct = 0.0f;
- if (scrollableHeight > 0)
- newScrollPct = mVertOffset / scrollableHeight;
- mVertScroll->_setHandleSize(newHandleSize);
- mVertScroll->_setScrollPos(newScrollPct);
- }
- // Horizontal scrollbar
- {
- GUILayoutData horzScrollData = data;
- horzScrollData.area = horzScrollBounds;
- UINT32 clippedScrollbarHeight = std::min((UINT32)data.area.height, ScrollBarWidth);
- horzScrollData.clipRect = Rect2I(0, 0, data.clipRect.width, clippedScrollbarHeight);
- mHorzScroll->_setLayoutData(horzScrollData);
- // This element is not a child of any layout so we treat it as a root element
- horzScrollData.clipRect = Rect2I(data.clipRect.x + (horzScrollBounds.x - data.area.x),
- data.clipRect.y + (horzScrollBounds.y - data.area.y), data.clipRect.width, clippedScrollbarHeight);
- mHorzScroll->_updateLayout(horzScrollData);
- // Set new handle size and update position to match the new size
- UINT32 newHandleSize = (UINT32)Math::floorToInt(mHorzScroll->getMaxHandleSize() * (horzScrollBounds.width / (float)mContentSize.x));
- newHandleSize = std::max(newHandleSize, MinHandleSize);
- UINT32 scrollableWidth = (UINT32)std::max(0, INT32(mContentSize.x) - INT32(horzScrollBounds.width));
- float newScrollPct = 0.0f;
- if (scrollableWidth > 0)
- newScrollPct = mHorzOffset / scrollableWidth;
- mHorzScroll->_setHandleSize(newHandleSize);
- mHorzScroll->_setScrollPos(newScrollPct);
- }
- if (elementAreas != nullptr)
- stackDeallocLast(elementAreas);
- }
- void GUIScrollArea::vertScrollUpdate(float scrollPos)
- {
- scrollToVertical(scrollPos);
- }
- void GUIScrollArea::horzScrollUpdate(float scrollPos)
- {
- scrollToHorizontal(scrollPos);
- }
- void GUIScrollArea::scrollToVertical(float pct)
- {
- UINT32 scrollableHeight = (UINT32)std::max(0, INT32(mContentSize.y) - INT32(mVisibleSize.y));
- mVertOffset = scrollableHeight * Math::clamp01(pct);
- _markContentAsDirty();
- }
- void GUIScrollArea::scrollToHorizontal(float pct)
- {
- UINT32 scrollableWidth = (UINT32)std::max(0, INT32(mContentSize.x) - INT32(mVisibleSize.x));
- mHorzOffset = scrollableWidth * Math::clamp01(pct);
- _markContentAsDirty();
- }
- float GUIScrollArea::getVerticalScroll() const
- {
- if (mVertScroll != nullptr)
- return mVertScroll->getScrollPos();
- return 0.0f;
- }
- float GUIScrollArea::getHorizontalScroll() const
- {
- if (mHorzScroll != nullptr)
- return mHorzScroll->getScrollPos();
- return 0.0f;
- }
- Rect2I GUIScrollArea::getContentBounds()
- {
- Rect2I bounds = getBounds();
- if (mHorzScroll)
- bounds.height -= ScrollBarWidth;
- if (mVertScroll)
- bounds.width -= ScrollBarWidth;
- return bounds;
- }
- void GUIScrollArea::scrollUpPx(UINT32 pixels)
- {
- if(mVertScroll != nullptr)
- {
- UINT32 scrollableSize = (UINT32)std::max(0, INT32(mContentSize.y) - INT32(mVisibleSize.y));
- float offset = 0.0f;
- if(scrollableSize > 0)
- offset = pixels / (float)scrollableSize;
- mVertScroll->scroll(offset);
- }
- }
- void GUIScrollArea::scrollDownPx(UINT32 pixels)
- {
- if(mVertScroll != nullptr)
- {
- UINT32 scrollableSize = (UINT32)std::max(0, INT32(mContentSize.y) - INT32(mVisibleSize.y));
- float offset = 0.0f;
- if(scrollableSize > 0)
- offset = pixels / (float)scrollableSize;
- mVertScroll->scroll(-offset);
- }
- }
- void GUIScrollArea::scrollLeftPx(UINT32 pixels)
- {
- if(mHorzScroll != nullptr)
- {
- UINT32 scrollableSize = (UINT32)std::max(0, INT32(mContentSize.x) - INT32(mVisibleSize.x));
- float offset = 0.0f;
- if(scrollableSize > 0)
- offset = pixels / (float)scrollableSize;
- mHorzScroll->scroll(offset);
- }
- }
- void GUIScrollArea::scrollRightPx(UINT32 pixels)
- {
- if(mHorzScroll != nullptr)
- {
- UINT32 scrollableSize = (UINT32)std::max(0, INT32(mContentSize.x) - INT32(mVisibleSize.x));
- float offset = 0.0f;
- if(scrollableSize > 0)
- offset = pixels / (float)scrollableSize;
- mHorzScroll->scroll(-offset);
- }
- }
- void GUIScrollArea::scrollUpPct(float percent)
- {
- if(mVertScroll != nullptr)
- mVertScroll->scroll(percent);
- }
- void GUIScrollArea::scrollDownPct(float percent)
- {
- if(mVertScroll != nullptr)
- mVertScroll->scroll(-percent);
- }
- void GUIScrollArea::scrollLeftPct(float percent)
- {
- if(mHorzScroll != nullptr)
- mHorzScroll->scroll(percent);
- }
- void GUIScrollArea::scrollRightPct(float percent)
- {
- if(mHorzScroll != nullptr)
- mHorzScroll->scroll(-percent);
- }
- 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(mContentSize.y) - INT32(mVisibleSize.y));
- float additionalScroll = (float)WheelScrollAmount / scrollableHeight;
- mVertScroll->scroll(additionalScroll * ev.getWheelScrollAmount());
- return true;
- }
- }
- return false;
- }
- GUIScrollArea* GUIScrollArea::create(ScrollBarType vertBarType, ScrollBarType horzBarType,
- const String& scrollBarStyle, const String& scrollAreaStyle)
- {
- return new (bs_alloc<GUIScrollArea, PoolAlloc>()) GUIScrollArea(vertBarType, horzBarType, scrollBarStyle,
- getStyleName<GUIScrollArea>(scrollAreaStyle), GUIDimensions::create());
- }
- GUIScrollArea* GUIScrollArea::create(const GUIOptions& options, const String& scrollBarStyle,
- const String& scrollAreaStyle)
- {
- return new (bs_alloc<GUIScrollArea, PoolAlloc>()) GUIScrollArea(ScrollBarType::ShowIfDoesntFit,
- ScrollBarType::ShowIfDoesntFit, scrollBarStyle, getStyleName<GUIScrollArea>(scrollAreaStyle), GUIDimensions::create(options));
- }
- GUIScrollArea* GUIScrollArea::create(const String& scrollBarStyle, const String& scrollAreaStyle)
- {
- return new (bs_alloc<GUIScrollArea, PoolAlloc>()) GUIScrollArea(ScrollBarType::ShowIfDoesntFit, ScrollBarType::ShowIfDoesntFit, scrollBarStyle,
- getStyleName<GUIScrollArea>(scrollAreaStyle), GUIDimensions::create());
- }
- GUIScrollArea* GUIScrollArea::create(ScrollBarType vertBarType,
- ScrollBarType horzBarType, const GUIOptions& options, const String& scrollBarStyle,
- const String& scrollAreaStyle)
- {
- return new (bs_alloc<GUIScrollArea, PoolAlloc>()) GUIScrollArea(vertBarType, horzBarType, scrollBarStyle,
- getStyleName<GUIScrollArea>(scrollAreaStyle), GUIDimensions::create(options));
- }
- const String& GUIScrollArea::getGUITypeName()
- {
- static String typeName = "ScrollArea";
- return typeName;
- }
- }
|