2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "GUIScrollBarControl.h"
11 #include "GUIMessage.h"
12 #include "input/actions/Action.h"
13 #include "input/actions/ActionIDs.h"
14 #include "input/mouse/MouseEvent.h"
15 #include "input/mouse/MouseStat.h"
16 #include "utils/StringUtils.h"
20 #define MIN_NIB_SIZE 4.0f
22 GUIScrollBarControl::GUIScrollBarControl(int parentID
,
28 const CTextureInfo
& backGroundTexture
,
29 const CTextureInfo
& barTexture
,
30 const CTextureInfo
& barTextureFocus
,
31 const CTextureInfo
& nibTexture
,
32 const CTextureInfo
& nibTextureFocus
,
33 ORIENTATION orientation
,
35 : CGUIControl(parentID
, controlID
, posX
, posY
, width
, height
),
36 m_guiBackground(CGUITexture::CreateTexture(posX
, posY
, width
, height
, backGroundTexture
)),
37 m_guiBarNoFocus(CGUITexture::CreateTexture(posX
, posY
, width
, height
, barTexture
)),
38 m_guiBarFocus(CGUITexture::CreateTexture(posX
, posY
, width
, height
, barTextureFocus
)),
39 m_guiNibNoFocus(CGUITexture::CreateTexture(posX
, posY
, width
, height
, nibTexture
)),
40 m_guiNibFocus(CGUITexture::CreateTexture(posX
, posY
, width
, height
, nibTextureFocus
))
42 m_guiNibNoFocus
->SetAspectRatio(CAspectRatio::AR_CENTER
);
43 m_guiNibFocus
->SetAspectRatio(CAspectRatio::AR_CENTER
);
47 ControlType
= GUICONTROL_SCROLLBAR
;
48 m_orientation
= orientation
;
49 m_showOnePage
= showOnePage
;
52 GUIScrollBarControl::GUIScrollBarControl(const GUIScrollBarControl
& control
)
53 : CGUIControl(control
),
54 m_guiBackground(control
.m_guiBackground
->Clone()),
55 m_guiBarNoFocus(control
.m_guiBarNoFocus
->Clone()),
56 m_guiBarFocus(control
.m_guiBarFocus
->Clone()),
57 m_guiNibNoFocus(control
.m_guiNibNoFocus
->Clone()),
58 m_guiNibFocus(control
.m_guiNibFocus
->Clone()),
59 m_numItems(control
.m_numItems
),
60 m_pageSize(control
.m_pageSize
),
61 m_offset(control
.m_offset
),
62 m_showOnePage(control
.m_showOnePage
),
63 m_orientation(control
.m_orientation
)
67 void GUIScrollBarControl::Process(unsigned int currentTime
, CDirtyRegionList
&dirtyregions
)
72 changed
|= UpdateBarSize();
74 changed
|= m_guiBackground
->Process(currentTime
);
75 changed
|= m_guiBarNoFocus
->Process(currentTime
);
76 changed
|= m_guiBarFocus
->Process(currentTime
);
77 changed
|= m_guiNibNoFocus
->Process(currentTime
);
78 changed
|= m_guiNibFocus
->Process(currentTime
);
83 CGUIControl::Process(currentTime
, dirtyregions
);
86 void GUIScrollBarControl::Render()
88 m_guiBackground
->Render();
91 m_guiBarFocus
->Render();
92 m_guiNibFocus
->Render();
96 m_guiBarNoFocus
->Render();
97 m_guiNibNoFocus
->Render();
100 CGUIControl::Render();
103 bool GUIScrollBarControl::OnMessage(CGUIMessage
& message
)
105 switch (message
.GetMessage())
107 case GUI_MSG_ITEM_SELECT
:
108 SetValue(message
.GetParam1());
110 case GUI_MSG_LABEL_RESET
:
111 SetRange(message
.GetParam1(), message
.GetParam2());
113 case GUI_MSG_PAGE_UP
:
116 case GUI_MSG_PAGE_DOWN
:
120 return CGUIControl::OnMessage(message
);
123 bool GUIScrollBarControl::OnAction(const CAction
&action
)
125 switch ( action
.GetID() )
127 case ACTION_MOVE_LEFT
:
128 if (m_orientation
== HORIZONTAL
)
135 case ACTION_MOVE_RIGHT
:
136 if (m_orientation
== HORIZONTAL
)
143 if (m_orientation
== VERTICAL
)
150 case ACTION_MOVE_DOWN
:
151 if (m_orientation
== VERTICAL
)
158 return CGUIControl::OnAction(action
);
161 bool GUIScrollBarControl::Move(int numSteps
)
163 if (numSteps
< 0 && m_offset
== 0) // we are at the beginning - can't scroll up/left anymore
165 if (numSteps
> 0 && m_offset
== std::max(m_numItems
- m_pageSize
, 0)) // we are at the end - we can't scroll down/right anymore
168 m_offset
+= numSteps
* m_pageSize
;
169 if (m_offset
> m_numItems
- m_pageSize
) m_offset
= m_numItems
- m_pageSize
;
170 if (m_offset
< 0) m_offset
= 0;
171 CGUIMessage
message(GUI_MSG_NOTIFY_ALL
, GetParentID(), GetID(), GUI_MSG_PAGE_CHANGE
, m_offset
);
172 SendWindowMessage(message
);
177 void GUIScrollBarControl::SetRange(int pageSize
, int numItems
)
179 if (m_pageSize
!= pageSize
|| m_numItems
!= numItems
)
181 m_pageSize
= pageSize
;
182 m_numItems
= numItems
;
188 void GUIScrollBarControl::SetValue(int value
)
190 if (m_offset
!= value
)
197 void GUIScrollBarControl::FreeResources(bool immediately
)
199 CGUIControl::FreeResources(immediately
);
200 m_guiBackground
->FreeResources(immediately
);
201 m_guiBarNoFocus
->FreeResources(immediately
);
202 m_guiBarFocus
->FreeResources(immediately
);
203 m_guiNibNoFocus
->FreeResources(immediately
);
204 m_guiNibFocus
->FreeResources(immediately
);
207 void GUIScrollBarControl::DynamicResourceAlloc(bool bOnOff
)
209 CGUIControl::DynamicResourceAlloc(bOnOff
);
210 m_guiBackground
->DynamicResourceAlloc(bOnOff
);
211 m_guiBarNoFocus
->DynamicResourceAlloc(bOnOff
);
212 m_guiBarFocus
->DynamicResourceAlloc(bOnOff
);
213 m_guiNibNoFocus
->DynamicResourceAlloc(bOnOff
);
214 m_guiNibFocus
->DynamicResourceAlloc(bOnOff
);
217 void GUIScrollBarControl::AllocResources()
219 CGUIControl::AllocResources();
220 m_guiBackground
->AllocResources();
221 m_guiBarNoFocus
->AllocResources();
222 m_guiBarFocus
->AllocResources();
223 m_guiNibNoFocus
->AllocResources();
224 m_guiNibFocus
->AllocResources();
227 void GUIScrollBarControl::SetInvalid()
229 CGUIControl::SetInvalid();
230 m_guiBackground
->SetInvalid();
231 m_guiBarFocus
->SetInvalid();
232 m_guiBarFocus
->SetInvalid();
233 m_guiNibNoFocus
->SetInvalid();
234 m_guiNibFocus
->SetInvalid();
237 bool GUIScrollBarControl::UpdateBarSize()
239 bool changed
= false;
241 // scale our textures to suit
242 if (m_orientation
== VERTICAL
)
244 // calculate the height to display the nib at
245 float percent
= (m_numItems
== 0) ? 0 : (float)m_pageSize
/ m_numItems
;
246 float nibSize
= GetHeight() * percent
;
247 if (nibSize
< m_guiNibFocus
->GetTextureHeight() + 2 * MIN_NIB_SIZE
)
248 nibSize
= m_guiNibFocus
->GetTextureHeight() + 2 * MIN_NIB_SIZE
;
249 if (nibSize
> GetHeight()) nibSize
= GetHeight();
251 changed
|= m_guiBarNoFocus
->SetHeight(nibSize
);
252 changed
|= m_guiBarFocus
->SetHeight(nibSize
);
253 changed
|= m_guiNibNoFocus
->SetHeight(nibSize
);
254 changed
|= m_guiNibFocus
->SetHeight(nibSize
);
255 // nibSize may be altered by the border size of the nib (and bar).
256 nibSize
= std::max(m_guiBarFocus
->GetHeight(), m_guiNibFocus
->GetHeight());
259 percent
= (m_numItems
== m_pageSize
) ? 0 : (float)m_offset
/ (m_numItems
- m_pageSize
);
260 float nibPos
= (GetHeight() - nibSize
) * percent
;
261 if (nibPos
< 0) nibPos
= 0;
262 if (nibPos
> GetHeight() - nibSize
) nibPos
= GetHeight() - nibSize
;
264 changed
|= m_guiBarNoFocus
->SetPosition(GetXPosition(), GetYPosition() + nibPos
);
265 changed
|= m_guiBarFocus
->SetPosition(GetXPosition(), GetYPosition() + nibPos
);
266 changed
|= m_guiNibNoFocus
->SetPosition(GetXPosition(), GetYPosition() + nibPos
);
267 changed
|= m_guiNibFocus
->SetPosition(GetXPosition(), GetYPosition() + nibPos
);
271 // calculate the height to display the nib at
272 float percent
= (m_numItems
== 0) ? 0 : (float)m_pageSize
/ m_numItems
;
273 float nibSize
= GetWidth() * percent
+ 0.5f
;
274 if (nibSize
< m_guiNibFocus
->GetTextureWidth() + 2 * MIN_NIB_SIZE
)
275 nibSize
= m_guiNibFocus
->GetTextureWidth() + 2 * MIN_NIB_SIZE
;
276 if (nibSize
> GetWidth()) nibSize
= GetWidth();
278 changed
|= m_guiBarNoFocus
->SetWidth(nibSize
);
279 changed
|= m_guiBarFocus
->SetWidth(nibSize
);
280 changed
|= m_guiNibNoFocus
->SetWidth(nibSize
);
281 changed
|= m_guiNibFocus
->SetWidth(nibSize
);
284 percent
= (m_numItems
== m_pageSize
) ? 0 : (float)m_offset
/ (m_numItems
- m_pageSize
);
285 float nibPos
= (GetWidth() - nibSize
) * percent
;
286 if (nibPos
< 0) nibPos
= 0;
287 if (nibPos
> GetWidth() - nibSize
) nibPos
= GetWidth() - nibSize
;
289 changed
|= m_guiBarNoFocus
->SetPosition(GetXPosition() + nibPos
, GetYPosition());
290 changed
|= m_guiBarFocus
->SetPosition(GetXPosition() + nibPos
, GetYPosition());
291 changed
|= m_guiNibNoFocus
->SetPosition(GetXPosition() + nibPos
, GetYPosition());
292 changed
|= m_guiNibFocus
->SetPosition(GetXPosition() + nibPos
, GetYPosition());
298 void GUIScrollBarControl::SetFromPosition(const CPoint
&point
)
301 if (m_orientation
== VERTICAL
)
302 fPercent
= (point
.y
- m_guiBackground
->GetYPosition() - 0.5f
* m_guiBarFocus
->GetHeight()) /
303 (m_guiBackground
->GetHeight() - m_guiBarFocus
->GetHeight());
305 fPercent
= (point
.x
- m_guiBackground
->GetXPosition() - 0.5f
* m_guiBarFocus
->GetWidth()) /
306 (m_guiBackground
->GetWidth() - m_guiBarFocus
->GetWidth());
307 if (fPercent
< 0) fPercent
= 0;
308 if (fPercent
> 1) fPercent
= 1;
310 int offset
= (int)(floor(fPercent
* (m_numItems
- m_pageSize
) + 0.5f
));
312 if (m_offset
!= offset
)
315 CGUIMessage
message(GUI_MSG_NOTIFY_ALL
, GetParentID(), GetID(), GUI_MSG_PAGE_CHANGE
, m_offset
);
316 SendWindowMessage(message
);
321 EVENT_RESULT
GUIScrollBarControl::OnMouseEvent(const CPoint
& point
, const MOUSE::CMouseEvent
& event
)
323 if (event
.m_id
== ACTION_MOUSE_DRAG
|| event
.m_id
== ACTION_MOUSE_DRAG_END
)
325 if (static_cast<HoldAction
>(event
.m_state
) == HoldAction::DRAG
)
326 { // we want exclusive access
327 CGUIMessage
msg(GUI_MSG_EXCLUSIVE_MOUSE
, GetID(), GetParentID());
328 SendWindowMessage(msg
);
330 else if (static_cast<HoldAction
>(event
.m_state
) == HoldAction::DRAG_END
)
331 { // we're done with exclusive access
332 CGUIMessage
msg(GUI_MSG_EXCLUSIVE_MOUSE
, 0, GetParentID());
333 SendWindowMessage(msg
);
335 SetFromPosition(point
);
336 return EVENT_RESULT_HANDLED
;
338 else if (event
.m_id
== ACTION_MOUSE_LEFT_CLICK
&& m_guiBackground
->HitTest(point
))
340 SetFromPosition(point
);
341 return EVENT_RESULT_HANDLED
;
343 else if (event
.m_id
== ACTION_MOUSE_WHEEL_UP
)
346 return EVENT_RESULT_HANDLED
;
348 else if (event
.m_id
== ACTION_MOUSE_WHEEL_DOWN
)
351 return EVENT_RESULT_HANDLED
;
353 else if (event
.m_id
== ACTION_GESTURE_NOTIFY
)
355 return (m_orientation
== HORIZONTAL
) ? EVENT_RESULT_PAN_HORIZONTAL_WITHOUT_INERTIA
: EVENT_RESULT_PAN_VERTICAL_WITHOUT_INERTIA
;
357 else if (event
.m_id
== ACTION_GESTURE_BEGIN
)
358 { // grab exclusive access
359 CGUIMessage
msg(GUI_MSG_EXCLUSIVE_MOUSE
, GetID(), GetParentID());
360 SendWindowMessage(msg
);
361 return EVENT_RESULT_HANDLED
;
363 else if (event
.m_id
== ACTION_GESTURE_PAN
)
365 SetFromPosition(point
);
366 return EVENT_RESULT_HANDLED
;
368 else if (event
.m_id
== ACTION_GESTURE_END
|| event
.m_id
== ACTION_GESTURE_ABORT
)
369 { // release exclusive access
370 CGUIMessage
msg(GUI_MSG_EXCLUSIVE_MOUSE
, 0, GetParentID());
371 SendWindowMessage(msg
);
372 return EVENT_RESULT_HANDLED
;
375 return EVENT_RESULT_UNHANDLED
;
378 std::string
GUIScrollBarControl::GetDescription() const
380 return StringUtils::Format("{}/{}", m_offset
, m_numItems
);
383 bool GUIScrollBarControl::UpdateColors(const CGUIListItem
* item
)
385 bool changed
= CGUIControl::UpdateColors(nullptr);
386 changed
|= m_guiBackground
->SetDiffuseColor(m_diffuseColor
);
387 changed
|= m_guiBarNoFocus
->SetDiffuseColor(m_diffuseColor
);
388 changed
|= m_guiBarFocus
->SetDiffuseColor(m_diffuseColor
);
389 changed
|= m_guiNibNoFocus
->SetDiffuseColor(m_diffuseColor
);
390 changed
|= m_guiNibFocus
->SetDiffuseColor(m_diffuseColor
);
395 bool GUIScrollBarControl::IsVisible() const
397 // page controls can be optionally disabled if the number of pages is 1
398 if (m_numItems
<= m_pageSize
&& !m_showOnePage
)
400 return CGUIControl::IsVisible();