commands.lua 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. Menu_background_color = {r=0.6, g=0.8, b=0.6}
  2. Menu_border_color = {r=0.6, g=0.7, b=0.6}
  3. Menu_command_color = {r=0.2, g=0.2, b=0.2}
  4. Menu_highlight_color = {r=0.5, g=0.7, b=0.3}
  5. function source.draw_menu_bar()
  6. if App.run_tests then return end -- disable in tests
  7. App.color(Menu_background_color)
  8. love.graphics.rectangle('fill', 0,0, App.screen.width, Menu_status_bar_height)
  9. App.color(Menu_border_color)
  10. love.graphics.rectangle('line', 0,0, App.screen.width, Menu_status_bar_height)
  11. App.color(Menu_command_color)
  12. Menu_cursor = 5
  13. if Show_file_navigator then
  14. source.draw_file_navigator()
  15. return
  16. end
  17. add_hotkey_to_menu('ctrl+e: run')
  18. if Focus == 'edit' then
  19. add_hotkey_to_menu('ctrl+g: switch file')
  20. if Show_log_browser_side then
  21. add_hotkey_to_menu('ctrl+l: hide log browser')
  22. else
  23. add_hotkey_to_menu('ctrl+l: show log browser')
  24. end
  25. add_hotkey_to_menu('ctrl+k: clear logs')
  26. if Editor_state.expanded then
  27. add_hotkey_to_menu('alt+b: collapse debug prints')
  28. else
  29. add_hotkey_to_menu('alt+b: expand debug prints')
  30. end
  31. add_hotkey_to_menu('alt+d: create/edit debug print')
  32. add_hotkey_to_menu('ctrl+f: find in file')
  33. add_hotkey_to_menu('alt+left alt+right: prev/next word')
  34. elseif Focus == 'log_browser' then
  35. -- nothing yet
  36. else
  37. assert(false, 'unknown focus "'..Focus..'"')
  38. end
  39. add_hotkey_to_menu('ctrl+z ctrl+y: undo/redo')
  40. add_hotkey_to_menu('ctrl+x ctrl+c ctrl+v: cut/copy/paste')
  41. add_hotkey_to_menu('ctrl+= ctrl+- ctrl+0: zoom')
  42. end
  43. function add_hotkey_to_menu(s)
  44. local width = App.width(s)
  45. if Menu_cursor > App.screen.width - 30 then
  46. return
  47. end
  48. App.color(Menu_command_color)
  49. App.screen.print(s, Menu_cursor,5)
  50. Menu_cursor = Menu_cursor + width + 30
  51. end
  52. function source.draw_file_navigator()
  53. App.color(Menu_command_color)
  54. App.screen.print(File_navigation.filter, 5, 5)
  55. draw_cursor(5 + App.width(File_navigation.filter), 5)
  56. if File_navigation.num_lines == nil then
  57. File_navigation.num_lines = source.num_lines_for_file_navigator(File_navigation.candidates)
  58. end
  59. App.color(Menu_background_color)
  60. love.graphics.rectangle('fill', 0,Menu_status_bar_height, App.screen.width, File_navigation.num_lines * Editor_state.line_height + --[[highlight padding]] 2)
  61. local x,y = 5, Menu_status_bar_height
  62. for i,filename in ipairs(File_navigation.candidates) do
  63. x,y = add_file_to_menu(x,y, filename, i == File_navigation.index)
  64. if Menu_cursor >= App.screen.width - 5 then
  65. break
  66. end
  67. end
  68. end
  69. function draw_cursor(x, y)
  70. -- blink every 0.5s
  71. if math.floor(Cursor_time*2)%2 == 0 then
  72. App.color(Cursor_color)
  73. love.graphics.rectangle('fill', x,y, 3,Editor_state.line_height)
  74. end
  75. end
  76. function source.file_navigator_candidates()
  77. if File_navigation.filter == '' then
  78. return File_navigation.all_candidates
  79. end
  80. local result = {}
  81. for _,filename in ipairs(File_navigation.all_candidates) do
  82. if starts_with(filename, File_navigation.filter) then
  83. table.insert(result, filename)
  84. end
  85. end
  86. return result
  87. end
  88. function source.num_lines_for_file_navigator(candidates)
  89. local result = 1
  90. local x = 5
  91. for i,filename in ipairs(candidates) do
  92. local width = App.width(filename)
  93. if x + width > App.screen.width - 5 then
  94. result = result+1
  95. x = 5 + width
  96. else
  97. x = x + width + 30
  98. end
  99. end
  100. return result
  101. end
  102. function add_file_to_menu(x,y, s, cursor_highlight)
  103. local width = App.width(s)
  104. if x + width > App.screen.width - 5 then
  105. y = y + Editor_state.line_height
  106. x = 5
  107. end
  108. local color = Menu_background_color
  109. if cursor_highlight then
  110. color = Menu_highlight_color
  111. end
  112. button(Editor_state, 'menu', {x=x-5, y=y-2, w=width+5*2, h=Editor_state.line_height+2*2, bg=color,
  113. onpress1 = function()
  114. navigate_to_file(s)
  115. end
  116. })
  117. App.color(Menu_command_color)
  118. App.screen.print(s, x,y)
  119. x = x + width + 30
  120. return x,y
  121. end
  122. function navigate_to_file(s)
  123. move_candidate_to_front(s)
  124. source.switch_to_file(s..'.lua')
  125. love.window.setTitle('lines.love - source - '..Editor_state.filename)
  126. reset_file_navigator()
  127. end
  128. function move_candidate_to_front(s)
  129. local index = array.find(File_navigation.all_candidates, s)
  130. assert(index, 'file missing from manifest')
  131. table.remove(File_navigation.all_candidates, index)
  132. table.insert(File_navigation.all_candidates, 1, s)
  133. end
  134. function reset_file_navigator()
  135. Show_file_navigator = false
  136. File_navigation.index = 1
  137. File_navigation.filter = ''
  138. File_navigation.candidates = File_navigation.all_candidates
  139. end
  140. function keychord_press_on_file_navigator(chord, key)
  141. log(2, 'file navigator: '..chord)
  142. log(2, {name='file_navigator_state', files=File_navigation.candidates, index=File_navigation.index})
  143. if chord == 'escape' then
  144. reset_file_navigator()
  145. elseif chord == 'return' then
  146. navigate_to_file(File_navigation.candidates[File_navigation.index])
  147. elseif chord == 'backspace' then
  148. local len = utf8.len(File_navigation.filter)
  149. local byte_offset = Text.offset(File_navigation.filter, len)
  150. File_navigation.filter = string.sub(File_navigation.filter, 1, byte_offset-1)
  151. File_navigation.index = 1
  152. File_navigation.candidates = source.file_navigator_candidates()
  153. elseif chord == 'left' then
  154. if File_navigation.index > 1 then
  155. File_navigation.index = File_navigation.index-1
  156. end
  157. elseif chord == 'right' then
  158. if File_navigation.index < #File_navigation.candidates then
  159. File_navigation.index = File_navigation.index+1
  160. end
  161. elseif chord == 'down' then
  162. file_navigator_down()
  163. elseif chord == 'up' then
  164. file_navigator_up()
  165. end
  166. end
  167. function log_render.file_navigator_state(o, x,y, w)
  168. -- duplicate structure of source.draw_file_navigator
  169. local num_lines = source.num_lines_for_file_navigator(o.files)
  170. local h = num_lines * Editor_state.line_height
  171. App.color(Menu_background_color)
  172. love.graphics.rectangle('fill', x,y, w,h)
  173. -- compute the x,y,width of the current index (in offsets from top left)
  174. local x2,y2 = 0,0
  175. local width = 0
  176. for i,filename in ipairs(o.files) do
  177. width = App.width(filename)
  178. if x2 + width > App.screen.width - 5 then
  179. y2 = y2 + Editor_state.line_height
  180. x2 = 0
  181. end
  182. if i == o.index then
  183. break
  184. end
  185. x2 = x2 + width + 30
  186. end
  187. -- figure out how much of the menu to display
  188. local menu_xmin = math.max(0, x2-w/2)
  189. local menu_xmax = math.min(App.screen.width, x2+w/2)
  190. -- now selectively print out entries
  191. local x3,y3 = 0,y -- x3 is relative, y3 is absolute
  192. local width = 0
  193. for i,filename in ipairs(o.files) do
  194. width = App.width(filename)
  195. if x3 + width > App.screen.width - 5 then
  196. y3 = y3 + Editor_state.line_height
  197. x3 = 0
  198. end
  199. if i == o.index then
  200. App.color(Menu_highlight_color)
  201. love.graphics.rectangle('fill', x + x3-menu_xmin - 5, y3-2, width+5*2, Editor_state.line_height+2*2)
  202. end
  203. if x3 >= menu_xmin and x3 + width < menu_xmax then
  204. App.color(Menu_command_color)
  205. App.screen.print(filename, x + x3-menu_xmin, y3)
  206. end
  207. x3 = x3 + width + 30
  208. end
  209. --
  210. return h+20
  211. end
  212. function file_navigator_up()
  213. local y, x, width = file_coord(File_navigation.index)
  214. local index = file_index(y-Editor_state.line_height, x, width)
  215. if index then
  216. File_navigation.index = index
  217. end
  218. end
  219. function file_navigator_down()
  220. local y, x, width = file_coord(File_navigation.index)
  221. local index = file_index(y+Editor_state.line_height, x, width)
  222. if index then
  223. File_navigation.index = index
  224. end
  225. end
  226. function file_coord(index)
  227. local y,x = Menu_status_bar_height, 5
  228. for i,filename in ipairs(File_navigation.candidates) do
  229. local width = App.width(filename)
  230. if x + width > App.screen.width - 5 then
  231. y = y + Editor_state.line_height
  232. x = 5
  233. end
  234. if i == index then
  235. return y, x, width
  236. end
  237. x = x + width + 30
  238. end
  239. end
  240. function file_index(fy, fx, fwidth)
  241. log_start('file index')
  242. log(2, ('for %d %d %d'):format(fy, fx, fwidth))
  243. local y,x = Menu_status_bar_height, 5
  244. local best_guess, best_guess_x, best_guess_width
  245. for i,filename in ipairs(File_navigation.candidates) do
  246. local width = App.width(filename)
  247. if x + width > App.screen.width - 5 then
  248. y = y + Editor_state.line_height
  249. x = 5
  250. end
  251. if y == fy then
  252. log(2, ('%d: correct row; considering %d %s %d %d'):format(y, i, filename, x, width))
  253. if best_guess == nil then
  254. log(2, 'nil')
  255. best_guess = i
  256. best_guess_x = x
  257. best_guess_width = width
  258. elseif math.abs(fx + fwidth/2 - x - width/2) < math.abs(fx + fwidth/2 - best_guess_x - best_guess_width/2) then
  259. best_guess = i
  260. best_guess_x = x
  261. best_guess_width = width
  262. end
  263. log(2, ('best guess now %d %s %d %d'):format(best_guess, File_navigation.candidates[best_guess], best_guess_x, best_guess_width))
  264. end
  265. x = x + width + 30
  266. end
  267. log_end('file index')
  268. return best_guess
  269. end
  270. function text_input_on_file_navigator(t)
  271. File_navigation.filter = File_navigation.filter..t
  272. File_navigation.candidates = source.file_navigator_candidates()
  273. end