list.lua 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. return function(BOXUI)
  2. local utils = BOXUI.utils
  3. local colorWhite = {1, 1, 1}
  4. local int = utils.int
  5. local max = utils.max
  6. local clamp = utils.clamp
  7. local ListContent = utils.newclass("list_content")
  8. local lcmt = ListContent
  9. ListContent.template_theme = {
  10. itemHeight = 32, itemPadVert = 8, itemPadHori = 8,
  11. itemBack = {0, 0, 1},
  12. itemFore = {0, 1, 0}, itemForeActive = {1, 1, 0},
  13. }
  14. ListContent.init = function(self, x, y, width, height)
  15. self.theme = BOXUI.theme
  16. self.x, self.y = x, y
  17. self.width, self.height = width, height
  18. self.items = nil
  19. self.itemActive = nil
  20. self.itemSelected = nil
  21. self.onClick = nil
  22. self.font = love.graphics.getFont()
  23. self.canvas = love.graphics.newCanvas(width, height)
  24. --self.canvas:setWrap("clampzero", "clampzero")
  25. self.quad = love.graphics.newQuad(0, 0, width, height, width, height)
  26. end
  27. ListContent.insert = function(self, text, context, refresh)
  28. local entry = {text = text, context = context}
  29. if not self.items then self.items = {} end
  30. table.insert(self.items, entry)
  31. if refresh == true then self:refresh() end
  32. end
  33. ListContent.setItems = function(self, t, refresh)
  34. self.items = t
  35. if refresh == true then self:refresh() end
  36. end
  37. ListContent.selectActiveItem = function(self, x, y)
  38. x, y = x - self.x, y - self.y
  39. if not self.items then return end
  40. local prevActive = self.itemActive
  41. local active = nil
  42. local theme = self.theme
  43. local height = theme.itemHeight
  44. local advance = height + theme.itemPadVert
  45. local relY = y % advance
  46. if relY >= 0 and relY < height then
  47. active = 1 + int(y / advance)
  48. if active < 1 or active > #self.items then active = nil end
  49. end
  50. self.itemActive = active
  51. if prevActive ~= active then
  52. return true
  53. end
  54. end
  55. ListContent.unfocus = function(self)
  56. self.itemActive = nil
  57. end
  58. ListContent.focus = function(self)
  59. end
  60. ListContent.mousemoved = function(self, x, y, dx, dy)
  61. self:selectActiveItem(x, y)
  62. end
  63. ListContent.mousepressed = function(self, x, y, button)
  64. if button ~= 1 then return end
  65. self.itemSelected = self.itemActive
  66. end
  67. ListContent.mousereleased = function(self, x, y, button)
  68. local selected, active = self.itemSelected, self.itemActive
  69. if button ~= 1 then return end
  70. if selected == nil then self:selectActiveItem(x, y) return end
  71. self.itemSelected = nil
  72. --self.itemActive = nil
  73. local item = self.items[selected]
  74. if selected == active then
  75. if item and self.onClick then
  76. self.onClick(item)
  77. end
  78. end
  79. end
  80. local lineWidth = function(font, text, icon, pad)
  81. local texttype = type(text)
  82. local w = icon and (icon:getWidth() + pad) or 0
  83. if texttype == "string" then
  84. w = w + font:getWidth(text)
  85. elseif texttype == "table" then
  86. for i = 2, #text, 2 do w = w + font:getWidth(text[i]) end
  87. end
  88. return w
  89. end
  90. ListContent.getLineWidth = function(self)
  91. local theme = self.theme
  92. local font = self.font
  93. local itemph = theme.itemPadHori
  94. local width = 0
  95. for i, v in ipairs(self.items) do
  96. width = max(width, lineWidth(font, v.text, v.icon, itemph))
  97. end
  98. return width + 2 * itemph
  99. end
  100. -- create a new canvas if required, return canvas size
  101. ListContent.fitCanvas = function(self)
  102. local selfw, selfh = self.width, self.height
  103. local canvas = self.canvas
  104. if not canvas then return selfw, selfh end
  105. local width, height = canvas:getDimensions()
  106. if selfw <= width and selfh <= height then return width, height end
  107. canvas:release()
  108. self.canvas = love.graphics.newCanvas(selfw, selfh)
  109. return selfw, selfh
  110. end
  111. ListContent.refresh = function(self, redraw)
  112. self.itemActive = nil
  113. self.itemSelected = nil
  114. local numitems = self.items and #self.items or 0
  115. local theme = self.theme
  116. if numitems == 0 then
  117. --self.canvas:renderTo(love.graphics.clear)
  118. self.width, self.height = theme.itemPadHori, theme.itemPadVert
  119. --self.overlay:renderTo(love.graphics.clear)
  120. return
  121. end
  122. local itemh = theme.itemHeight
  123. local itempv = theme.itemPadVert
  124. local itemph = theme.itemPadHori
  125. self.height = itemh * numitems + itempv * (numitems - 1)
  126. self.width = self:getLineWidth()
  127. self:fitCanvas()
  128. self.drawPassive = theme.draw.listContentPassive
  129. if redraw then self:redraw() end
  130. end
  131. ListContent.expand = function(self, width, height)
  132. if self.width < width then self.width = width end
  133. self:fitCanvas()
  134. end
  135. ListContent.redraw = function(self)
  136. local canvas = self.canvas
  137. if not canvas then return end
  138. love.graphics.setCanvas(canvas)
  139. love.graphics.clear()
  140. self:drawPassive(0, 0)
  141. love.graphics.setCanvas()
  142. end
  143. ListContent.updateViewport = function(self, vpx, vpy, vpw, vph)
  144. local canvas = self.canvas
  145. local width, height
  146. if self.canvas then width, height = canvas:getDimensions()
  147. else width, height = self.width, self.height end
  148. self.quad:setViewport(vpx, vpy, vpw, vph, width, height)
  149. end
  150. ListContent.draw = function(self, x, y)
  151. x, y = self.x + (x or 0), self.y + (y or 0)
  152. if self.canvas then
  153. love.graphics.setColor(colorWhite)
  154. love.graphics.draw(self.canvas, self.quad, x, y)
  155. else
  156. local vpx, vpy, vpw, vph = self.quad:getViewport()
  157. self:drawPassive(x - vpx, y - vpy, vpy, vpy + vph) -- not a typo; y = l1, l2
  158. end
  159. end
  160. BOXUI.add(ListContent)
  161. local Container = BOXUI.container
  162. local M = utils.newclass("list", Container)
  163. --utils.freshenprops(M.template_theme, default_theme)
  164. --local create = M.create
  165. M.init = function(self, x, y, width, height)
  166. Container.init(self, x, y, width, height)
  167. self.wrapHori = false
  168. self.wrapVert = false
  169. local list = ListContent.new(0,0, 10, 10)
  170. self.content = list
  171. self.list = list -- alias
  172. end
  173. M.insert = function(self, text, context, refresh)
  174. self.content:insert(text, context)
  175. if refresh == true then self:refresh(true) end
  176. end
  177. M.setItems = function(self, t, refresh)
  178. self.content:setItems(t)
  179. if refresh == true then self:refresh(true) end
  180. end
  181. M.refresh = function(self, redraw)
  182. Container.refresh(self, redraw)
  183. self.drawOverlay = self.theme.draw.listOverlay
  184. end
  185. BOXUI.add(M)
  186. end