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
54 -- a few text objects we can avoid recomputing unless the font changes
61 -- called only for real run
62 function source
.initialize()
64 if Settings
and Settings
.source
then
65 source
.load_settings()
67 source
.initialize_default_settings()
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')
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
88 Editor_state
.screen_top1
= {line
=1, pos
=1}
89 Editor_state
.cursor1
= {line
=1, pos
=1}
92 -- We currently start out with side B collapsed.
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}
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
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
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)
161 function source
.initialize_window_geometry(em_width
)
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
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}
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
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
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
}
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
,
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
)
293 if x
< Editor_state
.right
+ Margin_right
then
294 --? print('click on edit side')
295 if Focus
~= 'edit' then
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'
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
)
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
)
326 if Focus
== 'edit' then
327 return edit
.text_input(Editor_state
, t
)
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
)
340 if chord
== 'C-l' then
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
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
)
361 if chord
== 'C-k' then
363 love
.filesystem
.remove('log')
364 -- restart to reload state of logs on screen
365 Settings
.source
= source
.settings()
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()
373 if chord
== 'C-g' then
374 Show_file_navigator
= true
377 if Focus
== 'edit' then
378 return edit
.keychord_press(Editor_state
, chord
, key
)
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
)
389 return log_browser
.keychord_press(Log_browser_state
, chordkey
, scancode
)
393 -- use this sparingly
395 if Text_cache
[s
] == nil then
396 Text_cache
[s
] = App
.newText(love
.graphics
.getFont(), s
)