Revert "deemphasize the source editor"
[lines.love.git] / source.lua
blobd9e44b6c29f5f439747f7afd9f937bb832c1b2fe
1 source = {}
3 Editor_state = {}
5 -- called both in tests and real run
6 function source.initialize_globals()
7 -- tests currently mostly clear their own state
9 Show_log_browser_side = false
10 Focus = 'edit'
11 Show_file_navigator = false
12 File_navigation = {
13 all_candidates = {
14 'run',
15 'run_tests',
16 'log',
17 'edit',
18 'drawing',
19 'help',
20 'text',
21 'search',
22 'select',
23 'undo',
24 'text_tests',
25 'geom',
26 'drawing_tests',
27 'file',
28 'source',
29 'source_tests',
30 'commands',
31 'log_browser',
32 'source_edit',
33 'source_text',
34 'source_undo',
35 'colorize',
36 'source_text_tests',
37 'source_file',
38 'main',
39 'button',
40 'keychord',
41 'app',
42 'test',
43 'json',
45 index = 1,
46 filter = '',
47 cursors = {}, -- filename to cursor1, screen_top1
49 File_navigation.candidates = File_navigation.all_candidates -- modified with filter
51 Menu_status_bar_height = 5 + --[[line height in tests]] 15 + 5
53 -- blinking cursor
54 Cursor_time = 0
55 end
57 -- called only for real run
58 function source.initialize()
59 log_new('source')
60 if Settings and Settings.source then
61 source.load_settings()
62 else
63 source.initialize_default_settings()
64 end
66 source.initialize_edit_side()
67 source.initialize_log_browser_side()
69 Menu_status_bar_height = 5 + Editor_state.line_height + 5
70 Editor_state.top = Editor_state.top + Menu_status_bar_height
71 Log_browser_state.top = Log_browser_state.top + Menu_status_bar_height
75 -- keep a few blank lines around: https://merveilles.town/@akkartik/110084833821965708
76 love.window.setTitle('lines.love - source')
80 end
82 -- environment for a mutable file
83 -- TODO: some initialization is also happening in load_settings/initialize_default_settings. Clean that up.
84 function source.initialize_edit_side()
85 load_from_disk(Editor_state)
86 Text.redraw_all(Editor_state)
87 if File_navigation.cursors[Editor_state.filename] then
88 Editor_state.screen_top1 = File_navigation.cursors[Editor_state.filename].screen_top1
89 Editor_state.cursor1 = File_navigation.cursors[Editor_state.filename].cursor1
90 else
91 Editor_state.screen_top1 = {line=1, pos=1}
92 Editor_state.cursor1 = {line=1, pos=1}
93 end
94 edit.check_locs(Editor_state)
96 if Editor_state.cursor1.line > #Editor_state.lines then
97 Editor_state.cursor1 = {line=1, pos=1}
98 end
99 if Editor_state.screen_top1.line > #Editor_state.lines then
100 Editor_state.screen_top1 = {line=1, pos=1}
103 if rawget(_G, 'jit') then
104 jit.off()
105 jit.flush()
109 function print_and_log(s)
110 print(s)
111 log(3, s)
114 function source.load_settings()
115 local settings = Settings.source
116 love.graphics.setFont(love.graphics.newFont(settings.font_height))
117 -- set up desired window dimensions and make window resizable
118 _, _, App.screen.flags = App.screen.size()
119 App.screen.flags.resizable = true
120 App.screen.width, App.screen.height = settings.width, settings.height
121 App.screen.resize(App.screen.width, App.screen.height, App.screen.flags)
122 source.set_window_position_from_settings(settings)
123 Show_log_browser_side = settings.show_log_browser_side
124 local right = App.screen.width - Margin_right
125 if Show_log_browser_side then
126 right = App.screen.width/2 - Margin_right
128 Editor_state = edit.initialize_state(Margin_top, Margin_left, right, settings.font_height, math.floor(settings.font_height*1.3))
129 Editor_state.filename = settings.filename
130 Editor_state.filename = basename(Editor_state.filename) -- migrate settings that used full paths; we now support only relative paths within the app
131 if settings.cursors then
132 File_navigation.cursors = settings.cursors
133 Editor_state.screen_top1 = File_navigation.cursors[Editor_state.filename].screen_top1
134 Editor_state.cursor1 = File_navigation.cursors[Editor_state.filename].cursor1
135 else
136 -- migrate old settings
137 Editor_state.screen_top1 = {line=1, pos=1}
138 Editor_state.cursor1 = {line=1, pos=1}
142 function source.set_window_position_from_settings(settings)
143 local os = love.system.getOS()
144 if os == 'Linux' then
145 -- love.window.setPosition doesn't quite seem to do what is asked of it on Linux.
146 App.screen.move(settings.x, settings.y-37, settings.displayindex)
147 else
148 App.screen.move(settings.x, settings.y, settings.displayindex)
152 function source.initialize_default_settings()
153 local font_height = 20
154 love.graphics.setFont(love.graphics.newFont(font_height))
155 source.initialize_window_geometry()
156 Editor_state = edit.initialize_state(Margin_top, Margin_left, App.screen.width-Margin_right)
157 Editor_state.filename = 'run.lua'
158 Editor_state.font_height = font_height
159 Editor_state.line_height = math.floor(font_height*1.3)
162 function source.initialize_window_geometry()
163 -- Initialize window width/height and make window resizable.
165 -- I get tempted to have opinions about window dimensions here, but they're
166 -- non-portable:
167 -- - maximizing doesn't work on mobile and messes things up
168 -- - maximizing keeps the title bar on screen in Linux, but off screen on
169 -- Windows. And there's no way to get the height of the title bar.
170 -- It seems more robust to just follow LÖVE's default window size until
171 -- someone overrides it.
172 App.screen.width, App.screen.height, App.screen.flags = App.screen.size()
173 App.screen.flags.resizable = true
174 App.screen.resize(App.screen.width, App.screen.height, App.screen.flags)
177 function source.resize(w, h)
178 --? print(("Window resized to width: %d and height: %d."):format(w, h))
179 App.screen.width, App.screen.height = w, h
180 Text.redraw_all(Editor_state)
181 Editor_state.selection1 = {} -- no support for shift drag while we're resizing
182 if Show_log_browser_side then
183 Editor_state.right = App.screen.width/2 - Margin_right
184 else
185 Editor_state.right = App.screen.width-Margin_right
187 Log_browser_state.left = App.screen.width/2 + Margin_right
188 Log_browser_state.right = App.screen.width-Margin_right
189 Editor_state.width = Editor_state.right-Editor_state.left
190 Text.tweak_screen_top_and_cursor(Editor_state, Editor_state.left, Editor_state.right)
191 --? print('end resize')
194 function source.file_drop(file)
195 -- first make sure to save edits on any existing file
196 if Editor_state.next_save then
197 save_to_disk(Editor_state)
199 -- clear the slate for the new file
200 Editor_state.filename = file:getFilename()
201 file:open('r')
202 Editor_state.lines = load_from_file(file)
203 file:close()
204 Text.redraw_all(Editor_state)
205 Editor_state.screen_top1 = {line=1, pos=1}
206 Editor_state.cursor1 = {line=1, pos=1}
210 -- keep a few blank lines around: https://merveilles.town/@akkartik/110084833821965708
211 love.window.setTitle('lines.love - source')
217 -- a copy of source.file_drop when given a filename
218 function source.switch_to_file(filename)
219 -- first make sure to save edits on any existing file
220 if Editor_state.next_save then
221 save_to_disk(Editor_state)
223 -- save cursor position
224 File_navigation.cursors[Editor_state.filename] = {cursor1=Editor_state.cursor1, screen_top1=Editor_state.screen_top1}
225 -- clear the slate for the new file
226 Editor_state.filename = filename
227 load_from_disk(Editor_state)
228 Text.redraw_all(Editor_state)
229 if File_navigation.cursors[filename] then
230 Editor_state.screen_top1 = File_navigation.cursors[filename].screen_top1
231 Editor_state.cursor1 = File_navigation.cursors[filename].cursor1
232 else
233 Editor_state.screen_top1 = {line=1, pos=1}
234 Editor_state.cursor1 = {line=1, pos=1}
238 function source.draw()
239 edit.draw(Editor_state, --[[hide cursor?]] Show_file_navigator)
240 if Show_log_browser_side then
241 -- divider
242 App.color(Divider_color)
243 love.graphics.rectangle('fill', App.screen.width/2-1,Menu_status_bar_height, 3,App.screen.height)
245 log_browser.draw(Log_browser_state, --[[hide_cursor]] Focus ~= 'log_browser')
247 source.draw_menu_bar()
248 if Error_message then
249 local height = math.min(20*Editor_state.line_height, App.screen.height*0.2)
250 App.color{r=0.8,g=0,b=0}
251 love.graphics.rectangle('fill', 150, App.screen.height - height-10, App.screen.width, height+10)
252 App.color{r=0,g=0,b=0}
253 love.graphics.print(Error_message, 150+10, App.screen.height - height)
257 function source.update(dt)
258 Cursor_time = Cursor_time + dt
259 if App.mouse_x() < Editor_state.right then
260 edit.update(Editor_state, dt)
261 elseif Show_log_browser_side then
262 log_browser.update(Log_browser_state, dt)
266 function source.quit()
267 edit.quit(Editor_state)
268 log_browser.quit(Log_browser_state)
271 function source.settings()
272 if Settings == nil then Settings = {} end
273 if Settings.source == nil then Settings.source = {} end
274 Settings.source.x, Settings.source.y, Settings.source.displayindex = App.screen.position()
275 File_navigation.cursors[Editor_state.filename] = {cursor1=Editor_state.cursor1, screen_top1=Editor_state.screen_top1}
276 return {
277 x=Settings.source.x, y=Settings.source.y, displayindex=Settings.source.displayindex,
278 width=App.screen.width, height=App.screen.height,
279 font_height=Editor_state.font_height,
280 filename=Editor_state.filename,
281 cursors=File_navigation.cursors,
282 show_log_browser_side=Show_log_browser_side,
283 focus=Focus,
287 function source.mouse_press(x,y, mouse_button)
288 Cursor_time = 0 -- ensure cursor is visible immediately after it moves
289 --? print('mouse click', x, y)
290 --? print(Editor_state.left, Editor_state.right)
291 --? print(Log_browser_state.left, Log_browser_state.right)
292 if Show_file_navigator and y < Menu_status_bar_height + File_navigation.num_lines * Editor_state.line_height then
293 -- send click to buttons
294 edit.mouse_press(Editor_state, x,y, mouse_button)
295 return
297 if x < Editor_state.right + Margin_right then
298 --? print('click on edit side')
299 if Focus ~= 'edit' then
300 Focus = 'edit'
301 return
303 edit.mouse_press(Editor_state, x,y, mouse_button)
304 elseif Show_log_browser_side and Log_browser_state.left <= x and x < Log_browser_state.right then
305 --? print('click on log_browser side')
306 if Focus ~= 'log_browser' then
307 Focus = 'log_browser'
308 return
310 log_browser.mouse_press(Log_browser_state, x,y, mouse_button)
311 for _,line_cache in ipairs(Editor_state.line_cache) do line_cache.starty = nil end -- just in case we scroll
315 function source.mouse_release(x,y, mouse_button)
316 Cursor_time = 0 -- ensure cursor is visible immediately after it moves
317 if Focus == 'edit' then
318 return edit.mouse_release(Editor_state, x,y, mouse_button)
319 else
320 return log_browser.mouse_release(Log_browser_state, x,y, mouse_button)
324 function source.mouse_wheel_move(dx,dy)
325 Cursor_time = 0 -- ensure cursor is visible immediately after it moves
326 if Focus == 'edit' then
327 return edit.mouse_wheel_move(Editor_state, dx,dy)
328 else
329 return log_browser.mouse_wheel_move(Log_browser_state, dx,dy)
333 function source.text_input(t)
334 Cursor_time = 0 -- ensure cursor is visible immediately after it moves
335 if Show_file_navigator then
336 text_input_on_file_navigator(t)
337 return
339 if Focus == 'edit' then
340 return edit.text_input(Editor_state, t)
341 else
342 return log_browser.text_input(Log_browser_state, t)
346 function source.keychord_press(chord, key)
347 Cursor_time = 0 -- ensure cursor is visible immediately after it moves
348 --? print('source keychord')
349 if Show_file_navigator then
350 keychord_press_on_file_navigator(chord, key)
351 return
353 if chord == 'C-l' then
354 --? print('C-l')
355 Show_log_browser_side = not Show_log_browser_side
356 if Show_log_browser_side then
357 Editor_state.right = App.screen.width/2 - Margin_right
358 Editor_state.width = Editor_state.right-Editor_state.left
359 Text.redraw_all(Editor_state)
360 Log_browser_state.left = App.screen.width/2 + Margin_left
361 Log_browser_state.right = App.screen.width - Margin_right
362 else
363 Editor_state.right = App.screen.width - Margin_right
364 Editor_state.width = Editor_state.right-Editor_state.left
365 Text.redraw_all(Editor_state)
367 return
369 if chord == 'C-k' then
370 -- clear logs
371 love.filesystem.remove('log')
372 -- restart to reload state of logs on screen
373 Settings.source = source.settings()
374 source.quit()
375 love.filesystem.write('config', json.encode(Settings))
376 load_file_from_source_or_save_directory('main.lua')
377 App.undo_initialize()
378 App.run_tests_and_initialize()
379 return
381 if chord == 'C-g' then
382 Show_file_navigator = true
383 return
385 if Focus == 'edit' then
386 return edit.keychord_press(Editor_state, chord, key)
387 else
388 return log_browser.keychord_press(Log_browser_state, chord, key)
392 function source.key_release(key, scancode)
393 Cursor_time = 0 -- ensure cursor is visible immediately after it moves
394 if Focus == 'edit' then
395 return edit.key_release(Editor_state, key, scancode)
396 else
397 return log_browser.keychord_press(Log_browser_state, chordkey, scancode)