Guest User

LÖVE scrollbox widget

a guest
Mar 26th, 2025
36
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.90 KB | Source Code | 0 0
  1.  
  2. ```lua
  3. -- states/battle.lua
  4. local Scrollbox = require("widgets.scrollbox")
  5.  
  6. (...)
  7.  
  8. -- Initialize
  9. logScrollbox = Scrollbox:new({
  10. x = 50,
  11. y = 230,
  12. width = config.resolution.width - 100,
  13. height = config.resolution.height - 330,
  14. items = allLogMessages,
  15. title = "Battle Log"
  16. })
  17.  
  18. -- (...)
  19.  
  20. -- Every frame
  21. function battleState:update(dt)
  22. BattleController.update(dt)
  23.  
  24. -- Get the current battle state
  25. local state = BattleController.getBattleState()
  26.  
  27. -- Check if there are new messages to add to our full history
  28. if state.logMessages and #state.logMessages > 0 then
  29. local currentCount = #allLogMessages
  30. local newCount = #state.logMessages
  31.  
  32. -- Add any new messages to our complete history
  33. if newCount > currentCount then
  34. for i = currentCount + 1, newCount do
  35. table.insert(allLogMessages, state.logMessages[i])
  36. end
  37.  
  38. -- Update scrollbox with all messages
  39. logScrollbox:setItems(allLogMessages)
  40.  
  41. -- Always scroll to bottom when new messages are added
  42. logScrollbox:scrollToBottom()
  43. end
  44. end
  45.  
  46. -- Update the scrollbox (for scrolling)
  47. logScrollbox:update(dt)
  48. end
  49. ```
  50.  
  51.  
  52. ```lua
  53. -- widgets/scrollbox.lua
  54. --[[
  55. A reusable Scrollbox widget for displaying scrollable content.
  56. Usage:
  57. local Scrollbox = require("widgets.scrollbox")
  58. local myScrollbox = Scrollbox:new({
  59. x = 50, y = 200,
  60. width = 400, height = 300,
  61. items = {"Line 1", "Line 2", "Line 3"}, -- List of text items to display
  62. title = "Battle Log" -- Optional title
  63. })
  64. --]]
  65.  
  66. local Scrollbox = {}
  67. Scrollbox.__index = Scrollbox
  68.  
  69. -- Constructor
  70. function Scrollbox:new(params)
  71. local obj = setmetatable({}, Scrollbox)
  72.  
  73. -- Set defaults if params is missing or doesn't have keys
  74. obj.x = params.x or 0
  75. obj.y = params.y or 0
  76. obj.width = params.width or 400
  77. obj.height = params.height or 300
  78. obj.padding = params.padding or 10
  79. obj.items = params.items or {}
  80. obj.title = params.title or nil
  81. obj.lineHeight = params.lineHeight or 20
  82. obj.fontSize = params.fontSize or 16
  83.  
  84. -- Colors
  85. obj.backgroundColor = params.backgroundColor or {0.1, 0.1, 0.1, 0.8}
  86. obj.borderColor = params.borderColor or {0.6, 0.6, 0.6, 1}
  87. obj.textColor = params.textColor or {1, 1, 1, 1}
  88. obj.scrollbarColor = params.scrollbarColor or {0.6, 0.6, 0.6, 0.8}
  89. obj.scrollbarActiveColor = params.scrollbarActiveColor or {0.8, 0.8, 0.8, 1}
  90. obj.titleColor = params.titleColor or {1, 0.8, 0.2, 1}
  91.  
  92. -- Scrolling state
  93. obj.scrollOffset = 0
  94. obj.isScrolling = false
  95. obj.scrollbarWidth = params.scrollbarWidth or 15
  96.  
  97. -- Fonts
  98. obj.font = love.graphics.newFont(obj.fontSize)
  99. obj.titleFont = love.graphics.newFont(obj.fontSize + 2)
  100.  
  101. return obj
  102. end
  103.  
  104. function Scrollbox:update(dt)
  105. -- Calculate the max content height
  106. self.contentHeight = #self.items * self.lineHeight
  107.  
  108. -- Calculate the max scroll range
  109. self.maxScroll = math.max(0, self.contentHeight - (self.height - 2*self.padding))
  110.  
  111. -- Ensure scroll offset is in range
  112. self.scrollOffset = math.max(0, math.min(self.scrollOffset, self.maxScroll))
  113. end
  114.  
  115. function Scrollbox:draw()
  116. local contentX = self.x + self.padding
  117. local contentY = self.y + self.padding
  118. local contentWidth = self.width - (2 * self.padding) - (self:hasScrollbar() and self.scrollbarWidth or 0)
  119. local contentHeight = self.height - (2 * self.padding)
  120.  
  121. -- Draw background
  122. love.graphics.setColor(self.backgroundColor)
  123. love.graphics.rectangle("fill", self.x, self.y, self.width, self.height)
  124.  
  125. -- Draw border
  126. love.graphics.setColor(self.borderColor)
  127. love.graphics.rectangle("line", self.x, self.y, self.width, self.height)
  128.  
  129. -- Draw title if present
  130. if self.title then
  131. local prevFont = love.graphics.getFont()
  132. love.graphics.setFont(self.titleFont)
  133. love.graphics.setColor(self.titleColor)
  134. love.graphics.print(self.title, contentX, self.y - self.titleFont:getHeight() - 2)
  135. love.graphics.setFont(prevFont)
  136. end
  137.  
  138. -- Create a stencil for clipping content
  139. love.graphics.stencil(function()
  140. love.graphics.rectangle("fill", contentX, contentY, contentWidth, contentHeight)
  141. end, "replace", 1)
  142.  
  143. love.graphics.setStencilTest("greater", 0)
  144.  
  145. -- Draw items
  146. love.graphics.setColor(self.textColor)
  147. local prevFont = love.graphics.getFont()
  148. love.graphics.setFont(self.font)
  149.  
  150. for i, item in ipairs(self.items) do
  151. local itemY = contentY + ((i - 1) * self.lineHeight) - self.scrollOffset
  152. if itemY >= contentY - self.lineHeight and itemY <= contentY + contentHeight then
  153. love.graphics.printf(item, contentX, itemY, contentWidth, "left")
  154. end
  155. end
  156.  
  157. love.graphics.setFont(prevFont)
  158.  
  159. -- Disable stencil test
  160. love.graphics.setStencilTest()
  161.  
  162. -- Draw scrollbar if needed
  163. if self:hasScrollbar() then
  164. local scrollbarHeight = self:getScrollbarHeight()
  165. local scrollbarY = self:getScrollbarPosition()
  166.  
  167. -- Draw scrollbar background track
  168. love.graphics.setColor({0.15, 0.15, 0.15, 0.8}) -- Darker background for track
  169. love.graphics.rectangle("fill",
  170. self.x + self.width - self.scrollbarWidth - 1,
  171. contentY,
  172. self.scrollbarWidth,
  173. contentHeight
  174. )
  175.  
  176. -- Draw scrollbar handle with more visible appearance
  177. if self.isScrolling then
  178. love.graphics.setColor({0.9, 0.9, 0.9, 1.0}) -- Brighter when active
  179. else
  180. love.graphics.setColor({0.7, 0.7, 0.7, 0.9}) -- More visible when inactive
  181. end
  182.  
  183. love.graphics.rectangle("fill",
  184. self.x + self.width - self.scrollbarWidth - 1,
  185. scrollbarY,
  186. self.scrollbarWidth,
  187. scrollbarHeight,
  188. 4, 4 -- Rounded corners for handle
  189. )
  190.  
  191. -- Draw scrollbar border
  192. love.graphics.setColor(self.borderColor)
  193. love.graphics.rectangle("line",
  194. self.x + self.width - self.scrollbarWidth - 1,
  195. contentY,
  196. self.scrollbarWidth,
  197. contentHeight
  198. )
  199. end
  200. end
  201.  
  202. function Scrollbox:hasScrollbar()
  203. return self.contentHeight > (self.height - 2*self.padding)
  204. end
  205.  
  206. function Scrollbox:getScrollbarHeight()
  207. if not self:hasScrollbar() then return 0 end
  208.  
  209. local contentViewRatio = (self.height - 2*self.padding) / self.contentHeight
  210. local scrollbarHeight = math.max(30, contentViewRatio * (self.height - 2*self.padding))
  211. return scrollbarHeight
  212. end
  213.  
  214. function Scrollbox:getScrollbarPosition()
  215. if not self:hasScrollbar() then return self.y + self.padding end
  216.  
  217. local scrollRatio = self.scrollOffset / self.maxScroll
  218. local scrollableTrackHeight = (self.height - 2*self.padding) - self:getScrollbarHeight()
  219. local scrollbarY = (self.y + self.padding) + (scrollRatio * scrollableTrackHeight)
  220. return scrollbarY
  221. end
  222.  
  223. function Scrollbox:positionToScroll(y)
  224. if not self:hasScrollbar() then return 0 end
  225.  
  226. local scrollbarHeight = self:getScrollbarHeight()
  227. local scrollableTrackHeight = (self.height - 2*self.padding) - scrollbarHeight
  228. local trackY = y - (self.y + self.padding)
  229. local scrollRatio = math.max(0, math.min(1, trackY / scrollableTrackHeight))
  230.  
  231. return scrollRatio * self.maxScroll
  232. end
  233.  
  234. function Scrollbox:mousepressed(mx, my, buttonNum)
  235. -- Check if mouse is over scrollbox content area first (for dragging content directly)
  236. if buttonNum == 1 and self:isMouseOver(mx, my) then
  237. -- Check if we're clicking the scrollbar
  238. if self:hasScrollbar() then
  239. local scrollbarX = self.x + self.width - self.scrollbarWidth - 1
  240. local scrollbarY = self:getScrollbarPosition()
  241. local scrollbarHeight = self:getScrollbarHeight()
  242. local contentY = self.y + self.padding
  243.  
  244. -- Check if mouse is on scrollbar area
  245. if mx >= scrollbarX and mx <= scrollbarX + self.scrollbarWidth and
  246. my >= contentY and my <= contentY + (self.height - 2*self.padding) then
  247. -- Check if mouse is on scrollbar handle (for dragging)
  248. if my >= scrollbarY and my <= scrollbarY + scrollbarHeight then
  249. self.isScrolling = true
  250. self.scrollStartY = my
  251. self.scrollStartOffset = self.scrollOffset
  252. else
  253. -- Click on the track - jump to position
  254. self.scrollOffset = self:positionToScroll(my)
  255. end
  256. return true
  257. end
  258. end
  259.  
  260. -- Regular click inside content area
  261. return true
  262. end
  263. return false
  264. end
  265.  
  266. function Scrollbox:mousereleased(mx, my, buttonNum)
  267. if buttonNum == 1 and self.isScrolling then
  268. self.isScrolling = false
  269. return true
  270. end
  271. return false
  272. end
  273.  
  274. function Scrollbox:mousemoved(mx, my, dx, dy)
  275. if self.isScrolling then
  276. local scrollableTrackHeight = (self.height - 2*self.padding) - self:getScrollbarHeight()
  277. local moveRatio = scrollableTrackHeight / self.maxScroll
  278. local deltaScroll = (my - self.scrollStartY) / moveRatio
  279.  
  280. self.scrollOffset = math.max(0, math.min(self.maxScroll, self.scrollStartOffset + deltaScroll))
  281. return true
  282. end
  283. return false
  284. end
  285.  
  286. function Scrollbox:wheelmoved(x, y)
  287. -- Handle mouse wheel scrolling (y is typically -1 or 1)
  288. if self:isMouseOver(love.mouse.getPosition()) then
  289. -- Multiply by a factor to make scrolling more noticeable
  290. local scrollFactor = 3
  291. self.scrollOffset = math.max(0, math.min(self.maxScroll, self.scrollOffset - y * self.lineHeight * scrollFactor))
  292. return true
  293. end
  294. return false
  295. end
  296.  
  297. function Scrollbox:isMouseOver(mx, my)
  298. return mx >= self.x and mx <= self.x + self.width and
  299. my >= self.y and my <= self.y + self.height
  300. end
  301.  
  302. -- Methods to modify content
  303. function Scrollbox:setItems(items)
  304. self.items = items or {}
  305. self:update(0)
  306. end
  307.  
  308. function Scrollbox:addItem(item)
  309. table.insert(self.items, item)
  310. self:update(0)
  311. end
  312.  
  313. function Scrollbox:clearItems()
  314. self.items = {}
  315. self.scrollOffset = 0
  316. self:update(0)
  317. end
  318.  
  319. function Scrollbox:scrollToBottom()
  320. self.scrollOffset = self.maxScroll
  321. end
  322.  
  323. -- Calculate how many items can be displayed based on container size
  324. function Scrollbox:getVisibleItemCount()
  325. return math.floor((self.height - 2*self.padding) / self.lineHeight)
  326. end
  327.  
  328. return Scrollbox
  329. ```
Advertisement
Add Comment
Please, Sign In to add comment