Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ```lua
- -- states/battle.lua
- local Scrollbox = require("widgets.scrollbox")
- (...)
- -- Initialize
- logScrollbox = Scrollbox:new({
- x = 50,
- y = 230,
- width = config.resolution.width - 100,
- height = config.resolution.height - 330,
- items = allLogMessages,
- title = "Battle Log"
- })
- -- (...)
- -- Every frame
- function battleState:update(dt)
- BattleController.update(dt)
- -- Get the current battle state
- local state = BattleController.getBattleState()
- -- Check if there are new messages to add to our full history
- if state.logMessages and #state.logMessages > 0 then
- local currentCount = #allLogMessages
- local newCount = #state.logMessages
- -- Add any new messages to our complete history
- if newCount > currentCount then
- for i = currentCount + 1, newCount do
- table.insert(allLogMessages, state.logMessages[i])
- end
- -- Update scrollbox with all messages
- logScrollbox:setItems(allLogMessages)
- -- Always scroll to bottom when new messages are added
- logScrollbox:scrollToBottom()
- end
- end
- -- Update the scrollbox (for scrolling)
- logScrollbox:update(dt)
- end
- ```
- ```lua
- -- widgets/scrollbox.lua
- --[[
- A reusable Scrollbox widget for displaying scrollable content.
- Usage:
- local Scrollbox = require("widgets.scrollbox")
- local myScrollbox = Scrollbox:new({
- x = 50, y = 200,
- width = 400, height = 300,
- items = {"Line 1", "Line 2", "Line 3"}, -- List of text items to display
- title = "Battle Log" -- Optional title
- })
- --]]
- local Scrollbox = {}
- Scrollbox.__index = Scrollbox
- -- Constructor
- function Scrollbox:new(params)
- local obj = setmetatable({}, Scrollbox)
- -- Set defaults if params is missing or doesn't have keys
- obj.x = params.x or 0
- obj.y = params.y or 0
- obj.width = params.width or 400
- obj.height = params.height or 300
- obj.padding = params.padding or 10
- obj.items = params.items or {}
- obj.title = params.title or nil
- obj.lineHeight = params.lineHeight or 20
- obj.fontSize = params.fontSize or 16
- -- Colors
- obj.backgroundColor = params.backgroundColor or {0.1, 0.1, 0.1, 0.8}
- obj.borderColor = params.borderColor or {0.6, 0.6, 0.6, 1}
- obj.textColor = params.textColor or {1, 1, 1, 1}
- obj.scrollbarColor = params.scrollbarColor or {0.6, 0.6, 0.6, 0.8}
- obj.scrollbarActiveColor = params.scrollbarActiveColor or {0.8, 0.8, 0.8, 1}
- obj.titleColor = params.titleColor or {1, 0.8, 0.2, 1}
- -- Scrolling state
- obj.scrollOffset = 0
- obj.isScrolling = false
- obj.scrollbarWidth = params.scrollbarWidth or 15
- -- Fonts
- obj.font = love.graphics.newFont(obj.fontSize)
- obj.titleFont = love.graphics.newFont(obj.fontSize + 2)
- return obj
- end
- function Scrollbox:update(dt)
- -- Calculate the max content height
- self.contentHeight = #self.items * self.lineHeight
- -- Calculate the max scroll range
- self.maxScroll = math.max(0, self.contentHeight - (self.height - 2*self.padding))
- -- Ensure scroll offset is in range
- self.scrollOffset = math.max(0, math.min(self.scrollOffset, self.maxScroll))
- end
- function Scrollbox:draw()
- local contentX = self.x + self.padding
- local contentY = self.y + self.padding
- local contentWidth = self.width - (2 * self.padding) - (self:hasScrollbar() and self.scrollbarWidth or 0)
- local contentHeight = self.height - (2 * self.padding)
- -- Draw background
- love.graphics.setColor(self.backgroundColor)
- love.graphics.rectangle("fill", self.x, self.y, self.width, self.height)
- -- Draw border
- love.graphics.setColor(self.borderColor)
- love.graphics.rectangle("line", self.x, self.y, self.width, self.height)
- -- Draw title if present
- if self.title then
- local prevFont = love.graphics.getFont()
- love.graphics.setFont(self.titleFont)
- love.graphics.setColor(self.titleColor)
- love.graphics.print(self.title, contentX, self.y - self.titleFont:getHeight() - 2)
- love.graphics.setFont(prevFont)
- end
- -- Create a stencil for clipping content
- love.graphics.stencil(function()
- love.graphics.rectangle("fill", contentX, contentY, contentWidth, contentHeight)
- end, "replace", 1)
- love.graphics.setStencilTest("greater", 0)
- -- Draw items
- love.graphics.setColor(self.textColor)
- local prevFont = love.graphics.getFont()
- love.graphics.setFont(self.font)
- for i, item in ipairs(self.items) do
- local itemY = contentY + ((i - 1) * self.lineHeight) - self.scrollOffset
- if itemY >= contentY - self.lineHeight and itemY <= contentY + contentHeight then
- love.graphics.printf(item, contentX, itemY, contentWidth, "left")
- end
- end
- love.graphics.setFont(prevFont)
- -- Disable stencil test
- love.graphics.setStencilTest()
- -- Draw scrollbar if needed
- if self:hasScrollbar() then
- local scrollbarHeight = self:getScrollbarHeight()
- local scrollbarY = self:getScrollbarPosition()
- -- Draw scrollbar background track
- love.graphics.setColor({0.15, 0.15, 0.15, 0.8}) -- Darker background for track
- love.graphics.rectangle("fill",
- self.x + self.width - self.scrollbarWidth - 1,
- contentY,
- self.scrollbarWidth,
- contentHeight
- )
- -- Draw scrollbar handle with more visible appearance
- if self.isScrolling then
- love.graphics.setColor({0.9, 0.9, 0.9, 1.0}) -- Brighter when active
- else
- love.graphics.setColor({0.7, 0.7, 0.7, 0.9}) -- More visible when inactive
- end
- love.graphics.rectangle("fill",
- self.x + self.width - self.scrollbarWidth - 1,
- scrollbarY,
- self.scrollbarWidth,
- scrollbarHeight,
- 4, 4 -- Rounded corners for handle
- )
- -- Draw scrollbar border
- love.graphics.setColor(self.borderColor)
- love.graphics.rectangle("line",
- self.x + self.width - self.scrollbarWidth - 1,
- contentY,
- self.scrollbarWidth,
- contentHeight
- )
- end
- end
- function Scrollbox:hasScrollbar()
- return self.contentHeight > (self.height - 2*self.padding)
- end
- function Scrollbox:getScrollbarHeight()
- if not self:hasScrollbar() then return 0 end
- local contentViewRatio = (self.height - 2*self.padding) / self.contentHeight
- local scrollbarHeight = math.max(30, contentViewRatio * (self.height - 2*self.padding))
- return scrollbarHeight
- end
- function Scrollbox:getScrollbarPosition()
- if not self:hasScrollbar() then return self.y + self.padding end
- local scrollRatio = self.scrollOffset / self.maxScroll
- local scrollableTrackHeight = (self.height - 2*self.padding) - self:getScrollbarHeight()
- local scrollbarY = (self.y + self.padding) + (scrollRatio * scrollableTrackHeight)
- return scrollbarY
- end
- function Scrollbox:positionToScroll(y)
- if not self:hasScrollbar() then return 0 end
- local scrollbarHeight = self:getScrollbarHeight()
- local scrollableTrackHeight = (self.height - 2*self.padding) - scrollbarHeight
- local trackY = y - (self.y + self.padding)
- local scrollRatio = math.max(0, math.min(1, trackY / scrollableTrackHeight))
- return scrollRatio * self.maxScroll
- end
- function Scrollbox:mousepressed(mx, my, buttonNum)
- -- Check if mouse is over scrollbox content area first (for dragging content directly)
- if buttonNum == 1 and self:isMouseOver(mx, my) then
- -- Check if we're clicking the scrollbar
- if self:hasScrollbar() then
- local scrollbarX = self.x + self.width - self.scrollbarWidth - 1
- local scrollbarY = self:getScrollbarPosition()
- local scrollbarHeight = self:getScrollbarHeight()
- local contentY = self.y + self.padding
- -- Check if mouse is on scrollbar area
- if mx >= scrollbarX and mx <= scrollbarX + self.scrollbarWidth and
- my >= contentY and my <= contentY + (self.height - 2*self.padding) then
- -- Check if mouse is on scrollbar handle (for dragging)
- if my >= scrollbarY and my <= scrollbarY + scrollbarHeight then
- self.isScrolling = true
- self.scrollStartY = my
- self.scrollStartOffset = self.scrollOffset
- else
- -- Click on the track - jump to position
- self.scrollOffset = self:positionToScroll(my)
- end
- return true
- end
- end
- -- Regular click inside content area
- return true
- end
- return false
- end
- function Scrollbox:mousereleased(mx, my, buttonNum)
- if buttonNum == 1 and self.isScrolling then
- self.isScrolling = false
- return true
- end
- return false
- end
- function Scrollbox:mousemoved(mx, my, dx, dy)
- if self.isScrolling then
- local scrollableTrackHeight = (self.height - 2*self.padding) - self:getScrollbarHeight()
- local moveRatio = scrollableTrackHeight / self.maxScroll
- local deltaScroll = (my - self.scrollStartY) / moveRatio
- self.scrollOffset = math.max(0, math.min(self.maxScroll, self.scrollStartOffset + deltaScroll))
- return true
- end
- return false
- end
- function Scrollbox:wheelmoved(x, y)
- -- Handle mouse wheel scrolling (y is typically -1 or 1)
- if self:isMouseOver(love.mouse.getPosition()) then
- -- Multiply by a factor to make scrolling more noticeable
- local scrollFactor = 3
- self.scrollOffset = math.max(0, math.min(self.maxScroll, self.scrollOffset - y * self.lineHeight * scrollFactor))
- return true
- end
- return false
- end
- function Scrollbox:isMouseOver(mx, my)
- return mx >= self.x and mx <= self.x + self.width and
- my >= self.y and my <= self.y + self.height
- end
- -- Methods to modify content
- function Scrollbox:setItems(items)
- self.items = items or {}
- self:update(0)
- end
- function Scrollbox:addItem(item)
- table.insert(self.items, item)
- self:update(0)
- end
- function Scrollbox:clearItems()
- self.items = {}
- self.scrollOffset = 0
- self:update(0)
- end
- function Scrollbox:scrollToBottom()
- self.scrollOffset = self.maxScroll
- end
- -- Calculate how many items can be displayed based on container size
- function Scrollbox:getVisibleItemCount()
- return math.floor((self.height - 2*self.padding) / self.lineHeight)
- end
- return Scrollbox
- ```
Advertisement
Add Comment
Please, Sign In to add comment