runEditor :: Maybe FilePath -> Db -> IO ()
runEditor mFontPath db =
    do
        -- GLFW changes the directory from start directory, at least on macs.
        startDir <- Directory.getCurrentDirectory

        -- Load config as early as possible, before we open any windows/etc
        configSampler <- ConfigSampler.new startDir

        GLFWUtils.withGLFW $ do
            win <- GLFWUtils.createWindow "Lamdu" =<< GLFWUtils.getVideoModeSize
            Font.with startDir mFontPath $ \font -> do
                -- Fonts must be loaded after the GL context is created..
                zoom <- Zoom.make =<< GLFWUtils.getDisplayScale win
                addHelpWithStyle <- EventMapDoc.makeToggledHelpAdder EventMapDoc.HelpNotShown
                settingsRef <- newIORef Settings
                    { _sInfoMode = Settings.defaultInfoMode
                    }
                wrapFlyNav <- FlyNav.makeIO Style.flyNav WidgetIds.flyNav
                invalidateCacheRef <- newIORef (return ())
                let invalidateCache = join (readIORef invalidateCacheRef)
                evaluators <- DefEvaluators.new invalidateCache db
                let onSettingsChange settings =
                        case settings ^. Settings.sInfoMode of
                        Settings.Evaluation -> DefEvaluators.start evaluators
                        _ -> DefEvaluators.stop evaluators
                let makeWidget (config, size) =
                        do
                            cursor <-
                                DbLayout.cursor DbLayout.revisionProps
                                & Transaction.getP
                                & DbLayout.runDbTransaction db
                            sizeFactor <- Zoom.getSizeFactor zoom
                            globalEventMap <- Settings.mkEventMap onSettingsChange config settingsRef
                            let eventMap = globalEventMap `mappend` Zoom.eventMap zoom (Config.zoom config)
                            evalResults <- DefEvaluators.getResults evaluators
                            settings <- readIORef settingsRef
                            let env = GUIMain.Env
                                    { envEvalMap = evalResults
                                    , envConfig = config
                                    , envSettings = settings
                                    , envStyle = Style.base config font
                                    , envFullSize = size / sizeFactor
                                    , envCursor = cursor
                                    }
                            let dbToIO =
                                    case settings ^. Settings.sInfoMode of
                                    Settings.Evaluation ->
                                        DefEvaluators.runTransactionAndMaybeRestartEvaluators evaluators
                                    _ -> DbLayout.runDbTransaction db
                            widget <- mkWidgetWithFallback dbToIO env
                            return . Widget.scale sizeFactor $ Widget.weakerEvents eventMap widget
                (invalidateCacheAction, makeWidgetCached) <- cacheMakeWidget makeWidget
                refreshScheduler <- newRefreshScheduler
                writeIORef invalidateCacheRef $
                    do
                        invalidateCacheAction
                        scheduleRefresh refreshScheduler
                DefEvaluators.start evaluators

                mainLoop win refreshScheduler configSampler $
                    \config size ->
                    ( wrapFlyNav =<< makeWidgetCached (config, size)
                    , addHelpWithStyle (Style.help font (Config.help config)) size
                    )
