{-# LANGUAGE Rank2Types #-}

module Yi.UI.Common where

import           System.Exit (ExitCode)

{- | Record presenting a frontend's interface.

The functions 'layout' and 'refresh' are both run by the editor's main loop,
in response to user actions and so on. Their relation is a little subtle, and
is discussed here:

  * to see some code, look at the function @refreshEditor@ in "Yi.Core".
    This is the only place where 'layout' and 'refresh' are used.

  * the function 'layout' is responsible for updating the 'Editor' with the
    width and height of the windows. Some frontends, such as Pango, need to
    modify their internal state to do this, and will consequently change
    their display. This is expected.

  * the function 'refresh' should cause the UI to update its display with
    the information given in the 'Editor'.

  * the functionalities of 'layout' and 'refresh' overlap to some extent, in
    the sense that both may cause the frontend to update its display. The Yi core
    provides the following guarantees which the frontend may take advantage of:

      * in the main editor loop (i.e. in the @refreshEditor@ function),
        'layout' will be run (possibly multiple times) and then 'refresh' will
        be run. This guarantee will hold even in the case of threading (the
        function @refreshEditor@ will always be run atomically, using @MVar@s).

      * between the last run of 'layout' and the run of 'refresh', some changes
        may be made to the 'Editor'. However, the text, text attributes, and
        (displayed) window region of all windows will remain the same. However,
        the cursor location may change.

        This guarantee allows frontends which calculate rendering of the text
        during the 'layout' stage to avoid recalculating the render again during
        'refresh'. Pango is an example of such a frontend.

The Yi core provides no guarantee about the OS thread from which the functions
'layout' and 'refresh' are called from. In particular, subprocesses (e.g. compilation,
ghci) will run 'layout' and 'refresh' from new OS threads (see @startSubprocessWatchers@
in "Yi.Core"). The frontend must be preparaed for this: for instance, Gtk-based frontends
should wrap GUI updates in @postGUIAsync@.
-}
data UI e = UI
    { main                  :: IO ()                   -- ^ Main loop
    , end                   :: Maybe ExitCode -> IO () -- ^ Clean up, and also terminate if given an exit code.
    , suspend               :: IO ()                   -- ^ Suspend (or minimize) the program
    , refresh               :: e -> IO ()              -- ^ Refresh the UI with the given state
    , userForceRefresh      :: IO ()                   -- ^ User force-refresh (in case the screen has been messed up from outside)
    , layout                :: e -> IO e               -- ^ Set window width and height
    , reloadProject         :: FilePath -> IO ()       -- ^ Reload cabal project views
    }

dummyUI :: UI e
dummyUI = UI
  { main             = return ()
  , end              = const (return ())
  , suspend          = return ()
  , refresh          = const (return ())
  , userForceRefresh = return ()
  , layout           = return
  , reloadProject    = const (return ())
  }
