1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "GUI/BsGUIScrollBar.h"
4#include "Image/BsSpriteTexture.h"
5#include "GUI/BsGUIElementStyle.h"
6#include "GUI/BsGUIDimensions.h"
7#include "GUI/BsGUILayoutX.h"
8#include "GUI/BsGUILayoutY.h"
9#include "GUI/BsGUIButton.h"
10#include "GUI/BsGUISliderHandle.h"
11#include "GUI/BsGUISpace.h"
12
13using namespace std::placeholders;
14
15namespace bs
16{
17 const UINT32 GUIScrollBar::ButtonScrollAmount = 10;
18
19 const String& GUIScrollBar::getHScrollHandleType()
20 {
21 static String typeName = "UIScrollBarHHandle";
22 return typeName;
23 }
24
25 const String& GUIScrollBar::getVScrollHandleType()
26 {
27 static String typeName = "UIScrollBarVHandle";
28 return typeName;
29 }
30
31 GUIScrollBar::GUIScrollBar(bool horizontal, bool resizable, const String& styleName, const GUIDimensions& dimensions)
32 :GUIElement(styleName, dimensions), mHorizontal(horizontal)
33 {
34 mImageSprite = bs_new<ImageSprite>();
35
36 GUISliderHandleFlags flags;
37 if (resizable)
38 flags |= GUISliderHandleFlag::Resizeable;
39
40 if(mHorizontal)
41 {
42 mLayout = GUILayoutX::create();
43 _registerChildElement(mLayout);
44
45 mUpBtn = GUIButton::create(HString(""), "ScrollLeftBtn");
46 mDownBtn = GUIButton::create(HString(""), "ScrollRightBtn");
47
48 mHandleBtn = GUISliderHandle::create(flags | GUISliderHandleFlag::Horizontal, getSubStyleName(getHScrollHandleType()));
49 }
50 else
51 {
52 mLayout = GUILayoutY::create();
53 _registerChildElement(mLayout);
54
55 mUpBtn = GUIButton::create(HString(""), "ScrollUpBtn");
56 mDownBtn = GUIButton::create(HString(""), "ScrollDownBtn");
57
58 mHandleBtn = GUISliderHandle::create(flags | GUISliderHandleFlag::Vertical, getSubStyleName(getVScrollHandleType()));
59 }
60
61 GUIElementOptions scrollUpBtnOptions = mUpBtn->getOptionFlags();
62 scrollUpBtnOptions.unset(GUIElementOption::AcceptsKeyFocus);
63
64 mUpBtn->setOptionFlags(scrollUpBtnOptions);
65
66 GUIElementOptions scrollDownBtnOptions = mDownBtn->getOptionFlags();
67 scrollDownBtnOptions.unset(GUIElementOption::AcceptsKeyFocus);
68
69 mDownBtn->setOptionFlags(scrollDownBtnOptions);
70
71 mLayout->addNewElement<GUIFixedSpace>(2);
72 mLayout->addElement(mUpBtn);
73 mLayout->addElement(mHandleBtn);
74 mLayout->addElement(mDownBtn);
75 mLayout->addNewElement<GUIFixedSpace>(2);
76
77 mHandleBtn->onHandleMovedOrResized.connect(std::bind(&GUIScrollBar::handleMoved, this, _1, _2));
78
79 mUpBtn->onClick.connect(std::bind(&GUIScrollBar::upButtonClicked, this));
80 mDownBtn->onClick.connect(std::bind(&GUIScrollBar::downButtonClicked, this));
81 }
82
83 GUIScrollBar::~GUIScrollBar()
84 {
85 bs_delete(mImageSprite);
86
87 GUIElement::destroy(mUpBtn);
88 GUIElement::destroy(mDownBtn);
89 GUIElement::destroy(mHandleBtn);
90 }
91
92 UINT32 GUIScrollBar::_getNumRenderElements() const
93 {
94 return mImageSprite->getNumRenderElements();
95 }
96
97 const SpriteMaterialInfo& GUIScrollBar::_getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const
98 {
99 *material = mImageSprite->getMaterial(renderElementIdx);
100 return mImageSprite->getMaterialInfo(renderElementIdx);
101 }
102
103 void GUIScrollBar::_getMeshInfo(UINT32 renderElementIdx, UINT32& numVertices, UINT32& numIndices, GUIMeshType& type) const
104 {
105 UINT32 numQuads = mImageSprite->getNumQuads(renderElementIdx);
106
107 numVertices = numQuads * 4;
108 numIndices = numQuads * 6;
109 type = GUIMeshType::Triangle;
110 }
111
112 void GUIScrollBar::updateRenderElementsInternal()
113 {
114 IMAGE_SPRITE_DESC desc;
115
116 if(_getStyle()->normal.texture != nullptr && _getStyle()->normal.texture.isLoaded())
117 desc.texture = _getStyle()->normal.texture;
118
119 desc.width = mLayoutData.area.width;
120 desc.height = mLayoutData.area.height;
121 desc.color = getTint();
122
123 mImageSprite->update(desc, (UINT64)_getParentWidget());
124
125 GUIElement::updateRenderElementsInternal();
126 }
127
128 void GUIScrollBar::updateClippedBounds()
129 {
130 mClippedBounds = Rect2I(0, 0, 0, 0); // We don't want any mouse input for this element. This is just a container.
131 }
132
133 Vector2I GUIScrollBar::_getOptimalSize() const
134 {
135 return mLayout->_getOptimalSize();
136 }
137
138 UINT32 GUIScrollBar::_getRenderElementDepth(UINT32 renderElementIdx) const
139 {
140 return _getDepth() + 2; // + 2 depth because child buttons use +1
141 }
142
143 UINT32 GUIScrollBar::_getRenderElementDepthRange() const
144 {
145 return 3;
146 }
147
148 void GUIScrollBar::_fillBuffer(UINT8* vertices, UINT32* indices, UINT32 vertexOffset, UINT32 indexOffset,
149 UINT32 maxNumVerts, UINT32 maxNumIndices, UINT32 renderElementIdx) const
150 {
151 UINT8* uvs = vertices + sizeof(Vector2);
152 UINT32 vertexStride = sizeof(Vector2) * 2;
153 UINT32 indexStride = sizeof(UINT32);
154
155 Vector2I offset(mLayoutData.area.x, mLayoutData.area.y);
156 mImageSprite->fillBuffer(vertices, uvs, indices, vertexOffset, indexOffset, maxNumVerts, maxNumIndices,
157 vertexStride, indexStride, renderElementIdx, offset, mLayoutData.getLocalClipRect());
158 }
159
160 void GUIScrollBar::styleUpdated()
161 {
162 if (mHorizontal)
163 mHandleBtn->setStyle(getSubStyleName(getHScrollHandleType()));
164 else
165 mHandleBtn->setStyle(getSubStyleName(getVScrollHandleType()));
166 }
167
168 void GUIScrollBar::handleMoved(float handlePct, float sizePct)
169 {
170 if(!onScrollOrResize.empty())
171 onScrollOrResize(handlePct, sizePct);
172 }
173
174 void GUIScrollBar::upButtonClicked()
175 {
176 float handleOffset = 0.0f;
177 float scrollableSize = (float)mHandleBtn->getScrollableSize();
178
179 if(scrollableSize > 0.0f)
180 handleOffset = ButtonScrollAmount / scrollableSize;
181
182 scroll(handleOffset);
183 }
184
185 void GUIScrollBar::downButtonClicked()
186 {
187 float handleOffset = 0.0f;
188 float scrollableSize = (float)mHandleBtn->getScrollableSize();
189
190 if(scrollableSize > 0.0f)
191 handleOffset = ButtonScrollAmount / scrollableSize;
192
193 scroll(-handleOffset);
194 }
195
196 void GUIScrollBar::scroll(float amount)
197 {
198 float newHandlePos = Math::clamp01(mHandleBtn->getHandlePos() - amount);
199
200 float oldHandlePos = mHandleBtn->getHandlePos();
201 mHandleBtn->_setHandlePos(newHandlePos);
202
203 if (oldHandlePos != mHandleBtn->getHandlePos())
204 {
205 mHandleBtn->_markLayoutAsDirty();
206
207 if (!onScrollOrResize.empty())
208 onScrollOrResize(newHandlePos, mHandleBtn->_getHandleSizePct());
209 }
210 }
211
212 void GUIScrollBar::_setHandleSize(float pct)
213 {
214 mHandleBtn->_setHandleSize(pct);
215 }
216
217 void GUIScrollBar::_setScrollPos(float pct)
218 {
219 mHandleBtn->_setHandlePos(pct);
220 }
221
222 float GUIScrollBar::getScrollPos() const
223 {
224 return mHandleBtn->getHandlePos();
225 }
226
227 void GUIScrollBar::setScrollPos(float pct)
228 {
229 float oldHandlePos = mHandleBtn->getHandlePos();
230 mHandleBtn->_setHandlePos(pct);
231
232 if (oldHandlePos != mHandleBtn->getHandlePos())
233 mHandleBtn->_markLayoutAsDirty();
234 }
235
236 float GUIScrollBar::getHandleSize() const
237 {
238 return mHandleBtn->_getHandleSizePct();
239 }
240
241 void GUIScrollBar::setHandleSize(float pct)
242 {
243 mHandleBtn->_setHandleSize(pct);
244 mHandleBtn->_markLayoutAsDirty();
245 }
246
247 UINT32 GUIScrollBar::getScrollableSize() const
248 {
249 return mHandleBtn->getScrollableSize();
250 }
251
252 void GUIScrollBar::setTint(const Color& color)
253 {
254 mUpBtn->setTint(color);
255 mDownBtn->setTint(color);
256 mHandleBtn->setTint(color);
257
258 GUIElement::setTint(color);
259 }
260}