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
11 Show_file_navigator
= false
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 -- a few text objects we can avoid recomputing unless the font changes
60 -- called only for real run
61 function source
.initialize()
63 if Settings
and Settings
.source
then
64 source
.load_settings()
66 source
.initialize_default_settings()
69 source
.initialize_edit_side()
70 source
.initialize_log_browser_side()
72 Menu_status_bar_height
= 5 + Editor_state
.line_height
+ 5
73 Editor_state
.top
= Editor_state
.top
+ Menu_status_bar_height
74 Log_browser_state
.top
= Log_browser_state
.top
+ Menu_status_bar_height
78 -- keep a few blank lines around: https://merveilles.town/@akkartik/110084833821965708
79 love
.window
.setTitle('lines.love - source')
85 -- environment for a mutable file
86 -- TODO: some initialization is also happening in load_settings/initialize_default_settings. Clean that up.
87 function source
.initialize_edit_side()
88 load_from_disk(Editor_state
)
89 Text
.redraw_all(Editor_state
)
90 if File_navigation
.cursors
[Editor_state
.filename
] then
91 Editor_state
.screen_top1
= File_navigation
.cursors
[Editor_state
.filename
].screen_top1
92 Editor_state
.cursor1
= File_navigation
.cursors
[Editor_state
.filename
].cursor1
94 Editor_state
.screen_top1
= {line
=1, pos
=1}
95 Editor_state
.cursor1
= {line
=1, pos
=1}
97 edit
.check_locs(Editor_state
)
99 if Editor_state
.cursor1
.line
> #Editor_state
.lines
then
100 Editor_state
.cursor1
= {line
=1, pos
=1}
102 if Editor_state
.screen_top1
.line
> #Editor_state
.lines
then
103 Editor_state
.screen_top1
= {line
=1, pos
=1}
106 if rawget(_G
, 'jit') then
112 function source
.load_settings()
113 local settings
= Settings
.source
114 love
.graphics
.setFont(love
.graphics
.newFont(settings
.font_height
))
115 source
.resize_window_from_settings(settings
)
116 --? print('loading source position', settings.x, settings.y, settings.displayindex)
117 source
.set_window_position_from_settings(settings
)
118 Show_log_browser_side
= settings
.show_log_browser_side
119 local right
= App
.screen
.width
- Margin_right
120 if Show_log_browser_side
then
121 right
= App
.screen
.width
/2 - Margin_right
123 Editor_state
= edit
.initialize_state(Margin_top
, Margin_left
, right
, settings
.font_height
, math
.floor(settings
.font_height
*1.3))
124 Editor_state
.filename
= settings
.filename
125 Editor_state
.filename
= basename(Editor_state
.filename
) -- migrate settings that used full paths; we now support only relative paths within the app
126 if settings
.cursors
then
127 File_navigation
.cursors
= settings
.cursors
128 Editor_state
.screen_top1
= File_navigation
.cursors
[Editor_state
.filename
].screen_top1
129 Editor_state
.cursor1
= File_navigation
.cursors
[Editor_state
.filename
].cursor1
131 -- migrate old settings
132 Editor_state
.screen_top1
= {line
=1, pos
=1}
133 Editor_state
.cursor1
= {line
=1, pos
=1}
137 function source
.resize_window_from_settings(settings
)
138 local os
= love
.system
.getOS()
139 if os
== 'Android' or os
== 'iOS' then
140 -- maximizing on iOS breaks text rendering: https://github.com/deltadaedalus/vudu/issues/7
141 -- no point second-guessing window dimensions on mobile
142 App
.screen
.width
, App
.screen
.height
, App
.screen
.flags
= App
.screen
.size()
145 -- maximize window to determine maximum allowable dimensions
146 App
.screen
.resize(0, 0) -- maximize
147 Display_width
, Display_height
, App
.screen
.flags
= App
.screen
.size()
148 -- set up desired window dimensions
149 App
.screen
.flags
.resizable
= true
150 App
.screen
.flags
.minwidth
= math
.min(Display_width
, 200)
151 App
.screen
.flags
.minheight
= math
.min(Display_height
, 200)
152 App
.screen
.width
, App
.screen
.height
= settings
.width
, settings
.height
153 --? print('setting window from settings:', App.screen.width, App.screen.height)
154 App
.screen
.resize(App
.screen
.width
, App
.screen
.height
, App
.screen
.flags
)
157 function source
.set_window_position_from_settings(settings
)
158 -- love.window.setPosition doesn't quite seem to do what is asked of it on Linux.
159 App
.screen
.move(settings
.x
, settings
.y
-37, settings
.displayindex
)
162 function source
.initialize_default_settings()
163 local font_height
= 20
164 love
.graphics
.setFont(love
.graphics
.newFont(font_height
))
165 source
.initialize_window_geometry(App
.width('m'))
166 Editor_state
= edit
.initialize_state(Margin_top
, Margin_left
, App
.screen
.width
-Margin_right
)
167 Editor_state
.filename
= 'run.lua'
168 Editor_state
.font_height
= font_height
169 Editor_state
.line_height
= math
.floor(font_height
*1.3)
172 function source
.initialize_window_geometry(em_width
)
173 local os
= love
.system
.getOS()
174 if os
== 'Android' or os
== 'iOS' then
175 -- maximizing on iOS breaks text rendering: https://github.com/deltadaedalus/vudu/issues/7
176 -- no point second-guessing window dimensions on mobile
177 App
.screen
.width
, App
.screen
.height
, App
.screen
.flags
= App
.screen
.size()
181 App
.screen
.resize(0, 0) -- maximize
182 Display_width
, Display_height
, App
.screen
.flags
= App
.screen
.size()
183 -- shrink height slightly to account for window decoration
184 App
.screen
.height
= Display_height
-100
185 App
.screen
.width
= 40*em_width
186 App
.screen
.flags
.resizable
= true
187 App
.screen
.flags
.minwidth
= math
.min(App
.screen
.width
, 200)
188 App
.screen
.flags
.minheight
= math
.min(App
.screen
.height
, 200)
189 App
.screen
.resize(App
.screen
.width
, App
.screen
.height
, App
.screen
.flags
)
190 print('initializing source position')
191 if Settings
== nil then Settings
= {} end
192 if Settings
.source
== nil then Settings
.source
= {} end
193 Settings
.source
.x
, Settings
.source
.y
, Settings
.source
.displayindex
= App
.screen
.position()
196 function source
.resize(w
, h
)
197 --? print(("Window resized to width: %d and height: %d."):format(w, h))
198 App
.screen
.width
, App
.screen
.height
= w
, h
199 Text
.redraw_all(Editor_state
)
200 Editor_state
.selection1
= {} -- no support for shift drag while we're resizing
201 if Show_log_browser_side
then
202 Editor_state
.right
= App
.screen
.width
/2 - Margin_right
204 Editor_state
.right
= App
.screen
.width
-Margin_right
206 Log_browser_state
.left
= App
.screen
.width
/2 + Margin_right
207 Log_browser_state
.right
= App
.screen
.width
-Margin_right
208 Editor_state
.width
= Editor_state
.right
-Editor_state
.left
209 Text
.tweak_screen_top_and_cursor(Editor_state
, Editor_state
.left
, Editor_state
.right
)
210 --? print('end resize')
213 function source
.file_drop(file
)
214 -- first make sure to save edits on any existing file
215 if Editor_state
.next_save
then
216 save_to_disk(Editor_state
)
218 -- clear the slate for the new file
219 Editor_state
.filename
= file
:getFilename()
221 Editor_state
.lines
= load_from_file(file
)
223 Text
.redraw_all(Editor_state
)
224 Editor_state
.screen_top1
= {line
=1, pos
=1}
225 Editor_state
.cursor1
= {line
=1, pos
=1}
229 -- keep a few blank lines around: https://merveilles.town/@akkartik/110084833821965708
230 love
.window
.setTitle('lines.love - source')
236 -- a copy of source.file_drop when given a filename
237 function source
.switch_to_file(filename
)
238 -- first make sure to save edits on any existing file
239 if Editor_state
.next_save
then
240 save_to_disk(Editor_state
)
242 -- save cursor position
243 File_navigation
.cursors
[Editor_state
.filename
] = {cursor1
=Editor_state
.cursor1
, screen_top1
=Editor_state
.screen_top1
}
244 -- clear the slate for the new file
245 Editor_state
.filename
= filename
246 load_from_disk(Editor_state
)
247 Text
.redraw_all(Editor_state
)
248 if File_navigation
.cursors
[filename
] then
249 Editor_state
.screen_top1
= File_navigation
.cursors
[filename
].screen_top1
250 Editor_state
.cursor1
= File_navigation
.cursors
[filename
].cursor1
252 Editor_state
.screen_top1
= {line
=1, pos
=1}
253 Editor_state
.cursor1
= {line
=1, pos
=1}
257 function source
.draw()
258 edit
.draw(Editor_state
, --[[hide cursor?]] Show_file_navigator
)
259 if Show_log_browser_side
then
261 App
.color(Divider_color
)
262 love
.graphics
.rectangle('fill', App
.screen
.width
/2-1,Menu_status_bar_height
, 3,App
.screen
.height
)
264 log_browser
.draw(Log_browser_state
)
266 source
.draw_menu_bar()
269 function source
.update(dt
)
270 Cursor_time
= Cursor_time
+ dt
271 if App
.mouse_x() < Editor_state
.right
then
272 edit
.update(Editor_state
, dt
)
273 elseif Show_log_browser_side
then
274 log_browser
.update(Log_browser_state
, dt
)
278 function source
.quit()
279 edit
.quit(Editor_state
)
280 log_browser
.quit(Log_browser_state
)
283 function source
.settings()
284 if Current_app
== 'source' then
285 --? print('reading source window position')
286 Settings
.source
.x
, Settings
.source
.y
, Settings
.source
.displayindex
= App
.screen
.position()
288 --? print('saving source settings', Settings.source.x, Settings.source.y, Settings.source.displayindex)
289 File_navigation
.cursors
[Editor_state
.filename
] = {cursor1
=Editor_state
.cursor1
, screen_top1
=Editor_state
.screen_top1
}
291 x
=Settings
.source
.x
, y
=Settings
.source
.y
, displayindex
=Settings
.source
.displayindex
,
292 width
=App
.screen
.width
, height
=App
.screen
.height
,
293 font_height
=Editor_state
.font_height
,
294 filename
=Editor_state
.filename
,
295 cursors
=File_navigation
.cursors
,
296 show_log_browser_side
=Show_log_browser_side
,
301 function source
.mouse_press(x
,y
, mouse_button
)
302 Cursor_time
= 0 -- ensure cursor is visible immediately after it moves
303 --? print('mouse click', x, y)
304 --? print(Editor_state.left, Editor_state.right)
305 --? print(Log_browser_state.left, Log_browser_state.right)
306 if Show_file_navigator
and y
< Menu_status_bar_height
+ File_navigation
.num_lines
* Editor_state
.line_height
then
307 -- send click to buttons
308 edit
.mouse_press(Editor_state
, x
,y
, mouse_button
)
311 if x
< Editor_state
.right
+ Margin_right
then
312 --? print('click on edit side')
313 if Focus
~= 'edit' then
317 edit
.mouse_press(Editor_state
, x
,y
, mouse_button
)
318 elseif Show_log_browser_side
and Log_browser_state
.left
<= x
and x
< Log_browser_state
.right
then
319 --? print('click on log_browser side')
320 if Focus
~= 'log_browser' then
321 Focus
= 'log_browser'
324 log_browser
.mouse_press(Log_browser_state
, x
,y
, mouse_button
)
325 for _
,line_cache
in ipairs(Editor_state
.line_cache
) do line_cache
.starty
= nil end -- just in case we scroll
329 function source
.mouse_release(x
,y
, mouse_button
)
330 Cursor_time
= 0 -- ensure cursor is visible immediately after it moves
331 if Focus
== 'edit' then
332 return edit
.mouse_release(Editor_state
, x
,y
, mouse_button
)
334 return log_browser
.mouse_release(Log_browser_state
, x
,y
, mouse_button
)
338 function source
.mouse_wheel_move(dx
,dy
)
339 Cursor_time
= 0 -- ensure cursor is visible immediately after it moves
340 if Focus
== 'edit' then
341 return edit
.mouse_wheel_move(Editor_state
, dx
,dy
)
343 return log_browser
.mouse_wheel_move(Log_browser_state
, dx
,dy
)
347 function source
.text_input(t
)
348 Cursor_time
= 0 -- ensure cursor is visible immediately after it moves
349 if Show_file_navigator
then
350 text_input_on_file_navigator(t
)
353 if Focus
== 'edit' then
354 return edit
.text_input(Editor_state
, t
)
356 return log_browser
.text_input(Log_browser_state
, t
)
360 function source
.keychord_press(chord
, key
)
361 Cursor_time
= 0 -- ensure cursor is visible immediately after it moves
362 --? print('source keychord')
363 if Show_file_navigator
then
364 keychord_press_on_file_navigator(chord
, key
)
367 if chord
== 'C-l' then
369 Show_log_browser_side
= not Show_log_browser_side
370 if Show_log_browser_side
then
371 App
.screen
.width
= math
.min(Display_width
, App
.screen
.width
*2)
372 Editor_state
.right
= App
.screen
.width
/2 - Margin_right
373 Log_browser_state
.left
= App
.screen
.width
/2 + Margin_left
374 Log_browser_state
.right
= App
.screen
.width
- Margin_right
376 App
.screen
.width
= Editor_state
.right
+ Margin_right
378 --? print('setting window:', App.screen.width, App.screen.height)
379 App
.screen
.resize(App
.screen
.width
, App
.screen
.height
, App
.screen
.flags
)
380 --? print('done setting window')
381 -- try to restore position if possible
382 -- if the window gets wider the window manager may not respect this
383 if not App
.run_tests
then
384 source
.set_window_position_from_settings(Settings
.source
)
388 if chord
== 'C-k' then
390 love
.filesystem
.remove('log')
391 -- restart to reload state of logs on screen
392 Settings
.source
= source
.settings()
394 love
.filesystem
.write('config', json
.encode(Settings
))
395 load_file_from_source_or_save_directory('main.lua')
396 App
.undo_initialize()
397 App
.run_tests_and_initialize()
400 if chord
== 'C-g' then
401 Show_file_navigator
= true
404 if Focus
== 'edit' then
405 return edit
.keychord_press(Editor_state
, chord
, key
)
407 return log_browser
.keychord_press(Log_browser_state
, chord
, key
)
411 function source
.key_release(key
, scancode
)
412 Cursor_time
= 0 -- ensure cursor is visible immediately after it moves
413 if Focus
== 'edit' then
414 return edit
.key_release(Editor_state
, key
, scancode
)
416 return log_browser
.keychord_press(Log_browser_state
, chordkey
, scancode
)