2022-09-03 20:54:38 +00:00
utf8 = require ' utf8 '
2022-10-29 23:43:13 +00:00
json = require ' json '
2022-05-02 15:20:30 +00:00
2022-10-29 23:43:13 +00:00
require ' app '
require ' test '
2022-11-27 22:06:11 +00:00
require ' live '
2022-07-12 20:45:38 +00:00
2022-10-29 23:43:13 +00:00
require ' keychord '
require ' button '
2022-08-24 20:27:04 +00:00
2022-10-29 23:43:13 +00:00
-- delegate most business logic to a layer that can be reused by other projects
require ' edit '
Editor_state = { }
2022-06-07 20:35:56 +00:00
2022-10-29 23:43:13 +00:00
-- called both in tests and real run
function App . initialize_globals ( )
2023-12-07 08:51:29 +00:00
Supported_versions = { ' 11.5 ' , ' 11.4 ' , ' 11.3 ' , ' 11.2 ' , ' 11.1 ' , ' 11.0 ' , ' 12.0 ' } -- put the recommended version first
2023-12-07 16:54:35 +00:00
-- Available modes: run, error
if Mode == nil then -- might have already been initialized elsewhere
Mode = ' run '
end
2023-12-07 08:51:29 +00:00
Error_count = 0
2023-12-07 04:14:24 +00:00
2022-10-29 23:43:13 +00:00
-- tests currently mostly clear their own state
2022-06-09 15:15:32 +00:00
2022-10-29 23:43:13 +00:00
-- blinking cursor
Cursor_time = 0
2022-07-01 05:46:45 +00:00
2022-09-03 20:54:38 +00:00
-- for hysteresis in a few places
2022-11-04 04:02:31 +00:00
Current_time = 0
Last_focus_time = 0 -- https://love2d.org/forums/viewtopic.php?p=249700
Last_resize_time = 0
2022-07-01 05:46:45 +00:00
end
2022-05-02 04:55:57 +00:00
2022-10-29 23:43:13 +00:00
-- called only for real run
2022-09-03 20:54:38 +00:00
function App . initialize ( arg )
2022-10-29 23:43:13 +00:00
love.keyboard . setTextInput ( true ) -- bring up keyboard on touch screen
love.keyboard . setKeyRepeat ( true )
2022-06-07 20:24:43 +00:00
2022-11-27 22:06:11 +00:00
Editor_state = nil -- not used outside editor tests
2022-10-29 23:43:13 +00:00
love.graphics . setBackgroundColor ( 1 , 1 , 1 )
if love.filesystem . getInfo ( ' config ' ) then
load_settings ( )
2022-09-03 20:54:38 +00:00
else
2022-10-29 23:43:13 +00:00
initialize_default_settings ( )
2022-09-03 20:54:38 +00:00
end
2022-06-07 20:19:17 +00:00
2023-03-25 18:41:52 +00:00
2023-06-19 05:44:27 +00:00
-- TODO: app-specific stuff goes here
2023-03-25 18:41:52 +00:00
-- keep a few blank lines around: https://merveilles.town/@akkartik/110084833821965708
2022-11-27 19:05:14 +00:00
love.window . setTitle ( ' TODO ' )
2022-10-29 23:43:13 +00:00
2023-03-25 18:41:52 +00:00
2022-11-27 22:54:48 +00:00
if on.initialize then on.initialize ( arg ) end
2022-10-29 23:43:13 +00:00
if rawget ( _G , ' jit ' ) then
jit.off ( )
jit.flush ( )
2022-09-03 20:54:38 +00:00
end
2023-12-07 05:32:10 +00:00
check_love_version ( )
end
function check_love_version ( )
2023-12-07 08:51:29 +00:00
-- we'll reuse error mode on load for an initial version check
2023-12-07 05:32:10 +00:00
if array.find ( Supported_versions , Version ) == nil then
2023-12-07 08:51:29 +00:00
Mode = ' error '
if Error_message == nil then Error_message = ' ' end
Error_message = ( " This app hasn't been tested with LÖVE version %s; please use version %s if you run into errors. Press a key to try recovering. \n \n %s " ) : format ( Version , Supported_versions [ 1 ] , Error_message )
print ( Error_message )
2023-12-07 05:32:10 +00:00
-- continue initializing everything; hopefully we won't have errors during initialization
end
2022-05-16 22:26:22 +00:00
end
2023-06-04 22:25:46 +00:00
function print_and_log ( s )
print ( s )
log ( 3 , s )
end
2023-01-02 01:52:06 +00:00
function love . quit ( )
if on.quit then on.quit ( ) end
2023-01-02 02:19:08 +00:00
love.filesystem . write ( ' config ' , json.encode ( settings ( ) ) )
end
2023-04-08 01:18:43 +00:00
function restart ( )
if on.quit then on.quit ( ) end
love.filesystem . write ( ' config ' , json.encode ( settings ( ) ) )
load_settings ( )
if on.initialize then on.initialize ( ) end
end
2023-01-02 02:19:08 +00:00
function settings ( )
local x , y , displayindex = App.screen . position ( )
return {
x = x , y = y , displayindex = displayindex ,
width = App.screen . width , height = App.screen . height ,
2023-01-02 02:22:52 +00:00
app = on.save_settings and on.save_settings ( ) ,
2023-01-02 02:19:08 +00:00
}
2023-01-02 01:52:06 +00:00
end
2023-01-02 01:50:41 +00:00
function load_settings ( )
2022-10-29 23:43:13 +00:00
local settings = json.decode ( love.filesystem . read ( ' config ' ) )
2023-07-11 01:41:23 +00:00
-- set up desired window dimensions and make window resizable
_ , _ , App.screen . flags = App.screen . size ( )
2022-10-29 23:43:13 +00:00
App.screen . flags.resizable = true
App.screen . width , App.screen . height = settings.width , settings.height
2023-03-19 07:04:53 +00:00
App.screen . resize ( App.screen . width , App.screen . height , App.screen . flags )
2023-07-11 01:41:23 +00:00
set_window_position_from_settings ( settings )
2023-07-11 02:07:02 +00:00
if on.load_settings then on.load_settings ( settings.app ) end
2022-10-29 23:43:13 +00:00
end
2023-07-11 01:41:23 +00:00
function set_window_position_from_settings ( settings )
local os = love.system . getOS ( )
if os == ' Linux ' then
-- love.window.setPosition doesn't quite seem to do what is asked of it on Linux.
App.screen . move ( settings.x , settings.y - 37 , settings.displayindex )
2023-01-02 01:51:35 +00:00
else
2023-07-11 01:41:23 +00:00
App.screen . move ( settings.x , settings.y , settings.displayindex )
2023-01-02 01:51:35 +00:00
end
2022-10-29 23:43:13 +00:00
end
function initialize_default_settings ( )
local font_height = 20
love.graphics . setFont ( love.graphics . newFont ( font_height ) )
2023-08-05 07:08:02 +00:00
initialize_window_geometry ( )
2022-10-29 23:43:13 +00:00
end
2023-07-11 18:11:48 +00:00
function initialize_window_geometry ( )
2023-07-11 01:41:23 +00:00
-- Initialize window width/height and make window resizable.
--
-- I get tempted to have opinions about window dimensions here, but they're
-- non-portable:
-- - maximizing doesn't work on mobile and messes things up
-- - maximizing keeps the title bar on screen in Linux, but off screen on
-- Windows. And there's no way to get the height of the title bar.
-- It seems more robust to just follow LÖVE's default window size until
-- someone overrides it.
2023-03-19 07:04:53 +00:00
App.screen . width , App.screen . height , App.screen . flags = App.screen . size ( )
2022-10-29 23:43:13 +00:00
App.screen . flags.resizable = true
2023-03-19 07:04:53 +00:00
App.screen . resize ( App.screen . width , App.screen . height , App.screen . flags )
2022-10-29 23:43:13 +00:00
end
function App . resize ( w , h )
--? print(("Window resized to width: %d and height: %d."):format(w, h))
App.screen . width , App.screen . height = w , h
2022-11-04 04:46:20 +00:00
Last_resize_time = Current_time
2022-11-27 22:06:11 +00:00
if on.resize then on.resize ( w , h ) end
2022-10-29 23:43:13 +00:00
end
function App . filedropped ( file )
2023-01-20 22:18:04 +00:00
if on.file_drop then on.file_drop ( file ) end
2022-10-29 23:43:13 +00:00
end
2022-05-23 01:27:48 +00:00
function App . draw ( )
2023-04-20 07:30:25 +00:00
if Mode == ' error ' then
love.graphics . setColor ( 0 , 0 , 1 )
love.graphics . rectangle ( ' fill ' , 0 , 0 , App.screen . width , App.screen . height )
love.graphics . setColor ( 1 , 1 , 1 )
love.graphics . printf ( Error_message , 40 , 40 , 600 )
2023-04-20 02:45:17 +00:00
return
end
2022-11-27 22:06:11 +00:00
if on.draw then on.draw ( ) end
2022-05-02 04:55:57 +00:00
end
2022-05-23 01:27:48 +00:00
function App . update ( dt )
2022-11-04 04:02:31 +00:00
Current_time = Current_time + dt
2022-07-12 20:45:38 +00:00
-- some hysteresis while resizing
2022-11-04 04:02:31 +00:00
if Current_time < Last_resize_time + 0.1 then
2022-07-25 22:23:01 +00:00
return
2022-07-12 20:45:38 +00:00
end
2022-10-29 23:43:13 +00:00
Cursor_time = Cursor_time + dt
2023-04-20 07:30:25 +00:00
-- listen for commands in both 'error' and 'run' modes
2022-11-27 22:27:04 +00:00
live.update ( dt )
2023-04-20 07:30:25 +00:00
if Mode == ' run ' then
if on.update then on.update ( dt ) end
end
2022-10-29 23:43:13 +00:00
end
function App . mousepressed ( x , y , mouse_button )
2023-04-20 07:30:25 +00:00
if Mode == ' error ' then return end
2022-10-29 23:43:13 +00:00
Cursor_time = 0 -- ensure cursor is visible immediately after it moves
2022-12-24 04:30:35 +00:00
if on.mouse_press then on.mouse_press ( x , y , mouse_button ) end
2022-10-29 23:43:13 +00:00
end
function App . mousereleased ( x , y , mouse_button )
2023-04-20 07:30:25 +00:00
if Mode == ' error ' then return end
2022-10-29 23:43:13 +00:00
Cursor_time = 0 -- ensure cursor is visible immediately after it moves
2022-12-24 04:30:35 +00:00
if on.mouse_release then on.mouse_release ( x , y , mouse_button ) end
2022-10-29 23:43:13 +00:00
end
2023-12-26 16:28:23 +00:00
function App . mousemoved ( x , y , dx , dy , istouch )
if on.mouse_move then on.mouse_move ( x , y , dx , dy , istouch ) end
end
2023-03-24 04:48:27 +00:00
function App . wheelmoved ( dx , dy )
2023-04-20 07:30:25 +00:00
if Mode == ' error ' then return end
2023-03-24 04:48:27 +00:00
Cursor_time = 0 -- ensure cursor is visible immediately after it moves
2023-03-24 04:48:42 +00:00
if on.mouse_wheel_move then on.mouse_wheel_move ( dx , dy ) end
2023-03-24 04:48:27 +00:00
end
2022-10-29 23:43:13 +00:00
function App . focus ( in_focus )
2023-04-20 07:30:25 +00:00
if Mode == ' error ' then return end
2022-10-29 23:43:13 +00:00
if in_focus then
2022-11-04 04:46:20 +00:00
Last_focus_time = Current_time
2022-09-03 20:54:38 +00:00
end
2022-12-17 16:49:45 +00:00
if in_focus then
love.graphics . setBackgroundColor ( 1 , 1 , 1 )
else
2023-01-02 01:52:51 +00:00
love.graphics . setBackgroundColor ( 0.8 , 0.8 , 0.8 )
2022-12-17 16:49:45 +00:00
end
2022-11-27 22:06:11 +00:00
if on.focus then on.focus ( in_focus ) end
2022-05-14 20:08:33 +00:00
end
2022-11-27 22:06:11 +00:00
-- App.keypressed is defined in keychord.lua
2022-12-24 01:16:19 +00:00
function App . keychord_press ( chord , key )
2023-04-20 07:30:25 +00:00
if Mode == ' error ' then
if chord == ' C-c ' then
love.system . setClipboardText ( Error_message )
end
return
end
2022-09-03 20:54:38 +00:00
-- ignore events for some time after window in focus (mostly alt-tab)
2022-11-04 04:02:31 +00:00
if Current_time < Last_focus_time + 0.01 then
2022-09-03 20:54:38 +00:00
return
end
2022-10-29 23:43:13 +00:00
Cursor_time = 0 -- ensure cursor is visible immediately after it moves
2022-12-24 04:17:16 +00:00
if on.keychord_press then on.keychord_press ( chord , key ) end
2022-07-25 22:33:06 +00:00
end
2022-05-23 01:27:48 +00:00
function App . textinput ( t )
2023-04-20 07:30:25 +00:00
if Mode == ' error ' then return end
2022-09-03 20:54:38 +00:00
-- ignore events for some time after window in focus (mostly alt-tab)
2022-11-04 04:02:31 +00:00
if Current_time < Last_focus_time + 0.01 then
2022-07-25 22:33:06 +00:00
return
end
2022-10-29 23:43:13 +00:00
Cursor_time = 0 -- ensure cursor is visible immediately after it moves
2022-12-24 04:17:16 +00:00
if on.text_input then on.text_input ( t ) end
2022-05-21 21:03:06 +00:00
end
2022-10-29 23:43:13 +00:00
function App . keyreleased ( key , scancode )
2023-04-20 07:30:25 +00:00
if Mode == ' error ' then
2023-11-17 16:31:41 +00:00
if Redo_initialization then
Redo_initialization = nil
love.run ( ) -- won't actually replace the event loop;
-- we're just running it for its initialization side-effects
else
Mode = ' run '
end
2023-04-20 02:45:17 +00:00
return
end
2022-09-03 20:54:38 +00:00
-- ignore events for some time after window in focus (mostly alt-tab)
2022-11-04 04:02:31 +00:00
if Current_time < Last_focus_time + 0.01 then
2022-07-25 22:33:06 +00:00
return
end
2022-10-29 23:43:13 +00:00
Cursor_time = 0 -- ensure cursor is visible immediately after it moves
2023-01-17 06:46:40 +00:00
if on.key_release then on.key_release ( key , scancode ) end
2022-05-02 15:20:30 +00:00
end
2023-11-22 20:31:37 +00:00
-- plumb all other handlers through to on.*
for handler_name in pairs ( love.handlers ) do
if App [ handler_name ] == nil then
App [ handler_name ] = function ( ... )
if on [ handler_name ] then on [ handler_name ] ( ... ) end
end
end
end