menu.lua 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. --[[
  2. -- Menu Engine --
  3. Menu parameters (also available as members) :
  4. x, y, w, h:
  5. Viewport of the menu. (numbers)
  6. ox, oy:
  7. Offset of the menu. (numbers)
  8. current:
  9. Current selected element (integer)
  10. background:
  11. Color of the background table. (table[4]:{ r, g, b, a })
  12. locked:
  13. Locks the menu entry, thus, up and down are now catched by input instead
  14. of the menu. menu:pop() is ignored when menu is locked. (boolean)
  15. internal:
  16. Reserved for items, useful for shared stuff such as text fading. (table)
  17. default_height:
  18. Default item height if height is not defined in item. (number)
  19. funcs :
  20. Generic functions to draw stuff, required for basic stuff.
  21. - draw_background(menu) :
  22. Draws background (function)
  23. - draw_text(menu, text, color, x, y, w, align) :
  24. Prints a text at x, y (function)
  25. - simple_key(key) :
  26. Translates a key from "input" to a key readable by lynx
  27. (either "left", "right", "up", "down" or "enter")
  28. ]]
  29. local menu = require "classic":extend()
  30. -- Menu defaults
  31. menu.current = 1
  32. menu.background = { .25, .25, .25, .25 }
  33. menu.locked = false
  34. menu.default_height = 10
  35. menu.x, menu.y, menu.ox, menu.oy = 0, 0, 0, 0
  36. menu.w, menu.h = 0, 0
  37. function menu:new(items, params)
  38. if #items == 0 then
  39. -- We can't have no items.
  40. items[1] = { selectable = true }
  41. end
  42. self.items = items
  43. if params then
  44. for k,v in pairs(params) do
  45. self[k] = params[k]
  46. end
  47. end
  48. self.stack = {}
  49. self.internal = {}
  50. end
  51. function menu:current_item()
  52. return self.items[self.current]
  53. end
  54. function menu:push(new_menu)
  55. local pushed = {}
  56. for k,v in pairs(new_menu) do
  57. if k ~= "stack" then
  58. pushed[k] = self[k]
  59. self[k] = v
  60. end
  61. end
  62. self.stack[#self.stack + 1] = pushed
  63. end
  64. function menu:pop()
  65. -- Don't do anything if already in top menu or menu is locked.
  66. if #self.stack > 0 and not self.locked then
  67. -- Pop the menu
  68. local latest_menu = self.stack[#self.stack]
  69. -- Remove this menu from stack
  70. self.stack[#self.stack] = nil
  71. for k,v in pairs(latest_menu) do
  72. self[k] = v
  73. end
  74. end
  75. end
  76. function menu:up()
  77. if not self.locked then
  78. local old = self.current
  79. while self.current > 1 do
  80. self.current = self.current - 1
  81. if self:current_item().selectable ~= false then
  82. return
  83. end
  84. end
  85. self.current = old
  86. end
  87. end
  88. function menu:down()
  89. if not self.locked then
  90. local old = self.current
  91. while self.current < #self.items do
  92. self.current = self.current + 1
  93. if self:current_item().selectable then
  94. return
  95. end
  96. end
  97. self.current = old
  98. end
  99. end
  100. function menu:item_viewport(item)
  101. local found = false
  102. local y = 0
  103. -- Find which item it corresponds
  104. for i,it in ipairs(self.items) do
  105. if item == it then
  106. found = true
  107. break
  108. end
  109. y = y + (it.height or self.default_height)
  110. end
  111. if not found then
  112. return
  113. end
  114. -- ax, ay, bx, by
  115. return 0, y, self.w, y + (item.height or self.default_height)
  116. end
  117. function menu:update(dt)
  118. -- Update each items
  119. for _,item in ipairs(self.items) do
  120. if item.update then
  121. item:update(self, dt)
  122. end
  123. end
  124. end
  125. function menu:draw(x, y)
  126. local current_item = self:current_item()
  127. -- Draw each item
  128. local x, y = (x or self.x) + self.ox, (y or self.y) + self.oy
  129. local w = self.w - self.ox
  130. for i,item in ipairs(self.items) do
  131. local h = item.height or self.default_height
  132. if current_item == item then
  133. self.funcs.draw_background(self, x, y, w, h)
  134. end
  135. if item.draw then
  136. item:draw(self, x, y, w, h)
  137. end
  138. y = y + h
  139. end
  140. end
  141. function menu:input_key(key, state)
  142. local current_item = self:current_item()
  143. if not self.locked and state == "pressed" then
  144. -- Control menu navigation
  145. local simple_key = self.funcs.simple_key(key)
  146. if simple_key == "up" then
  147. self:up()
  148. return
  149. elseif simple_key == "down" then
  150. self:down()
  151. return
  152. end
  153. end
  154. if current_item and current_item.input then
  155. current_item:input(self, key, state)
  156. end
  157. end
  158. function menu:input_mouse(x, y, btn)
  159. local current_item = self:current_item()
  160. local vx, vy = self.x + self.ox, self.y + self.oy
  161. local vw, vh = self.w - self.ox, self.h - self.oy
  162. local item_y = 0
  163. if vx <= x and x <= vx + vw and vy <= y and y <= vy + vh then
  164. -- Mouse is inside the viewport, YAY
  165. if not self.locked then
  166. local vp_y = y - vy
  167. local y = 0
  168. -- Find which item it corresponds
  169. for i,item in ipairs(self.items) do
  170. y = y + (item.height or self.default_height)
  171. if vp_y < y then
  172. if self.items[i].selectable then
  173. self.current = i
  174. current_item = self:current_item()
  175. end
  176. break
  177. end
  178. item_y = item_y + (item.height or self.default_height)
  179. end
  180. end
  181. if current_item and current_item.mouse then
  182. current_item:mouse(self, x - vx, y - item_y - vy, btn)
  183. end
  184. end
  185. end
  186. function menu:input_text(string)
  187. local current_item = self:current_item()
  188. if current_item and current_item.text then
  189. current_item:text(self, string)
  190. end
  191. end
  192. function menu:__tostring()
  193. return "lynx.menu"
  194. end
  195. return menu