4 Line_number_width
= 3 -- in ems
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
12 Show_file_navigator
= false
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
58 -- called only for real run
59 function source
.initialize()
61 if Settings
and Settings
.source
then
62 source
.load_settings()
64 source
.initialize_default_settings()
67 source
.initialize_edit_side()
68 source
.initialize_log_browser_side()
70 Menu_status_bar_height
= 5 + Editor_state
.line_height
+ 5
71 Editor_state
.top
= Editor_state
.top
+ Menu_status_bar_height
72 Log_browser_state
.top
= Log_browser_state
.top
+ Menu_status_bar_height
76 -- keep a few blank lines around: https://merveilles.town/@akkartik/110084833821965708
77 love
.window
.setTitle('lines.love - source')
83 -- environment for a mutable file
84 -- TODO: some initialization is also happening in load_settings/initialize_default_settings. Clean that up.
85 function source
.initialize_edit_side()
86 load_from_disk(Editor_state
)
87 Text
.redraw_all(Editor_state
)
88 if File_navigation
.cursors
[Editor_state
.filename
] then
89 Editor_state
.screen_top1
= File_navigation
.cursors
[Editor_state
.filename
].screen_top1
90 Editor_state
.cursor1
= File_navigation
.cursors
[Editor_state
.filename
].cursor1
92 Editor_state
.screen_top1
= {line
=1, pos
=1}
93 Editor_state
.cursor1
= {line
=1, pos
=1}
95 edit
.check_locs(Editor_state
)
97 if Editor_state
.cursor1
.line
> #Editor_state
.lines
then
98 Editor_state
.cursor1
= {line
=1, pos
=1}
100 if Editor_state
.screen_top1
.line
> #Editor_state
.lines
then
101 Editor_state
.screen_top1
= {line
=1, pos
=1}
104 if rawget(_G
, 'jit') then
110 function print_and_log(s
)
115 function source
.load_settings()
116 local settings
= Settings
.source
117 love
.graphics
.setFont(love
.graphics
.newFont(settings
.font_height
))
118 -- set up desired window dimensions and make window resizable
119 _
, _
, App
.screen
.flags
= App
.screen
.size()
120 App
.screen
.flags
.resizable
= true
121 App
.screen
.width
, App
.screen
.height
= settings
.width
, settings
.height
122 App
.screen
.resize(App
.screen
.width
, App
.screen
.height
, App
.screen
.flags
)
123 source
.set_window_position_from_settings(settings
)
124 Show_log_browser_side
= settings
.show_log_browser_side
125 local right
= App
.screen
.width
- Margin_right
126 if Show_log_browser_side
then
127 right
= App
.screen
.width
/2 - Margin_right
129 Editor_state
= edit
.initialize_state(Margin_top
, Margin_left
+ Line_number_width
*App
.width('m'), right
, settings
.font_height
, math
.floor(settings
.font_height
*1.3))
130 Editor_state
.filename
= settings
.filename
131 Editor_state
.filename
= basename(Editor_state
.filename
) -- migrate settings that used full paths; we now support only relative paths within the app
132 if settings
.cursors
then
133 File_navigation
.cursors
= settings
.cursors
134 Editor_state
.screen_top1
= File_navigation
.cursors
[Editor_state
.filename
].screen_top1
135 Editor_state
.cursor1
= File_navigation
.cursors
[Editor_state
.filename
].cursor1
137 -- migrate old settings
138 Editor_state
.screen_top1
= {line
=1, pos
=1}
139 Editor_state
.cursor1
= {line
=1, pos
=1}
143 function source
.set_window_position_from_settings(settings
)
144 local os
= love
.system
.getOS()
145 if os
== 'Linux' then
146 -- love.window.setPosition doesn't quite seem to do what is asked of it on Linux.
147 App
.screen
.move(settings
.x
, settings
.y
-37, settings
.displayindex
)
149 App
.screen
.move(settings
.x
, settings
.y
, settings
.displayindex
)
153 function source
.initialize_default_settings()
154 local font_height
= 20
155 love
.graphics
.setFont(love
.graphics
.newFont(font_height
))
156 source
.initialize_window_geometry()
157 Editor_state
= edit
.initialize_state(Margin_top
, Margin_left
+ Line_number_width
*App
.width('m'), App
.screen
.width
-Margin_right
)
158 Editor_state
.filename
= 'run.lua'
159 Editor_state
.font_height
= font_height
160 Editor_state
.line_height
= math
.floor(font_height
*1.3)
163 function source
.initialize_window_geometry()
164 -- Initialize window width/height and make window resizable.
166 -- I get tempted to have opinions about window dimensions here, but they're
168 -- - maximizing doesn't work on mobile and messes things up
169 -- - maximizing keeps the title bar on screen in Linux, but off screen on
170 -- Windows. And there's no way to get the height of the title bar.
171 -- It seems more robust to just follow LÖVE's default window size until
172 -- someone overrides it.
173 App
.screen
.width
, App
.screen
.height
, App
.screen
.flags
= App
.screen
.size()
174 App
.screen
.flags
.resizable
= true
175 App
.screen
.resize(App
.screen
.width
, App
.screen
.height
, App
.screen
.flags
)
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
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()
203 Editor_state
.lines
= load_from_file(file
)
205 Text
.redraw_all(Editor_state
)
206 Editor_state
.screen_top1
= {line
=1, pos
=1}
207 Editor_state
.cursor1
= {line
=1, pos
=1}
211 -- keep a few blank lines around: https://merveilles.town/@akkartik/110084833821965708
212 love
.window
.setTitle('lines.love - source')
218 -- a copy of source.file_drop when given a filename
219 function source
.switch_to_file(filename
)
220 -- first make sure to save edits on any existing file
221 if Editor_state
.next_save
then
222 save_to_disk(Editor_state
)
224 -- save cursor position
225 File_navigation
.cursors
[Editor_state
.filename
] = {cursor1
=Editor_state
.cursor1
, screen_top1
=Editor_state
.screen_top1
}
226 -- clear the slate for the new file
227 Editor_state
.filename
= filename
228 load_from_disk(Editor_state
)
229 Text
.redraw_all(Editor_state
)
230 if File_navigation
.cursors
[filename
] then
231 Editor_state
.screen_top1
= File_navigation
.cursors
[filename
].screen_top1
232 Editor_state
.cursor1
= File_navigation
.cursors
[filename
].cursor1
234 Editor_state
.screen_top1
= {line
=1, pos
=1}
235 Editor_state
.cursor1
= {line
=1, pos
=1}
239 function source
.draw()
240 edit
.draw(Editor_state
, --[[hide cursor?]] Show_file_navigator
)
241 if Show_log_browser_side
then
243 App
.color(Divider_color
)
244 love
.graphics
.rectangle('fill', App
.screen
.width
/2-1,Menu_status_bar_height
, 3,App
.screen
.height
)
246 log_browser
.draw(Log_browser_state
, --[[hide_cursor]] Focus
~= 'log_browser')
248 source
.draw_menu_bar()
249 if Error_message
then
250 local height
= math
.min(20*Editor_state
.line_height
, App
.screen
.height
*0.2)
251 App
.color
{r
=0.8,g
=0,b
=0}
252 love
.graphics
.rectangle('fill', 150, App
.screen
.height
- height
-10, App
.screen
.width
, height
+10)
253 App
.color
{r
=0,g
=0,b
=0}
254 love
.graphics
.print(Error_message
, 150+10, App
.screen
.height
- height
)
258 function source
.update(dt
)
259 Cursor_time
= Cursor_time
+ dt
260 if App
.mouse_x() < Editor_state
.right
then
261 edit
.update(Editor_state
, dt
)
262 elseif Show_log_browser_side
then
263 log_browser
.update(Log_browser_state
, dt
)
267 function source
.quit()
268 edit
.quit(Editor_state
)
269 log_browser
.quit(Log_browser_state
)
272 function source
.settings()
273 if Settings
== nil then Settings
= {} end
274 if Settings
.source
== nil then Settings
.source
= {} end
275 Settings
.source
.x
, Settings
.source
.y
, Settings
.source
.displayindex
= App
.screen
.position()
276 File_navigation
.cursors
[Editor_state
.filename
] = {cursor1
=Editor_state
.cursor1
, screen_top1
=Editor_state
.screen_top1
}
278 x
=Settings
.source
.x
, y
=Settings
.source
.y
, displayindex
=Settings
.source
.displayindex
,
279 width
=App
.screen
.width
, height
=App
.screen
.height
,
280 font_height
=Editor_state
.font_height
,
281 filename
=Editor_state
.filename
,
282 cursors
=File_navigation
.cursors
,
283 show_log_browser_side
=Show_log_browser_side
,
288 function source
.mouse_press(x
,y
, mouse_button
)
289 Cursor_time
= 0 -- ensure cursor is visible immediately after it moves
290 --? print('mouse click', x, y)
291 --? print(Editor_state.left, Editor_state.right)
292 --? print(Log_browser_state.left, Log_browser_state.right)
293 if Show_file_navigator
and y
< Menu_status_bar_height
+ File_navigation
.num_lines
* Editor_state
.line_height
then
294 -- send click to buttons
295 edit
.mouse_press(Editor_state
, x
,y
, mouse_button
)
298 if x
< Editor_state
.right
+ Margin_right
then
299 --? print('click on edit side')
300 if Focus
~= 'edit' then
304 edit
.mouse_press(Editor_state
, x
,y
, mouse_button
)
305 elseif Show_log_browser_side
and Log_browser_state
.left
<= x
and x
< Log_browser_state
.right
then
306 --? print('click on log_browser side')
307 if Focus
~= 'log_browser' then
308 Focus
= 'log_browser'
311 log_browser
.mouse_press(Log_browser_state
, x
,y
, mouse_button
)
312 for _
,line_cache
in ipairs(Editor_state
.line_cache
) do line_cache
.starty
= nil end -- just in case we scroll
316 function source
.mouse_release(x
,y
, mouse_button
)
317 Cursor_time
= 0 -- ensure cursor is visible immediately after it moves
318 if Focus
== 'edit' then
319 return edit
.mouse_release(Editor_state
, x
,y
, mouse_button
)
321 return log_browser
.mouse_release(Log_browser_state
, x
,y
, mouse_button
)
325 function source
.mouse_wheel_move(dx
,dy
)
326 Cursor_time
= 0 -- ensure cursor is visible immediately after it moves
327 if Focus
== 'edit' then
328 return edit
.mouse_wheel_move(Editor_state
, dx
,dy
)
330 return log_browser
.mouse_wheel_move(Log_browser_state
, dx
,dy
)
334 function source
.text_input(t
)
335 Cursor_time
= 0 -- ensure cursor is visible immediately after it moves
336 if Show_file_navigator
then
337 text_input_on_file_navigator(t
)
340 if Focus
== 'edit' then
341 return edit
.text_input(Editor_state
, t
)
343 return log_browser
.text_input(Log_browser_state
, t
)
347 function source
.keychord_press(chord
, key
)
348 Cursor_time
= 0 -- ensure cursor is visible immediately after it moves
349 --? print('source keychord')
350 if Show_file_navigator
then
351 keychord_press_on_file_navigator(chord
, key
)
354 if chord
== 'C-l' then
356 Show_log_browser_side
= not Show_log_browser_side
357 if Show_log_browser_side
then
358 Editor_state
.right
= App
.screen
.width
/2 - Margin_right
359 Editor_state
.width
= Editor_state
.right
-Editor_state
.left
360 Text
.redraw_all(Editor_state
)
361 Log_browser_state
.left
= App
.screen
.width
/2 + Margin_left
362 Log_browser_state
.right
= App
.screen
.width
- Margin_right
364 Editor_state
.right
= App
.screen
.width
- Margin_right
365 Editor_state
.width
= Editor_state
.right
-Editor_state
.left
366 Text
.redraw_all(Editor_state
)
370 if chord
== 'C-k' then
372 love
.filesystem
.remove('log')
373 -- restart to reload state of logs on screen
374 Settings
.source
= source
.settings()
376 love
.filesystem
.write('config', json
.encode(Settings
))
377 load_file_from_source_or_save_directory('main.lua')
378 App
.undo_initialize()
379 App
.run_tests_and_initialize()
382 if chord
== 'C-g' then
383 Show_file_navigator
= true
386 if Focus
== 'edit' then
387 return edit
.keychord_press(Editor_state
, chord
, key
)
389 return log_browser
.keychord_press(Log_browser_state
, chord
, key
)
393 function source
.key_release(key
, scancode
)
394 Cursor_time
= 0 -- ensure cursor is visible immediately after it moves
395 if Focus
== 'edit' then
396 return edit
.key_release(Editor_state
, key
, scancode
)
398 return log_browser
.keychord_press(Log_browser_state
, chordkey
, scancode
)