remove a condition
[view.love.git] / source.lua
blobb3357baeaf28186e183e66258738aaebdd7a80cd
1 source = {}
2 log_render = {}
4 Editor_state = {}
6 -- called both in tests and real run
7 function source.initialize_globals()
8 -- tests currently mostly clear their own state
10 Show_log_browser_side = false
11 Focus = 'edit'
12 Show_file_navigator = false
13 File_navigation = {
14 all_candidates = {
15 'run',
16 'run_tests',
17 'log',
18 'edit',
19 'drawing',
20 'help',
21 'text',
22 'search',
23 'select',
24 'undo',
25 'text_tests',
26 'geom',
27 'drawing_tests',
28 'file',
29 'source',
30 'source_tests',
31 'commands',
32 'log_browser',
33 'source_edit',
34 'source_text',
35 'source_undo',
36 'colorize',
37 'source_text_tests',
38 'source_file',
39 'main',
40 'button',
41 'keychord',
42 'app',
43 'test',
44 'json',
46 index = 1,
47 filter = '',
48 cursors = {}, -- filename to cursor1, screen_top1
50 File_navigation.candidates = File_navigation.all_candidates -- modified with filter
52 Menu_status_bar_height = 5 + --[[line height in tests]] 15 + 5
54 -- a few text objects we can avoid recomputing unless the font changes
55 Text_cache = {}
57 -- blinking cursor
58 Cursor_time = 0
59 end
61 -- called only for real run
62 function source.initialize()
63 log_new('source')
64 if Settings and Settings.source then
65 source.load_settings()
66 else
67 source.initialize_default_settings()
68 end
70 source.initialize_edit_side()
71 source.initialize_log_browser_side()
73 Menu_status_bar_height = 5 + Editor_state.line_height + 5
74 Editor_state.top = Editor_state.top + Menu_status_bar_height
75 Log_browser_state.top = Log_browser_state.top + Menu_status_bar_height
76 love.window.setTitle('lines.love - source')
77 end
79 -- environment for a mutable file of bifolded text
80 -- TODO: some initialization is also happening in load_settings/initialize_default_settings. Clean that up.
81 function source.initialize_edit_side()
82 load_from_disk(Editor_state)
83 Text.redraw_all(Editor_state)
84 if File_navigation.cursors[Editor_state.filename] then
85 Editor_state.screen_top1 = File_navigation.cursors[Editor_state.filename].screen_top1
86 Editor_state.cursor1 = File_navigation.cursors[Editor_state.filename].cursor1
87 else
88 Editor_state.screen_top1 = {line=1, pos=1}
89 Editor_state.cursor1 = {line=1, pos=1}
90 end
92 -- We currently start out with side B collapsed.
93 -- Other options:
94 -- * save all expanded state by line
95 -- * expand all if any location is in side B
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}
102 edit.eradicate_locations_after_the_fold(Editor_state)
104 if rawget(_G, 'jit') then
105 jit.off()
106 jit.flush()
110 function source.load_settings()
111 local settings = Settings.source
112 love.graphics.setFont(love.graphics.newFont(settings.font_height))
113 -- maximize window to determine maximum allowable dimensions
114 App.screen.resize(0, 0) -- maximize
115 Display_width, Display_height, App.screen.flags = App.screen.size()
116 -- set up desired window dimensions
117 App.screen.flags.resizable = true
118 App.screen.flags.minwidth = math.min(Display_width, 200)
119 App.screen.flags.minheight = math.min(Display_height, 200)
120 App.screen.width, App.screen.height = settings.width, settings.height
121 --? print('setting window from settings:', App.screen.width, App.screen.height)
122 App.screen.resize(App.screen.width, App.screen.height, App.screen.flags)
123 --? print('loading source position', settings.x, settings.y, settings.displayindex)
124 source.set_window_position_from_settings(settings)
125 Show_log_browser_side = settings.show_log_browser_side
126 local right = App.screen.width - Margin_right
127 if Show_log_browser_side then
128 right = App.screen.width/2 - Margin_right
130 Editor_state = edit.initialize_state(Margin_top, Margin_left, right, settings.font_height, math.floor(settings.font_height*1.3))
131 Editor_state.filename = settings.filename
132 Editor_state.filename = basename(Editor_state.filename) -- migrate settings that used full paths; we now support only relative paths within the app
133 if settings.cursors then
134 File_navigation.cursors = settings.cursors
135 Editor_state.screen_top1 = File_navigation.cursors[Editor_state.filename].screen_top1
136 Editor_state.cursor1 = File_navigation.cursors[Editor_state.filename].cursor1
137 else
138 -- migrate old settings
139 Editor_state.screen_top1 = {line=1, pos=1}
140 Editor_state.cursor1 = {line=1, pos=1}
144 function source.set_window_position_from_settings(settings)
145 -- setPosition doesn't quite seem to do what is asked of it on Linux.
146 love.window.setPosition(settings.x, settings.y-37, settings.displayindex)
149 function source.initialize_default_settings()
150 local font_height = 20
151 love.graphics.setFont(love.graphics.newFont(font_height))
152 local em = App.newText(love.graphics.getFont(), 'm')
153 source.initialize_window_geometry(App.width(em))
154 Editor_state = edit.initialize_state(Margin_top, Margin_left, App.screen.width-Margin_right)
155 Editor_state.filename = 'run.lua'
156 Editor_state.font_height = font_height
157 Editor_state.line_height = math.floor(font_height*1.3)
158 Editor_state.em = em
161 function source.initialize_window_geometry(em_width)
162 -- maximize window
163 App.screen.resize(0, 0) -- maximize
164 Display_width, Display_height, App.screen.flags = App.screen.size()
165 -- shrink height slightly to account for window decoration
166 App.screen.height = Display_height-100
167 App.screen.width = 40*em_width
168 App.screen.flags.resizable = true
169 App.screen.flags.minwidth = math.min(App.screen.width, 200)
170 App.screen.flags.minheight = math.min(App.screen.height, 200)
171 App.screen.resize(App.screen.width, App.screen.height, App.screen.flags)
172 print('initializing source position')
173 if Settings == nil then Settings = {} end
174 if Settings.source == nil then Settings.source = {} end
175 Settings.source.x, Settings.source.y, Settings.source.displayindex = App.screen.position()
178 function source.resize(w, h)
179 --? print(("Window resized to width: %d and height: %d."):format(w, h))
180 App.screen.width, App.screen.height = w, h
181 Text.redraw_all(Editor_state)
182 Editor_state.selection1 = {} -- no support for shift drag while we're resizing
183 if Show_log_browser_side then
184 Editor_state.right = App.screen.width/2 - Margin_right
185 else
186 Editor_state.right = App.screen.width-Margin_right
188 Log_browser_state.left = App.screen.width/2 + Margin_right
189 Log_browser_state.right = App.screen.width-Margin_right
190 Editor_state.width = Editor_state.right-Editor_state.left
191 Text.tweak_screen_top_and_cursor(Editor_state, Editor_state.left, Editor_state.right)
192 --? print('end resize')
195 function source.file_drop(file)
196 -- first make sure to save edits on any existing file
197 if Editor_state.next_save then
198 save_to_disk(Editor_state)
200 -- clear the slate for the new file
201 Editor_state.filename = file:getFilename()
202 file:open('r')
203 Editor_state.lines = load_from_file(file)
204 file:close()
205 Text.redraw_all(Editor_state)
206 Editor_state.screen_top1 = {line=1, pos=1}
207 Editor_state.cursor1 = {line=1, pos=1}
208 love.window.setTitle('lines.love - source')
211 -- a copy of source.file_drop when given a filename
212 function source.switch_to_file(filename)
213 -- first make sure to save edits on any existing file
214 if Editor_state.next_save then
215 save_to_disk(Editor_state)
217 -- save cursor position
218 File_navigation.cursors[Editor_state.filename] = {cursor1=Editor_state.cursor1, screen_top1=Editor_state.screen_top1}
219 -- clear the slate for the new file
220 Editor_state.filename = filename
221 load_from_disk(Editor_state)
222 Text.redraw_all(Editor_state)
223 if File_navigation.cursors[filename] then
224 Editor_state.screen_top1 = File_navigation.cursors[filename].screen_top1
225 Editor_state.cursor1 = File_navigation.cursors[filename].cursor1
226 else
227 Editor_state.screen_top1 = {line=1, pos=1}
228 Editor_state.cursor1 = {line=1, pos=1}
232 function source.draw()
233 edit.draw(Editor_state, --[[hide cursor?]] Show_file_navigator)
234 if Show_log_browser_side then
235 -- divider
236 App.color(Divider_color)
237 love.graphics.rectangle('fill', App.screen.width/2-1,Menu_status_bar_height, 3,App.screen.height)
239 log_browser.draw(Log_browser_state)
241 source.draw_menu_bar()
244 function source.update(dt)
245 Cursor_time = Cursor_time + dt
246 if App.mouse_x() < Editor_state.right then
247 edit.update(Editor_state, dt)
248 elseif Show_log_browser_side then
249 log_browser.update(Log_browser_state, dt)
253 function source.quit()
254 edit.quit(Editor_state)
255 log_browser.quit(Log_browser_state)
256 -- convert any bifold files here
259 function source.convert_bifold_text(infilename, outfilename)
260 local contents = love.filesystem.read(infilename)
261 contents = contents:gsub('\u{1e}', ';')
262 love.filesystem.write(outfilename, contents)
265 function source.settings()
266 if Current_app == 'source' then
267 --? print('reading source window position')
268 Settings.source.x, Settings.source.y, Settings.source.displayindex = App.screen.position()
270 --? print('saving source settings', Settings.source.x, Settings.source.y, Settings.source.displayindex)
271 File_navigation.cursors[Editor_state.filename] = {cursor1=Editor_state.cursor1, screen_top1=Editor_state.screen_top1}
272 return {
273 x=Settings.source.x, y=Settings.source.y, displayindex=Settings.source.displayindex,
274 width=App.screen.width, height=App.screen.height,
275 font_height=Editor_state.font_height,
276 filename=Editor_state.filename,
277 cursors=File_navigation.cursors,
278 show_log_browser_side=Show_log_browser_side,
279 focus=Focus,
283 function source.mouse_press(x,y, mouse_button)
284 Cursor_time = 0 -- ensure cursor is visible immediately after it moves
285 --? print('mouse click', x, y)
286 --? print(Editor_state.left, Editor_state.right)
287 --? print(Log_browser_state.left, Log_browser_state.right)
288 if Show_file_navigator and y < Menu_status_bar_height + File_navigation.num_lines * Editor_state.line_height then
289 -- send click to buttons
290 edit.mouse_press(Editor_state, x,y, mouse_button)
291 return
293 if x < Editor_state.right + Margin_right then
294 --? print('click on edit side')
295 if Focus ~= 'edit' then
296 Focus = 'edit'
297 return
299 edit.mouse_press(Editor_state, x,y, mouse_button)
300 elseif Show_log_browser_side and Log_browser_state.left <= x and x < Log_browser_state.right then
301 --? print('click on log_browser side')
302 if Focus ~= 'log_browser' then
303 Focus = 'log_browser'
304 return
306 log_browser.mouse_press(Log_browser_state, x,y, mouse_button)
307 for _,line_cache in ipairs(Editor_state.line_cache) do line_cache.starty = nil end -- just in case we scroll
311 function source.mouse_release(x,y, mouse_button)
312 Cursor_time = 0 -- ensure cursor is visible immediately after it moves
313 if Focus == 'edit' then
314 return edit.mouse_release(Editor_state, x,y, mouse_button)
315 else
316 return log_browser.mouse_release(Log_browser_state, x,y, mouse_button)
320 function source.text_input(t)
321 Cursor_time = 0 -- ensure cursor is visible immediately after it moves
322 if Show_file_navigator then
323 text_input_on_file_navigator(t)
324 return
326 if Focus == 'edit' then
327 return edit.text_input(Editor_state, t)
328 else
329 return log_browser.text_input(Log_browser_state, t)
333 function source.keychord_press(chord, key)
334 Cursor_time = 0 -- ensure cursor is visible immediately after it moves
335 --? print('source keychord')
336 if Show_file_navigator then
337 keychord_press_on_file_navigator(chord, key)
338 return
340 if chord == 'C-l' then
341 --? print('C-l')
342 Show_log_browser_side = not Show_log_browser_side
343 if Show_log_browser_side then
344 App.screen.width = math.min(Display_width, App.screen.width*2)
345 Editor_state.right = App.screen.width/2 - Margin_right
346 Log_browser_state.left = App.screen.width/2 + Margin_left
347 Log_browser_state.right = App.screen.width - Margin_right
348 else
349 App.screen.width = Editor_state.right + Margin_right
351 --? print('setting window:', App.screen.width, App.screen.height)
352 App.screen.resize(App.screen.width, App.screen.height, App.screen.flags)
353 --? print('done setting window')
354 -- try to restore position if possible
355 -- if the window gets wider the window manager may not respect this
356 if not App.run_tests then
357 source.set_window_position_from_settings(Settings.source)
359 return
361 if chord == 'C-k' then
362 -- clear logs
363 love.filesystem.remove('log')
364 -- restart to reload state of logs on screen
365 Settings.source = source.settings()
366 source.quit()
367 love.filesystem.write('config', json.encode(Settings))
368 load_file_from_source_or_save_directory('main.lua')
369 App.undo_initialize()
370 App.run_tests_and_initialize()
371 return
373 if chord == 'C-g' then
374 Show_file_navigator = true
375 return
377 if Focus == 'edit' then
378 return edit.keychord_press(Editor_state, chord, key)
379 else
380 return log_browser.keychord_press(Log_browser_state, chord, key)
384 function source.key_release(key, scancode)
385 Cursor_time = 0 -- ensure cursor is visible immediately after it moves
386 if Focus == 'edit' then
387 return edit.key_release(Editor_state, key, scancode)
388 else
389 return log_browser.keychord_press(Log_browser_state, chordkey, scancode)
393 -- use this sparingly
394 function to_text(s)
395 if Text_cache[s] == nil then
396 Text_cache[s] = App.newText(love.graphics.getFont(), s)
398 return Text_cache[s]