runEditor :: Maybe FilePath -> Db -> IO ()
runEditor mFontPath db =
    do
        -- GLFW changes the directory from start directory, at least on macs.
        startDir <- Directory.getCurrentDirectory

        -- Load config as early as possible, before we open any windows/etc
        configSampler <- ConfigSampler.new startDir

        GLFWUtils.withGLFW $ do
            win <- GLFWUtils.createWindow "Lamdu" =<< GLFWUtils.getVideoModeSize
            Font.with startDir mFontPath $ \font -> do
                -- Fonts must be loaded after the GL context is created..
                zoom <- Zoom.make =<< GLFWUtils.getDisplayScale win
                addHelpWithStyle <- EventMapDoc.makeToggledHelpAdder EventMapDoc.HelpNotShown
                settingsRef <- newIORef Settings
                    { _sInfoMode = Settings.defaultInfoMode
                    }
                wrapFlyNav <- FlyNav.makeIO Style.flyNav WidgetIds.flyNav
                invalidateCacheRef <- newIORef (return ())
                let invalidateCache = join (readIORef invalidateCacheRef)
                evaluators <- DefEvaluators.new invalidateCache db
                let onSettingsChange settings =
                        case settings ^. Settings.sInfoMode of
                        Settings.Evaluation -> DefEvaluators.start evaluators
                        _ -> DefEvaluators.stop evaluators
                let makeWidget (config, size) =
                        do
                            cursor <-
                                DbLayout.cursor DbLayout.revisionProps
                                & Transaction.getP
                                & DbLayout.runDbTransaction db
                            sizeFactor <- Zoom.getSizeFactor zoom
                            globalEventMap <- Settings.mkEventMap onSettingsChange config settingsRef
                            let eventMap = globalEventMap `mappend` Zoom.eventMap zoom (Config.zoom config)
                            evalResults <- DefEvaluators.getResults evaluators
                            settings <- readIORef settingsRef
                            let env = GUIMain.Env
                                    { envEvalMap = evalResults
                                    , envConfig = config
                                    , envSettings = settings
                                    , envStyle = Style.base config font
                                    , envFullSize = size / sizeFactor
                                    , envCursor = cursor
                                    }
                            let dbToIO =
                                    case settings ^. Settings.sInfoMode of
                                    Settings.Evaluation ->
                                        DefEvaluators.runTransactionAndMaybeRestartEvaluators evaluators
                                    _ -> DbLayout.runDbTransaction db
                            widget <- mkWidgetWithFallback dbToIO env
                            return . Widget.scale sizeFactor $ Widget.weakerEvents eventMap widget
                (invalidateCacheAction, makeWidgetCached) <- cacheMakeWidget makeWidget
                refreshScheduler <- newRefreshScheduler
                writeIORef invalidateCacheRef $
                    do
                        invalidateCacheAction
                        scheduleRefresh refreshScheduler
                DefEvaluators.start evaluators

                mainLoop win refreshScheduler configSampler $
                    \config size ->
                    ( wrapFlyNav =<< makeWidgetCached (config, size)
                    , addHelpWithStyle (Style.help font (Config.help config)) size
                    )
runEditor mFontPath db =
    do
        -- GLFW changes the directory from start directory, at least on macs.
        startDir <- Directory.getCurrentDirectory

        -- Load config as early as possible, before we open any windows/etc
        configSampler <- ConfigSampler.new startDir

        GLFWUtils.withGLFW $ do
            win <- GLFWUtils.createWindow "Lamdu" =<< GLFWUtils.getVideoModeSize
            Font.with startDir mFontPath $ \font -> do
                -- Fonts must be loaded after the GL context is created..
                zoom <- Zoom.make =<< GLFWUtils.getDisplayScale win
                addHelpWithStyle <- EventMapDoc.makeToggledHelpAdder EventMapDoc.HelpNotShown
                settingsRef <- newIORef Settings
                    { _sInfoMode = Settings.defaultInfoMode
                    }
                wrapFlyNav <- FlyNav.makeIO Style.flyNav WidgetIds.flyNav
                invalidateCacheRef <- newIORef (return ())
                let invalidateCache = join (readIORef invalidateCacheRef)
                evaluators <- DefEvaluators.new invalidateCache db
                let onSettingsChange settings =
                        case settings ^. Settings.sInfoMode of
                        Settings.Evaluation -> DefEvaluators.start evaluators
                        _ -> DefEvaluators.stop evaluators
                let makeWidget (config, size) =
                        do
                            cursor <-
                                DbLayout.cursor DbLayout.revisionProps
                                & Transaction.getP
                                & DbLayout.runDbTransaction db
                            sizeFactor <- Zoom.getSizeFactor zoom
                            globalEventMap <- Settings.mkEventMap onSettingsChange config settingsRef
                            let eventMap = globalEventMap `mappend` Zoom.eventMap zoom (Config.zoom config)
                            evalResults <- DefEvaluators.getResults evaluators
                            settings <- readIORef settingsRef
                            let env = GUIMain.Env
                                    { envEvalMap = evalResults
                                    , envConfig = config
                                    , envSettings = settings
                                    , envStyle = Style.base config font
                                    , envFullSize = size / sizeFactor
                                    , envCursor = cursor
                                    }
                            let dbToIO =
                                    case settings ^. Settings.sInfoMode of
                                    Settings.Evaluation ->
                                        DefEvaluators.runTransactionAndMaybeRestartEvaluators evaluators
                                    _ -> DbLayout.runDbTransaction db
                            widget <- mkWidgetWithFallback dbToIO env
                            return . Widget.scale sizeFactor $ Widget.weakerEvents eventMap widget
                (invalidateCacheAction, makeWidgetCached) <- cacheMakeWidget makeWidget
                refreshScheduler <- newRefreshScheduler
                writeIORef invalidateCacheRef $
                    do
                        invalidateCacheAction
                        scheduleRefresh refreshScheduler
                DefEvaluators.start evaluators

                mainLoop win refreshScheduler configSampler $
                    \config size ->
                    ( wrapFlyNav =<< makeWidgetCached (config, size)
                    , addHelpWithStyle (Style.help font (Config.help config)) size
                    )