buttonbar.lua 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. --Minetest
  2. --Copyright (C) 2014 sapier
  3. --Copyright (C) 2023 Gregor Parzefall
  4. --
  5. --This program is free software; you can redistribute it and/or modify
  6. --it under the terms of the GNU Lesser General Public License as published by
  7. --the Free Software Foundation; either version 2.1 of the License, or
  8. --(at your option) any later version.
  9. --
  10. --This program is distributed in the hope that it will be useful,
  11. --but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. --MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. --GNU Lesser General Public License for more details.
  14. --
  15. --You should have received a copy of the GNU Lesser General Public License along
  16. --with this program; if not, write to the Free Software Foundation, Inc.,
  17. --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  18. local BASE_SPACING = 0.1
  19. local function get_scroll_btn_width()
  20. return core.settings:get_bool("touch_gui") and 0.8 or 0.5
  21. end
  22. local function buttonbar_formspec(self)
  23. if self.hidden then
  24. return ""
  25. end
  26. local formspec = {
  27. string.format("box[%f,%f;%f,%f;%s]", self.pos.x, self.pos.y, self.size.x,
  28. self.size.y, self.bgcolor),
  29. }
  30. local btn_size = self.size.y - 2*BASE_SPACING
  31. -- Spacing works like CSS Flexbox with `justify-content: space-evenly;`.
  32. -- `BASE_SPACING` is used as the minimum spacing, like `gap` in CSS Flexbox.
  33. -- The number of buttons per page is always calculated as if the scroll
  34. -- buttons were visible.
  35. local avail_space = self.size.x - 2*BASE_SPACING - 2*get_scroll_btn_width()
  36. local btns_per_page = math.floor((avail_space - BASE_SPACING) / (btn_size + BASE_SPACING))
  37. self.num_pages = math.ceil(#self.buttons / btns_per_page)
  38. self.cur_page = math.min(self.cur_page, self.num_pages)
  39. local first_btn = (self.cur_page - 1) * btns_per_page + 1
  40. local show_scroll_btns = self.num_pages > 1
  41. -- In contrast, the button spacing calculation takes hidden scroll buttons
  42. -- into account.
  43. local real_avail_space = show_scroll_btns and avail_space or self.size.x
  44. local btn_spacing = (real_avail_space - btns_per_page * btn_size) / (btns_per_page + 1)
  45. local btn_start_x = self.pos.x + btn_spacing
  46. if show_scroll_btns then
  47. btn_start_x = btn_start_x + BASE_SPACING + get_scroll_btn_width()
  48. end
  49. for i = first_btn, first_btn + btns_per_page - 1 do
  50. local btn = self.buttons[i]
  51. if btn == nil then
  52. break
  53. end
  54. local btn_pos = {
  55. x = btn_start_x + (i - first_btn) * (btn_size + btn_spacing),
  56. y = self.pos.y + BASE_SPACING,
  57. }
  58. table.insert(formspec, string.format("image_button[%f,%f;%f,%f;%s;%s;%s;false;false]tooltip[%s;%s]",
  59. btn_pos.x, btn_pos.y, btn_size, btn_size, btn.image, btn.name,
  60. btn.caption, btn.name, btn.tooltip))
  61. end
  62. if show_scroll_btns then
  63. local btn_prev_pos = {
  64. x = self.pos.x + BASE_SPACING,
  65. y = self.pos.y + BASE_SPACING,
  66. }
  67. local btn_next_pos = {
  68. x = self.pos.x + self.size.x - BASE_SPACING - get_scroll_btn_width(),
  69. y = self.pos.y + BASE_SPACING,
  70. }
  71. table.insert(formspec, string.format("button[%f,%f;%f,%f;%s;<]",
  72. btn_prev_pos.x, btn_prev_pos.y, get_scroll_btn_width(), btn_size,
  73. self.btn_prev_name))
  74. table.insert(formspec, string.format("button[%f,%f;%f,%f;%s;>]",
  75. btn_next_pos.x, btn_next_pos.y, get_scroll_btn_width(), btn_size,
  76. self.btn_next_name))
  77. end
  78. return table.concat(formspec)
  79. end
  80. local function buttonbar_buttonhandler(self, fields)
  81. if fields[self.btn_prev_name] then
  82. if self.cur_page > 1 then
  83. self.cur_page = self.cur_page - 1
  84. return true
  85. elseif self.cur_page == 1 then
  86. self.cur_page = self.num_pages
  87. return true
  88. end
  89. end
  90. if fields[self.btn_next_name] then
  91. if self.cur_page < self.num_pages then
  92. self.cur_page = self.cur_page + 1
  93. return true
  94. elseif self.cur_page == self.num_pages then
  95. self.cur_page = 1
  96. return true
  97. end
  98. end
  99. for _, btn in ipairs(self.buttons) do
  100. if fields[btn.name] then
  101. return self.userbuttonhandler(fields)
  102. end
  103. end
  104. end
  105. local buttonbar_metatable = {
  106. handle_buttons = buttonbar_buttonhandler,
  107. handle_events = function(self, event) end,
  108. get_formspec = buttonbar_formspec,
  109. hide = function(self) self.hidden = true end,
  110. show = function(self) self.hidden = false end,
  111. delete = function(self) ui.delete(self) end,
  112. add_button = function(self, name, caption, image, tooltip)
  113. if caption == nil then caption = "" end
  114. if image == nil then image = "" end
  115. if tooltip == nil then tooltip = "" end
  116. table.insert(self.buttons, {
  117. name = name,
  118. caption = caption,
  119. image = image,
  120. tooltip = tooltip,
  121. })
  122. end,
  123. }
  124. buttonbar_metatable.__index = buttonbar_metatable
  125. function buttonbar_create(name, pos, size, bgcolor, cbf_buttonhandler)
  126. assert(type(name) == "string" )
  127. assert(type(pos) == "table" )
  128. assert(type(size) == "table" )
  129. assert(type(bgcolor) == "string" )
  130. assert(type(cbf_buttonhandler) == "function")
  131. local self = {}
  132. self.type = "addon"
  133. self.name = name
  134. self.pos = pos
  135. self.size = size
  136. self.bgcolor = bgcolor
  137. self.userbuttonhandler = cbf_buttonhandler
  138. self.hidden = false
  139. self.buttons = {}
  140. self.num_pages = 1
  141. self.cur_page = 1
  142. self.btn_prev_name = "btnbar_prev_" .. self.name
  143. self.btn_next_name = "btnbar_next_" .. self.name
  144. setmetatable(self, buttonbar_metatable)
  145. ui.add(self)
  146. return self
  147. end