├── .gitignore
├── README.md
├── preview_slidebar.png
└── slidebar.lua
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Lua sources
2 | luac.out
3 |
4 | # luarocks build files
5 | *.src.rock
6 | *.zip
7 | *.tar.gz
8 |
9 | # Object files
10 | *.o
11 | *.os
12 | *.ko
13 | *.obj
14 | *.elf
15 |
16 | # Precompiled Headers
17 | *.gch
18 | *.pch
19 |
20 | # Libraries
21 | *.lib
22 | *.a
23 | *.la
24 | *.lo
25 | *.def
26 | *.exp
27 |
28 | # Shared objects (inc. Windows DLLs)
29 | *.dll
30 | *.so
31 | *.so.*
32 | *.dylib
33 |
34 | # Executables
35 | *.exe
36 | *.out
37 | *.app
38 | *.i*86
39 | *.x86_64
40 | *.hex
41 |
42 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Library slidebar
2 | DX library that allows you to easily create slidebar.
3 |
4 | # License
5 | #### This is library free, you can freely use and edit.
6 |
7 | # About
8 | This is a simple library for create slidebars which allows easy creation of some resources
9 |
10 | # How to use
11 | You need download the file ```slidebar.lua``` and put it in your project, but don't forget to load it in the meta.xml
12 |
13 | # Functions
14 | Create a new slidebar
15 | ```lua
16 | slidebar = createSlideBar(x, y, width, height, radiusBorder, minValue, maxValue, circleScale, postGUI)
17 | ```
18 | Preview
19 | 
20 |
21 | Destroy the slidebar
22 | ```lua
23 | slidebar:destroy()
24 | ```
25 | Change a property of slide bar
26 | ```lua
27 | slidebar:setProperty(property, value)
28 | ```
29 |
30 | Set a new offset
31 | ```lua
32 | slidebar:setScrollOffset(value)
33 | ```
34 |
35 | Get the scroll input
36 | ```lua
37 | slidebar:onScroll()
38 | ```
39 |
40 | Get the output the scroll input
41 | ```lua
42 | slidebar:onScrollEnd()
43 | ```
44 |
--------------------------------------------------------------------------------
/preview_slidebar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lodsdev/slidebar/80ff8411d7de51d813cf25a7eba40fc1a6586eb0/preview_slidebar.png
--------------------------------------------------------------------------------
/slidebar.lua:
--------------------------------------------------------------------------------
1 | -- small optimizations
2 | local tblInsert = table.insert
3 | local tblRemove = table.remove
4 | local strFormat = string.format
5 | local floor = math.floor
6 | local ceil = math.ceil
7 | local tblMax = table.maxn
8 | local max = math.max
9 | local min = math.min
10 | local interpolate = interpolateBetween
11 |
12 | local screenW, screenH = guiGetScreenSize()
13 |
14 | local scrollInput = {}
15 | local inputHover
16 | local posI, posF
17 | local typeProgress, tickSmooth
18 |
19 | scrollInput.inputs = {}
20 |
21 | -- create the scroll input
22 | function createSliderBar(x, y, width, height, indicatorScale, radiusBar, radiusIndicator, sizeStroke, minValue, maxValue, postGUI)
23 | if (not (x or y)) then
24 | local input = (not x and "Error in argument #1. Define a position X") or (not y and "Error in argument #2. Define a position Y")
25 | warn(input)
26 | end
27 | if (not (width or height)) then
28 | local input = (not width and "Error in argument #3. Define a width") or (not height and "Error in argument #4. Define a height")
29 | warn(input)
30 | end
31 | if ((width == 0) or (height == 0)) then
32 | local input = ((width == 0) and "Error in argument #3. Define a width greater than 0") or ((height == 0) and "Error in argument #4. Define a height greater than 0")
33 | warn(input)
34 | end
35 | if (minValue > maxValue) then
36 | warn("Error in argument #5. The min value don't can't be greater than max value")
37 | end
38 | if (maxValue < minValue) then
39 | warn("Error in argument #6. The max value don't can't be smaller than min value")
40 | end
41 |
42 | radius = radiusBar and height / 2 or 0
43 | indicatorScale = indicatorScale or 10
44 | radiusIndicator = radiusIndicator or 0
45 | sizeStroke = sizeStroke or 0
46 |
47 | local rawDataCircle = strFormat([[
48 |
51 | ]], indicatorScale, indicatorScale, radiusIndicator, indicatorScale, indicatorScale)
52 |
53 | local rawDataBar = strFormat([[
54 |
57 | ]], width, height, radius, width, height)
58 |
59 | local datas = {
60 | x = x,
61 | y = y,
62 | width = width,
63 | height = height,
64 | circlePos = 0,
65 | circleY = 0,
66 | minValue = minValue or 0,
67 | maxValue = maxValue or 100,
68 | radius = radius,
69 | indicatorScale = indicatorScale,
70 | bgBarColor = {255, 255, 255},
71 | barColor = {255, 255, 255},
72 | barColorHover = {255, 255, 255},
73 | indicatorColor = {255, 255, 255},
74 | borderColor = {255, 255, 255},
75 | sizeStroke = sizeStroke,
76 | postGUI = postGUI or false,
77 | circle = svgCreate(indicatorScale, indicatorScale, rawDataCircle),
78 | barInput = svgCreate(width, height, rawDataBar),
79 | scrolling = false,
80 | changeColorBar = false,
81 | animationIndicator = false,
82 | scrollOffset = 100,
83 | -- Events Methods
84 | scroll_event = false,
85 | scrollEnd_event = false,
86 | -- Technicals
87 | endedScrolling = true
88 | }
89 | datas.circlePos = (datas.x + datas.width)
90 | datas.circleY = datas.y + ((datas.height / 2) - (datas.indicatorScale / 2))
91 |
92 | setmetatable(datas, {__index = scrollInput})
93 | tblInsert(scrollInput.inputs, datas)
94 |
95 | if (tblMax(scrollInput.inputs) == 1) then
96 | addEventHandler('onClientRender', root, renderSlidebar, false, 'low-5')
97 | addEventHandler('onClientClick', root, clickSliderBar, false, 'low-5')
98 | addEventHandler('onClientKey', root, keySliderBar, false, 'low-5')
99 | end
100 | return datas
101 | end
102 |
103 | -- create vector images for the scroll input
104 | local function dxDrawSVG(svg, x, y, width, height, color, postGUI)
105 | if (not svg) then
106 | warn("Error in argument #1. Define a SVG.")
107 | end
108 | if (not (width or height)) then
109 | local input = (not width and 'Error in argument #2. Define a width') or (not height and 'Error in argument #3. Define a height')
110 | warn(input)
111 | end
112 |
113 | dxSetBlendMode('add')
114 | dxDrawImage(x, y, width, height, svg, 0, 0, 0, color, postGUI)
115 | dxSetBlendMode('blend')
116 | end
117 |
118 | -- render the scroll input
119 | function renderSlidebar()
120 | if (not scrollInput.inputs or (not (#scrollInput.inputs > 0))) then
121 | return
122 | end
123 |
124 | inputHover = nil
125 |
126 | for _, self in ipairs(scrollInput.inputs) do
127 | local value = ((self.circlePos - self.x) / self.width) * 100
128 | local barValue = (self.width / 100) * value
129 | local barSelectedColor = self.barColor
130 |
131 | if (typeProgress == 'in_progress') then
132 | local progress = (getTickCount()-tickSmooth)/250
133 | local newCirclePos = interpolate(posI, 0, 0, posF, 0, 0, progress, 'Linear')
134 |
135 | if (progress >= 1) then
136 | typeProgress = nil
137 | tickSmooth = nil
138 | posI = nil
139 | posF = nil
140 | end
141 |
142 | self.circlePos = newCirclePos
143 | end
144 |
145 | if (isCursorOnElement(self.x, self.y, ((barValue + self.indicatorScale) * self.width) / 100, self.height) or isCursorOnElement(self.circlePos - (self.indicatorScale/2), self.circleY, self.indicatorScale, self.indicatorScale)) then
146 | if (self.changeColorBar) then
147 | barSelectedColor = self.barColorHover
148 | end
149 | inputHover = self
150 | end
151 |
152 | if (isCursorOnElement(self.x, self.y, self.width, self.height)) then
153 | inputHover = self
154 | end
155 |
156 | if (self.scrolling) then
157 | local mx, _ = getCursorPosition()
158 | local cursorX = mx * screenW
159 |
160 | self.circlePos = clamp(cursorX, self.x, (self.x + self.width))
161 | self.scrollOffset = floor(clamp(((self.circlePos - self.x) / self.width) * 100, self.minValue, self.maxValue))
162 |
163 | if (self.scroll_event) then
164 | self.scroll_event(self.scrollOffset)
165 | end
166 |
167 | if (self.changeColorBar) then
168 | barSelectedColor = self.barColorHover
169 | end
170 | end
171 |
172 | dxDrawSVG(self.barInput, self.x, self.y, self.width, self.height, tocolor(unpack(self.bgBarColor)), self.postGUI) -- background bar
173 | dxDrawSVG(self.barInput, self.x, self.y, barValue, self.height, tocolor(unpack(barSelectedColor)), self.postGUI) -- bar
174 | -- dxDrawSVG(self.circle, (self.circlePos - (self.indicatorScale/2)), self.circleY, self.indicatorScale, self.indicatorScale, ((self.borderColor ~= self.indicatorColor) and tocolor(unpack(self.borderColor)) or tocolor(unpack(self.indicatorColor))), self.postGUI) -- indicator border
175 | dxDrawSVG(self.circle, self.circlePos - (self.indicatorScale/2), self.circleY, self.indicatorScale, self.indicatorScale, tocolor(unpack(self.borderColor)), self.postGUI)
176 |
177 | if (self.sizeStroke and self.sizeStroke ~= 0) then
178 | dxDrawSVG(self.circle, self.circlePos - (self.indicatorScale/2) + (self.sizeStroke/2), self.circleY + (self.sizeStroke/2), self.indicatorScale - self.sizeStroke, self.indicatorScale - self.sizeStroke, tocolor(unpack(self.indicatorColor)), self.postGUI)
179 | end
180 |
181 | if (getKeyState('mouse1')) then
182 | if (isCursorOnElement(self.circlePos - (self.indicatorScale/2), self.circleY, self.indicatorScale, self.indicatorScale)) then
183 | self.scrolling = true
184 |
185 | if (self.endedScrolling) then
186 | self.endedScrolling = false
187 | end
188 | end
189 | else
190 | if (not self.endedScrolling) then
191 | self.scrolling = false
192 | self.endedScrolling = true
193 |
194 | if (self.scrollEnd_event) then
195 | self.scrollEnd_event(self.scrollOffset)
196 | end
197 | end
198 | end
199 | end
200 | end
201 |
202 | -- function to check if the cursor is on the element and change the position of circle
203 | function clickSliderBar(button, state)
204 | if (not scrollInput.inputs or (not (#scrollInput.inputs > 0))) then
205 | return
206 | end
207 |
208 | if (inputHover) then
209 | if (button == 'left' and state == 'down') then
210 | local mx, _ = getCursorPosition()
211 | local cursorX = mx * screenW
212 |
213 | if (inputHover.smoothScroll) then
214 | local newCirclePos = clamp(cursorX, inputHover.x, (inputHover.x + inputHover.width))
215 | inputHover.scrollOffset = floor(clamp(((newCirclePos - inputHover.x) / inputHover.width) * 100, inputHover.minValue, inputHover.maxValue))
216 |
217 | typeProgress = 'in_progress'
218 | tickSmooth = getTickCount()
219 | posI, posF = inputHover.circlePos, newCirclePos
220 | else
221 | inputHover.circlePos = clamp(cursorX, inputHover.x, (inputHover.x + inputHover.width))
222 | inputHover.scrollOffset = floor(clamp(((inputHover.circlePos - inputHover.x) / inputHover.width) * 100, inputHover.minValue, inputHover.maxValue))
223 | end
224 |
225 | if (inputHover.scrollEnd_event) then
226 | inputHover.scrollEnd_event(inputHover.scrollOffset)
227 | end
228 | end
229 | end
230 | end
231 |
232 | -- function to change the offset of slidebar on scroll
233 | function keySliderBar(button, press)
234 | if (not scrollInput.inputs or (not (#scrollInput.inputs > 0))) then
235 | return
236 | end
237 |
238 | if (not press) then return end
239 |
240 | if (inputHover) then
241 | if (button == 'mouse_wheel_up' and inputHover.scrollOffset < inputHover.maxValue) then
242 | inputHover.scrollOffset = inputHover.scrollOffset + 1
243 | inputHover:setScrollOffset(inputHover.scrollOffset)
244 |
245 | if (inputHover.scroll_event) then
246 | inputHover.scroll_event(inputHover.scrollOffset)
247 | end
248 | elseif (button == 'mouse_wheel_down' and inputHover.scrollOffset > inputHover.minValue) then
249 | inputHover.scrollOffset = inputHover.scrollOffset - 1
250 | inputHover:setScrollOffset(inputHover.scrollOffset)
251 |
252 | if (inputHover.scroll_event) then
253 | inputHover.scroll_event(inputHover.scrollOffset)
254 | end
255 | end
256 | end
257 | end
258 |
259 | -- function to destroy the scroll input
260 | function scrollInput:destroy()
261 | if (not self) then
262 | warn("Error in argument #1. Define a object.")
263 | end
264 |
265 | for i, v in ipairs(scrollInput.inputs) do
266 | if (v == self) then
267 | -- free memory
268 | if (isElement(v.circle)) then
269 | destroyElement(v.circle)
270 | end
271 | if (isElement(v.barInput)) then
272 | destroyElement(v.barInput)
273 | end
274 | tblRemove(scrollInput.inputs, i)
275 | end
276 | end
277 |
278 | if (not (tblMax(scrollInput.inputs) > 0)) then
279 | removeEventHandler('onClientRender', root, renderSlidebar)
280 | removeEventHandler('onClientClick', root, clickSliderBar)
281 | removeEventHandler('onClientKey', root, keySliderBar)
282 | end
283 | end
284 |
285 | -- change the width of bar using the value of scrolloffset
286 | function scrollInput:setScrollOffset(value)
287 | if (not self) then
288 | warn("Error in argument #1. Define a object.")
289 | end
290 |
291 | if (not value) then
292 | warn("Error in argument #2. Defina a value.")
293 | end
294 |
295 | value = clamp(value, self.minValue, self.maxValue)
296 |
297 | if (self.smoothScroll) then
298 | self.scrollOffset = value
299 | local newCirclePos = self.x + ((self.width / self.maxValue) * self.scrollOffset)
300 |
301 | typeProgress = 'in_progress'
302 | tickSmooth = getTickCount()
303 | posI, posF = self.circlePos, newCirclePos
304 | else
305 | self.scrollOffset = value
306 | self.circlePos = self.x + ((self.width / self.maxValue) * self.scrollOffset)
307 | end
308 | end
309 |
310 | -- function to get the scroll input
311 | function scrollInput:onScroll(func)
312 | self.scroll_event = func
313 | end
314 |
315 | -- function to get output the scroll input
316 | function scrollInput:onScrollEnd(func)
317 | self.scrollEnd_event = func
318 | end
319 |
320 | local changeableProperties = {
321 | ['x'] = true,
322 | ['y'] = true,
323 | ['width'] = true,
324 | ['height'] = true,
325 | ['minValue'] = true,
326 | ['maxValue'] = true,
327 | ['scrollOffset'] = true,
328 | ['smoothScroll'] = true,
329 | ['bgBarColor'] = true,
330 | ['barColor'] = true,
331 | ['barColorHover'] = true,
332 | ['indicatorColor'] = true,
333 | ['borderColor'] = true,
334 | ['animationIndicator'] = true,
335 | -- ['strokeCircle'] = true,
336 | -- ['strokeindicatorColor'] = true,
337 | ['postGUI'] = true,
338 | }
339 |
340 | -- function to change the property of scrollbar
341 | ---comment
342 | ---@param property string
343 | ---@param value any
344 | ---@return boolean
345 | function scrollInput:setProperty(property, value)
346 | if (not self) then
347 | warn("No elements were found.")
348 | end
349 |
350 | if (not property or (type(property) ~= 'string')) then
351 | local input = (not property and "Error in argument #1. Define a property") or (type(property) ~= 'string' and "Error in argument #1. Define a valid property")
352 | warn(input)
353 | end
354 |
355 | for propertyIndex, active in pairs(changeableProperties) do
356 | if (propertyIndex == property and active) then
357 | self[propertyIndex] = value
358 | break
359 | end
360 | end
361 | end
362 |
363 | -- get the max and min of a value
364 | function clamp(number, min, max)
365 | if (number < min) then
366 | return min
367 | elseif (number > max) then
368 | return max
369 | end
370 | return number
371 | end
372 |
373 | -- function to send error in debugscript
374 | function warn(message)
375 | return error(tostring(message), 2)
376 | end
377 |
378 | -- function to get the mouse position absolute
379 | function isCursorOnElement(x, y, w, h)
380 | if (not isCursorShowing()) then return end
381 | local cursor = {getCursorPosition ()}
382 | local mx, my = cursor[1] * screenW, cursor[2] * screenH
383 | return (mx >= x and mx <= x + w and my >= y and my <= y + h)
384 | end
385 |
--------------------------------------------------------------------------------