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 - '..Editor_state
.filename
)
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 local font
= 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
*font
:getWidth('m'), right
, font
, 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 local font
= love
.graphics
.newFont(font_height
)
156 source
.initialize_window_geometry()
157 Editor_state
= edit
.initialize_state(Margin_top
, Margin_left
+ Line_number_width
*font
:getWidth('m'), App
.screen
.width
-Margin_right
, font
, font_height
, math
.floor(font_height
*1.3))
158 Editor_state
.filename
= 'run.lua'
161 function source
.initialize_window_geometry()
162 -- Initialize window width/height and make window resizable.
164 -- I get tempted to have opinions about window dimensions here, but they're
166 -- - maximizing doesn't work on mobile and messes things up
167 -- - maximizing keeps the title bar on screen in Linux, but off screen on
168 -- Windows. And there's no way to get the height of the title bar.
169 -- It seems more robust to just follow LÖVE's default window size until
170 -- someone overrides it.
171 App
.screen
.width
, App
.screen
.height
, App
.screen
.flags
= App
.screen
.size()
172 App
.screen
.flags
.resizable
= true
173 App
.screen
.resize(App
.screen
.width
, App
.screen
.height
, App
.screen
.flags
)
176 function source
.resize(w
, h
)
177 --? print(("Window resized to width: %d and height: %d."):format(w, h))
178 App
.screen
.width
, App
.screen
.height
= w
, h
179 Text
.redraw_all(Editor_state
)
180 Editor_state
.selection1
= {} -- no support for shift drag while we're resizing
181 if Show_log_browser_side
then
182 Editor_state
.right
= App
.screen
.width
/2 - Margin_right
184 Editor_state
.right
= App
.screen
.width
-Margin_right
186 Log_browser_state
.left
= App
.screen
.width
/2 + Margin_right
187 Log_browser_state
.right
= App
.screen
.width
-Margin_right
188 Editor_state
.width
= Editor_state
.right
-Editor_state
.left
189 Text
.tweak_screen_top_and_cursor(Editor_state
, Editor_state
.left
, Editor_state
.right
)
190 --? print('end resize')
193 function source
.file_drop(file
)
194 -- first make sure to save edits on any existing file
195 if Editor_state
.next_save
then
196 save_to_disk(Editor_state
)
198 -- clear the slate for the new file
199 Editor_state
.filename
= file
:getFilename()
201 Editor_state
.lines
= load_from_file(file
)
203 Text
.redraw_all(Editor_state
)
204 Editor_state
.screen_top1
= {line
=1, pos
=1}
205 Editor_state
.cursor1
= {line
=1, pos
=1}
209 -- keep a few blank lines around: https://merveilles.town/@akkartik/110084833821965708
210 love
.window
.setTitle('lines.love - source')
216 -- a copy of source.file_drop when given a filename
217 function source
.switch_to_file(filename
)
218 -- first make sure to save edits on any existing file
219 if Editor_state
.next_save
then
220 save_to_disk(Editor_state
)
222 -- save cursor position
223 File_navigation
.cursors
[Editor_state
.filename
] = {cursor1
=Editor_state
.cursor1
, screen_top1
=Editor_state
.screen_top1
}
224 -- clear the slate for the new file
225 Editor_state
.filename
= filename
226 load_from_disk(Editor_state
)
227 Text
.redraw_all(Editor_state
)
228 if File_navigation
.cursors
[filename
] then
229 Editor_state
.screen_top1
= File_navigation
.cursors
[filename
].screen_top1
230 Editor_state
.cursor1
= File_navigation
.cursors
[filename
].cursor1
232 Editor_state
.screen_top1
= {line
=1, pos
=1}
233 Editor_state
.cursor1
= {line
=1, pos
=1}
237 function source
.draw()
238 edit
.draw(Editor_state
, --[[hide cursor?]] Show_file_navigator
, --[[show line numbers]] true)
239 if Show_log_browser_side
then
241 App
.color(Divider_color
)
242 love
.graphics
.rectangle('fill', App
.screen
.width
/2-1,Menu_status_bar_height
, 3,App
.screen
.height
)
244 log_browser
.draw(Log_browser_state
, --[[hide_cursor]] Focus
~= 'log_browser')
246 source
.draw_menu_bar()
247 if Error_message
then
248 local height
= math
.min(20*Editor_state
.line_height
, App
.screen
.height
*0.2)
249 App
.color
{r
=0.8,g
=0,b
=0}
250 love
.graphics
.rectangle('fill', 150, App
.screen
.height
- height
-10, App
.screen
.width
, height
+10)
251 App
.color
{r
=0,g
=0,b
=0}
252 love
.graphics
.print(Error_message
, 150+10, App
.screen
.height
- height
)
256 function source
.update(dt
)
257 Cursor_time
= Cursor_time
+ dt
258 if App
.mouse_x() < Editor_state
.right
then
259 edit
.update(Editor_state
, dt
)
260 elseif Show_log_browser_side
then
261 log_browser
.update(Log_browser_state
, dt
)
265 function source
.quit()
266 edit
.quit(Editor_state
)
267 log_browser
.quit(Log_browser_state
)
270 function source
.settings()
271 if Settings
== nil then Settings
= {} end
272 if Settings
.source
== nil then Settings
.source
= {} end
273 Settings
.source
.x
, Settings
.source
.y
, Settings
.source
.displayindex
= App
.screen
.position()
274 File_navigation
.cursors
[Editor_state
.filename
] = {cursor1
=Editor_state
.cursor1
, screen_top1
=Editor_state
.screen_top1
}
276 x
=Settings
.source
.x
, y
=Settings
.source
.y
, displayindex
=Settings
.source
.displayindex
,
277 width
=App
.screen
.width
, height
=App
.screen
.height
,
278 font_height
=Editor_state
.font_height
,
279 filename
=Editor_state
.filename
,
280 cursors
=File_navigation
.cursors
,
281 show_log_browser_side
=Show_log_browser_side
,
286 function source
.mouse_press(x
,y
, mouse_button
)
287 Cursor_time
= 0 -- ensure cursor is visible immediately after it moves
288 --? print('mouse click', x, y)
289 --? print(Editor_state.left, Editor_state.right)
290 --? print(Log_browser_state.left, Log_browser_state.right)
291 if Show_file_navigator
and y
< Menu_status_bar_height
+ File_navigation
.num_lines
* Editor_state
.line_height
then
292 -- send click to buttons
293 edit
.mouse_press(Editor_state
, x
,y
, mouse_button
)
296 if x
< Editor_state
.right
+ Margin_right
then
297 --? print('click on edit side')
298 if Focus
~= 'edit' then
302 edit
.mouse_press(Editor_state
, x
,y
, mouse_button
)
303 elseif Show_log_browser_side
and Log_browser_state
.left
<= x
and x
< Log_browser_state
.right
then
304 --? print('click on log_browser side')
305 if Focus
~= 'log_browser' then
306 Focus
= 'log_browser'
309 log_browser
.mouse_press(Log_browser_state
, x
,y
, mouse_button
)
310 for _
,line_cache
in ipairs(Editor_state
.line_cache
) do line_cache
.starty
= nil end -- just in case we scroll
314 function source
.mouse_release(x
,y
, mouse_button
)
315 Cursor_time
= 0 -- ensure cursor is visible immediately after it moves
316 if Focus
== 'edit' then
317 return edit
.mouse_release(Editor_state
, x
,y
, mouse_button
)
319 return log_browser
.mouse_release(Log_browser_state
, x
,y
, mouse_button
)
323 function source
.mouse_wheel_move(dx
,dy
)
324 Cursor_time
= 0 -- ensure cursor is visible immediately after it moves
325 if Focus
== 'edit' then
326 return edit
.mouse_wheel_move(Editor_state
, dx
,dy
)
328 return log_browser
.mouse_wheel_move(Log_browser_state
, dx
,dy
)
332 function source
.text_input(t
)
333 Cursor_time
= 0 -- ensure cursor is visible immediately after it moves
334 if Show_file_navigator
then
335 text_input_on_file_navigator(t
)
338 if Focus
== 'edit' then
339 return edit
.text_input(Editor_state
, t
)
341 return log_browser
.text_input(Log_browser_state
, t
)
345 function source
.keychord_press(chord
, key
)
346 Cursor_time
= 0 -- ensure cursor is visible immediately after it moves
347 --? print('source keychord')
348 if Show_file_navigator
then
349 keychord_press_on_file_navigator(chord
, key
)
352 if chord
== 'C-l' then
354 Show_log_browser_side
= not Show_log_browser_side
355 if Show_log_browser_side
then
356 Editor_state
.right
= App
.screen
.width
/2 - Margin_right
357 Editor_state
.width
= Editor_state
.right
-Editor_state
.left
358 Text
.redraw_all(Editor_state
)
359 Log_browser_state
.left
= App
.screen
.width
/2 + Margin_left
360 Log_browser_state
.right
= App
.screen
.width
- Margin_right
362 Editor_state
.right
= App
.screen
.width
- Margin_right
363 Editor_state
.width
= Editor_state
.right
-Editor_state
.left
364 Text
.redraw_all(Editor_state
)
368 if chord
== 'C-k' then
370 love
.filesystem
.remove('log')
371 -- restart to reload state of logs on screen
372 Settings
.source
= source
.settings()
374 love
.filesystem
.write('config', json
.encode(Settings
))
375 load_file_from_source_or_save_directory('main.lua')
376 App
.undo_initialize()
377 App
.run_tests_and_initialize()
380 if chord
== 'C-g' then
381 Show_file_navigator
= true
384 if Focus
== 'edit' then
385 return edit
.keychord_press(Editor_state
, chord
, key
)
387 return log_browser
.keychord_press(Log_browser_state
, chord
, key
)
391 function source
.key_release(key
, scancode
)
392 Cursor_time
= 0 -- ensure cursor is visible immediately after it moves
393 if Focus
== 'edit' then
394 return edit
.key_release(Editor_state
, key
, scancode
)
396 return log_browser
.keychord_press(Log_browser_state
, chordkey
, scancode
)