diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,3 @@
+#!/usr/bin/env runhaskell
+> import Distribution.Simple
+> main = defaultMain
diff --git a/TODO b/TODO
new file mode 100644
--- /dev/null
+++ b/TODO
@@ -0,0 +1,49 @@
+* Restructure it as a library and upload to hackage.
+  Export lists everywhere!
+  * Mention its HaskGame based (it uses its Vector2 type/etc)
+* Abstract away SDL keyboard keys, events, flip and delay
+  * Implement a GLUT-based version
+
+* Grid with delete row/column, Box with delete item?
+
+* Animations:
+  * Draw -> Animate?
+    * Can use ID's on all drawings, and have it animate stuff moving around
+
+Questions:
+
+* After chat with conal:
+
+  * type FontMap = Map String Font
+  * type Draw = FontMap -> Image
+  * render :: Font -> (Image, Size)
+    * unsafePerformIO all font renderings?
+    * Have Size be an opaque type usable only for Image manipulations?
+
+* Is the "Clipboard" a good idea?
+  * Maybe have a "Clipring" instead?
+
+  * Clipring is a list of N items, and showing it shows one line per item
+    with the "summary" of each.
+
+  * When whole content fits in line it is shown, otherwise, it is
+    "summarized". Each model needs to be associated with 
+
+  * Bottom line of the screen can show summary of top clipring item
+  * Clipring accessible with some key, animating all the contents up,
+    allowing to move item to the top
+
+* What do widget actions do, besides updating the model?
+  1. Read and write from the clipboard (Clipboard monad?)
+  2. "Signal"? Probably not, as it implies a side-effect.
+
+* Consider removing the accessor arg and using adapter always
+  This means child containers have to be parameterized on the child
+  rather than on the generic model, so Grid is not Widget model
+  but Widget (Mutable, Map Cursor childModel)?
+  Then, you can adapt the real model to that tuple.
+  This is probably less convenient than what I have now
+
+* Can I create a generic newDelegate method in FocusDelegator instead
+  of copying it into each widget?  Its probably possible by using ugly
+  accessors/adapters, but less important at the moment.
diff --git a/lui.cabal b/lui.cabal
new file mode 100644
--- /dev/null
+++ b/lui.cabal
@@ -0,0 +1,51 @@
+Name:                lui
+Version:             0.0.1
+Cabal-Version:       >= 1.2
+Synopsis:            Purely FunctionaL User Interface
+Category:            graphics
+Description:
+  This package contains a set of composable purely functional widgets
+  and a mainloop adapter to adapt the widgets to run in IO. It is
+  HaskGame based.
+  .
+  HaskGame does not yet wrap SDL properly, so it uses SDL directly as
+  well.
+  .
+  Project wiki page: <http://haskell.org/haskellwiki/LUI>
+  .
+  &#169; 2009 by Eyal Lotem; BSD3 license.
+
+Author:              Eyal Lotem
+Maintainer:          eyal.lotem@gmail.com
+--Homepage:            http://haskell.org/haskellwiki/LUI
+--Package-Url:         http://code.haskell.org/LUI
+Copyright:           (c) 2008 by Eyal Lotem
+License:             BSD3
+Stability:           experimental
+build-type:          Simple
+
+Library
+  hs-Source-Dirs:      src
+  Extensions:
+  Build-Depends:       base, haskell98, containers, mtl, SDL, haskgame
+  Exposed-Modules:     Graphics.UI.LUI.Accessor,
+                       Graphics.UI.LUI.Draw,
+                       Graphics.UI.LUI.Run,
+                       Graphics.UI.LUI.Widget,
+                       Graphics.UI.LUI.Widgets.Adapter,
+                       Graphics.UI.LUI.Widgets.Box,
+                       Graphics.UI.LUI.Widgets.FocusDelegator,
+                       Graphics.UI.LUI.Widgets.Grid,
+                       Graphics.UI.LUI.Widgets.KeysTable,
+                       Graphics.UI.LUI.Widgets.Space,
+                       Graphics.UI.LUI.Widgets.TextEdit,
+                       Graphics.UI.LUI.Widgets.TextView,
+                       Graphics.UI.LUI.Widgets.Unfocusable
+  Other-Modules:       Example,
+                       Graphics.UI.LUI.Func,
+                       Graphics.UI.LUI.List,
+                       Graphics.UI.LUI.Tuple
+
+  ghc-options:         -Wall
+
+--  ghc-prof-options:    -prof -auto-all 
diff --git a/src/Example.hs b/src/Example.hs
new file mode 100644
--- /dev/null
+++ b/src/Example.hs
@@ -0,0 +1,159 @@
+{-# OPTIONS_GHC -Wall -O2
+ #-}
+
+module Example(main) where
+
+import qualified Graphics.UI.LUI.Run as Run
+import qualified Graphics.UI.LUI.Widgets.TextEdit as TextEdit
+import qualified Graphics.UI.LUI.Widgets.TextView as TextView
+import qualified Graphics.UI.LUI.Widgets.Grid as Grid
+import qualified Graphics.UI.LUI.Widgets.Box as Box
+import qualified Graphics.UI.LUI.Widgets.Space as Space
+import qualified Graphics.UI.LUI.Widgets.KeysTable as KeysTable
+import Graphics.UI.LUI.Widget(Widget)
+import Graphics.UI.LUI.Accessor(Accessor, accessor, aMapValue, (^>), (^.))
+
+import qualified Graphics.UI.HaskGame.Font as Font
+import qualified Graphics.UI.HaskGame as HaskGame
+import Graphics.UI.HaskGame.Font(Font)
+import Graphics.UI.HaskGame.Color(Color(..))
+
+import qualified Data.Map as Map
+import Data.Maybe(listToMaybe)
+import Control.Monad(mapM)
+
+isSorted :: (Ord a) => [a] -> Bool
+isSorted xs = and $ zipWith (<=) xs (tail xs)
+
+main :: IO ()
+main = HaskGame.withInit $ do
+    gui <- makeGui
+    Run.mainLoop gui guiModel
+    return ()
+
+-- Model:
+data Model = Model
+    {
+      vboxModel :: Box.DelegatedMutable
+    , hboxModel :: Box.Mutable
+    , textEditModels :: Map.Map Grid.Cursor TextEdit.DelegatedMutable
+    , gridModel :: Grid.DelegatedMutable
+    }
+
+data Fonts = Fonts
+    {
+      defaultFont, textViewFont, keysFont, descFont :: Font
+    }
+
+-- TODO: Replace with TH auto-gen
+avboxModel :: Accessor Model Box.DelegatedMutable
+avboxModel = accessor vboxModel (\new x -> x{vboxModel=new})
+ahboxModel :: Accessor Model Box.Mutable
+ahboxModel = accessor hboxModel (\new x -> x{hboxModel=new})
+atextEditModels :: Accessor Model (Map.Map Grid.Cursor TextEdit.DelegatedMutable)
+atextEditModels = accessor textEditModels (\new x -> x{textEditModels=new})
+agridModel :: Accessor Model Grid.DelegatedMutable
+agridModel = accessor gridModel (\new x -> x{gridModel=new})
+
+texts :: [String]
+texts =
+    [
+     "Hello"
+    ,"World"
+    ,"Blah"
+    ,"Bleh"
+    ]
+
+guiModel :: Model
+guiModel =
+    Model
+    {
+      vboxModel = Box.delegatedMutable False 0
+    , hboxModel = Box.Mutable 0
+    , textEditModels =
+      Map.fromList [((x, y),
+                     TextEdit.delegatedMutable False (texts!!(y*2+x)) 5)
+                    | x <- [0..1]
+                   , y <- [0..1]]
+    , gridModel = Grid.delegatedMutable False (0, 0)
+    }
+
+
+-- Widgets
+
+textEditCursorColor, textViewColor, textEditColor, textEditingColor :: Color
+textEditingColor = Color 30 20 100
+textEditColor = Color 255 255 255
+textViewColor = Color 255 100 255
+textEditCursorColor = Color 255 0 0
+
+textEdit :: Grid.Cursor -> Fonts -> Widget Model
+textEdit cursor fonts =
+    TextEdit.newDelegated textEditingColor
+                          textEditCursorColor
+                          (defaultFont fonts)
+                          textEditColor $
+                          atextEditModels ^> aMapValue cursor
+                          
+
+textView :: String -> Fonts -> Widget Model
+textView text fonts =
+    TextView.new textViewColor (textViewFont fonts) text
+
+grid, hbox, vbox, keysTable, proxy1, proxy2 :: Fonts -> Widget Model
+
+gridSize :: Grid.Cursor
+gridSize = (2, 2)
+
+grid fonts =
+    Grid.newDelegated gridSize items agridModel
+    where
+      items = Map.fromList
+              [((x, y), Grid.Item (textEdit (x, y) fonts) (0.5, 1))
+               | x <- [0..1], y <- [0..1]]
+
+hbox fonts =
+    Box.new Box.Horizontal items ahboxModel
+    where
+      items = [Box.Item (vbox fonts) 0.5
+              ,Box.Item (Space.newW 50) 0
+              ,Box.Item (keysTable fonts) 0]
+
+vbox fonts = Box.newDelegated Box.Vertical items avboxModel
+    where
+      items = [Box.Item (grid fonts) 1
+              ,Box.Item (Space.newH 100) 0.5
+              ,Box.Item (proxy1 fonts) 0.5
+              ,Box.Item (textView "This is just a view" fonts) 0.5
+              ,Box.Item (proxy2 fonts) 0.5]
+
+keysTable fonts = KeysTable.newForWidget (keysFont fonts) (descFont fonts) (hbox fonts)
+
+proxy1 fonts model =
+    textEdit (model ^. agridModel ^. Grid.aDelegatedMutableCursor) fonts model
+
+simpleRead :: Read a => String -> Maybe a
+simpleRead = listToMaybe . map fst . filter (null . snd) . reads
+
+readCursor :: String -> Maybe Grid.Cursor
+readCursor text =
+    let (xCount, yCount) = gridSize
+        verifyCursor cursor@(x, y) =
+            if isSorted [0, x, xCount-1] &&
+               isSorted [0, y, yCount-1]
+            then Just cursor
+            else Nothing
+    in verifyCursor =<< simpleRead text
+
+proxy2 fonts model =
+    let cursor = model ^. agridModel ^. Grid.aDelegatedMutableCursor
+        text = model ^. atextEditModels ^. aMapValue cursor ^.
+               TextEdit.aDelegatedMutableText
+    in maybe (textView ("Invalid cursor position selected: " ++ text) fonts model)
+             (\cur -> textEdit cur fonts model) $
+       readCursor text
+
+makeGui :: IO (Widget Model)
+makeGui = do
+  [f15, f25, f30] <- mapM Font.defaultFont [15, 25, 30]
+  return . hbox $ Fonts f30 f15 f25 f25
diff --git a/src/Graphics/UI/LUI/Accessor.hs b/src/Graphics/UI/LUI/Accessor.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/UI/LUI/Accessor.hs
@@ -0,0 +1,83 @@
+{-# OPTIONS_GHC -Wall -O2
+ #-}
+
+-- TODO: Put this in a more generic library
+
+module Graphics.UI.LUI.Accessor
+    (Accessor,accessor,convertor
+    -- getter/setter
+    ,(^.),(^:)
+    -- composition
+    ,(<^),(^>)
+    -- Accessors
+    ,self,null,reader
+    ,write
+    -- Tuple
+    ,afirst,asecond
+    -- List
+    ,anth
+    -- Data.Map
+    ,aMapValue
+    ,aMapValueDefault) where
+
+import Graphics.UI.LUI.List(nth)
+
+import Control.Arrow(first, second)
+import qualified Data.Map as Map
+import Data.Map(Map)
+
+data Accessor whole part =
+    Accessor
+    {
+      accessorGet :: whole -> part
+    , accessorSet :: part -> whole -> whole
+    }
+
+accessor :: (whole -> part) ->
+            (part -> whole -> whole) ->
+            Accessor whole part
+accessor = Accessor
+
+-- If you can create a whole from a part, then its really a convertor:
+convertor :: (whole -> part) -> (part -> whole) ->
+             Accessor whole part
+convertor extract build = accessor extract (const . build)
+
+self :: Accessor a a
+self = accessor id const
+
+reader :: r -> Accessor a r
+reader x = accessor (const x) (const id)
+
+write :: Accessor whole part -> part -> whole -> whole
+write = accessorSet
+
+(^.) :: whole -> Accessor whole part -> part
+(^.) = flip accessorGet
+
+(^:) :: Accessor whole part -> (part -> part) -> whole -> whole
+(acc ^: modifyPart) whole = accessorSet acc
+                            (modifyPart (whole ^. acc)) whole
+
+(^>) :: Accessor a b -> Accessor b c -> Accessor a c
+x ^> y = accessor (accessorGet y . accessorGet x)
+                  ((x ^:) . accessorSet y)
+
+(<^) :: Accessor b c -> Accessor a b -> Accessor a c
+(<^) = flip (^>)
+
+afirst :: Accessor (a, b) a
+afirst = accessor fst (first . const)
+asecond :: Accessor (a, b) b
+asecond = accessor snd (second . const)
+
+anth :: Int -> Accessor [a] a
+anth n = accessor (!!n) (nth n . const)
+
+aMapValue :: Ord k => k -> Accessor (Map k a) a
+aMapValue key = accessor (Map.! key) setValue
+    where
+      setValue value = Map.adjust (const value) key
+
+aMapValueDefault :: Ord k => a -> k -> Accessor (Map k a) a
+aMapValueDefault def key = accessor (Map.findWithDefault def key) (Map.insert key)
diff --git a/src/Graphics/UI/LUI/Draw.hs b/src/Graphics/UI/LUI/Draw.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/UI/LUI/Draw.hs
@@ -0,0 +1,85 @@
+{-# OPTIONS -Wall -O2
+  -XGeneralizedNewtypeDeriving
+  #-}
+
+module Graphics.UI.LUI.Draw
+    (Position,Size
+    -- The monads
+    ,Draw
+    ,Compute
+    -- Monad runners
+    ,render
+    ,computeResult
+    -- Compute primitives
+    ,textSize
+    -- Draw primitives
+    ,computeToDraw
+    ,text
+    ,rect
+    ,move
+    ) where
+
+import qualified Graphics.UI.HaskGame as HaskGame
+import qualified Graphics.UI.HaskGame.Font as Font
+import qualified Graphics.UI.HaskGame.Rect as Rect
+import Graphics.UI.HaskGame.Vector2(Vector2(..))
+import Graphics.UI.HaskGame.Color(Color)
+import Graphics.UI.HaskGame.Font(Font)
+
+import Control.Monad.Trans(lift)
+import Control.Monad.Reader(ReaderT, ask, local, runReaderT)
+
+type Position = Vector2 Int
+type Size = Vector2 Int
+
+newtype Compute a = Compute { unCompute :: IO a }
+    deriving Monad
+liftIO :: IO a -> Compute a
+liftIO = Compute
+
+-- Monad Transformers require a bit of boiler-plate...
+newtype Draw a = Draw { unDraw :: ReaderT HaskGame.Surface (ReaderT Position Compute) a }
+    deriving Monad
+liftPosition :: ReaderT Position Compute a -> Draw a
+liftPosition = Draw . lift
+liftSurface :: ReaderT HaskGame.Surface (ReaderT Position Compute) a -> Draw a
+liftSurface = Draw
+
+computeToDraw :: Compute a -> Draw a
+computeToDraw = Draw . lift . lift
+
+computeResult :: Compute a -> IO a
+computeResult = unCompute
+
+render :: HaskGame.Surface -> Position -> Draw a -> IO a
+render surface pos = computeResult .
+                     flip runReaderT pos .
+                     flip runReaderT surface .
+                     unDraw
+
+textSize :: Font -> String -> Compute Size
+textSize font str = do
+  liftIO $ Font.textSize font str
+
+text :: Color -> Font -> String -> Draw Size
+text color font str = do
+  surface <- liftSurface ask
+  position <- liftPosition ask
+  computeToDraw $ do
+    textSurface <- liftIO $ Font.renderText font str color
+    liftIO $ HaskGame.blit surface position textSurface
+    textSize font str
+  
+rect :: Color -> Size -> Draw Size
+rect color size = do
+  surface <- liftSurface $ ask
+  position <- liftPosition ask
+  let r = Rect.makeRect position size
+  computeToDraw . liftIO $ HaskGame.fillRect surface r color
+  return size
+
+move :: Position -> Draw a -> Draw a
+move delta (Draw act) = do
+  surface <- liftSurface $ ask
+  let posReaderT = flip runReaderT surface act
+  liftPosition $ local (+delta) posReaderT
diff --git a/src/Graphics/UI/LUI/Func.hs b/src/Graphics/UI/LUI/Func.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/UI/LUI/Func.hs
@@ -0,0 +1,18 @@
+{-# OPTIONS_GHC -Wall -O2
+  #-}
+
+module Graphics.UI.LUI.Func(result, argument, (~>)) where
+
+-- TODO: Put this in a more generic library
+
+import Control.Arrow(Arrow, (<<<), (>>>))
+
+result :: Arrow arr => arr b c -> arr a b -> arr a c
+result = (<<<)
+
+argument :: Arrow arr => arr a b -> arr b c -> arr a c
+argument = (>>>)
+
+infixr 2 ~>
+(~>) :: (Arrow arr) => arr a b -> arr c d -> arr b c -> arr a d
+(arg ~> res) func = arg >>> func >>> res
diff --git a/src/Graphics/UI/LUI/List.hs b/src/Graphics/UI/LUI/List.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/UI/LUI/List.hs
@@ -0,0 +1,15 @@
+{-# OPTIONS_GHC -Wall -O2
+  #-}
+
+-- TODO: Put this in a more generic library
+
+module Graphics.UI.LUI.List(isSorted, nth) where
+    isSorted :: (Ord a) => [a] -> Bool
+    isSorted xs = and $ zipWith (<=) xs (tail xs)
+
+    -- Semantic editor combinator, like "first" or "second", but for
+    -- an index in a list
+    nth :: Int -> (a -> a) -> [a] -> [a]
+    nth _ _    [] = error "nth index out of range"
+    nth 0 func (x:xs) = func x:xs
+    nth n func (x:xs) = x:nth (n-1) func xs
diff --git a/src/Graphics/UI/LUI/Run.hs b/src/Graphics/UI/LUI/Run.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/UI/LUI/Run.hs
@@ -0,0 +1,82 @@
+{-# OPTIONS_GHC -Wall -O2
+ #-}
+
+module Graphics.UI.LUI.Run(mainLoop)
+where
+
+import qualified Graphics.UI.LUI.Draw as Draw
+import qualified Graphics.UI.LUI.Widget as Widget
+import Graphics.UI.LUI.Widget(Widget, WidgetFuncs(..))
+
+import qualified Graphics.UI.SDL as SDL
+import qualified Graphics.UI.HaskGame as HaskGame
+import qualified Graphics.UI.HaskGame.Key as Key
+import qualified Graphics.UI.HaskGame.Keys as Keys
+import Graphics.UI.HaskGame.Vector2(Vector2(..))
+import Graphics.UI.HaskGame.Color(Color(..))
+
+import qualified Control.Monad.State as State
+import qualified Data.Map as Map
+import Control.Monad(forM, forM_, msum)
+import Control.Monad.Trans(lift)
+
+-- Commented out for 6.8's lack of the new Exception
+-- import qualified Control.Exception as Exc
+-- import Data.Typeable(Typeable)
+
+-- Commented out for 6.8's lack of the new Exception
+-- data QuitRequest = QuitRequest
+--   deriving (Typeable, Show)
+-- instance Exc.Exception QuitRequest where
+
+handleKeyAction :: WidgetFuncs model ->
+                   Widget.KeyStatus -> Key.Keysym -> Maybe model
+handleKeyAction widgetFuncs keyStatus keySym =
+  let key = Key.keyOfEvent keySym
+      keyGroups = Keys.groupsOfKey key
+      mKeyHandler = msum $ map lookupGroup keyGroups
+      lookupGroup keyGroup = Map.lookup (keyStatus, keyGroup) =<<
+                             widgetGetKeymap widgetFuncs
+      runHandler (_, func) = func key
+  in fmap runHandler mKeyHandler
+
+handleEvents :: [HaskGame.Event] -> Widget model ->
+                State.StateT model IO Bool
+handleEvents events widget =
+  fmap or $ forM events $ \event -> do
+    model <- State.get
+    let mNewModel = case event of
+                      -- 6.8 exceptions :-(
+                      -- lift $ Exc.throwIO QuitRequest
+                      SDL.Quit -> error "Quit"
+                      SDL.KeyDown k -> handleKeyAction (widget model) Widget.KeyDown k
+                      SDL.KeyUp k -> handleKeyAction (widget model) Widget.KeyUp k
+                      _ -> Nothing
+    case mNewModel of
+      Nothing ->
+          return False
+      Just newState -> do
+          State.put newState
+          return True
+
+-- TODO: Must return the new model
+mainLoop :: Widget model -> model -> IO model
+mainLoop widget initModel = do
+  display <- HaskGame.setVideoMode 800 600 16
+  (`State.execStateT` initModel) $
+    forM_ (True:repeat False) $ \shouldDraw -> do
+      events <- lift $ HaskGame.getEvents
+      handledEvent <- handleEvents events widget
+      model <- State.get
+      lift $ do
+        HaskGame.fillSurface display (Color 0 0 0)
+        if handledEvent || shouldDraw
+          then do
+            -- forM_ (Map.assocs $ fromMaybe Map.empty $ Widget.getKeymap widget model) $
+            --   \((_, group), (desc, _)) -> do
+            --     print (MySDLKey.keyGroupName group, desc)
+            let draw = widgetDraw (widget model) (Widget.DrawInfo True)
+            Draw.render display (Vector2 0 0) draw
+            SDL.flip display
+          else
+            SDL.delay 20
diff --git a/src/Graphics/UI/LUI/Tuple.hs b/src/Graphics/UI/LUI/Tuple.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/UI/LUI/Tuple.hs
@@ -0,0 +1,8 @@
+{-# OPTIONS_GHC -Wall -O2
+  #-}
+
+-- TODO: Put this in a more generic library
+
+module Graphics.UI.LUI.Tuple(swap) where
+    swap :: (a, b) -> (b, a)
+    swap (x, y) = (y, x)
diff --git a/src/Graphics/UI/LUI/Widget.hs b/src/Graphics/UI/LUI/Widget.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/UI/LUI/Widget.hs
@@ -0,0 +1,47 @@
+{-# OPTIONS_GHC -Wall -O2
+  #-}
+
+module Graphics.UI.LUI.Widget
+    (DrawInfo(..)
+    ,WidgetFuncs(..)
+    ,KeyStatus(..)
+    ,Widget
+    ,New
+    ,KeyAction
+    ,Handler
+    ,ActionHandlers
+    )
+where
+
+import Graphics.UI.LUI.Draw(Draw, Compute, Size)
+import Graphics.UI.LUI.Accessor(Accessor)
+
+import qualified Graphics.UI.HaskGame.Key as Key
+
+import qualified Data.Map as Map
+
+data KeyStatus = KeyDown | KeyUp
+  deriving (Eq, Ord, Show, Read)
+
+type KeyAction = (KeyStatus, Key.KeyGroup)
+type Handler model = (String, Key.ModKey -> model)
+type ActionHandlers model = Map.Map KeyAction (Handler model)
+
+data DrawInfo = DrawInfo
+    {
+      diHasFocus :: Bool
+    }
+  deriving (Eq, Ord, Show, Read)
+
+-- TODO: Consider moving the model argument outside of the record
+data WidgetFuncs model = WidgetFuncs
+    {
+      widgetDraw :: DrawInfo -> Draw Size
+    , widgetSize :: DrawInfo -> Compute Size
+    , widgetGetKeymap :: Maybe (ActionHandlers model)
+    }
+
+type Widget model = model -> WidgetFuncs model
+
+type New model mutable =
+    Accessor model mutable -> Widget model
diff --git a/src/Graphics/UI/LUI/Widgets/Adapter.hs b/src/Graphics/UI/LUI/Widgets/Adapter.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/UI/LUI/Widgets/Adapter.hs
@@ -0,0 +1,27 @@
+{-# OPTIONS_GHC -Wall -O2
+  #-}
+
+module Graphics.UI.LUI.Widgets.Adapter(adapt) where
+
+import qualified Graphics.UI.LUI.Widget as Widget
+import Graphics.UI.LUI.Widget(Widget, WidgetFuncs(..))
+
+import Graphics.UI.LUI.Func(result)
+import Graphics.UI.LUI.Accessor(Accessor, write, (^.))
+
+import qualified Data.Map as Map
+import Control.Arrow(second)
+
+adapt :: Accessor whole part -> Widget part -> Widget whole
+adapt acc widget model =
+    let widgetFuncs = widget (model ^. acc)
+    in
+      WidgetFuncs
+      {
+        widgetDraw = widgetDraw widgetFuncs
+      , widgetSize = widgetSize widgetFuncs
+      , widgetGetKeymap =
+          let keymap = widgetGetKeymap widgetFuncs
+              convertModel part = write acc part model
+          in (fmap . Map.map . second . result) convertModel $ keymap
+      }
diff --git a/src/Graphics/UI/LUI/Widgets/Box.hs b/src/Graphics/UI/LUI/Widgets/Box.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/UI/LUI/Widgets/Box.hs
@@ -0,0 +1,82 @@
+{-# OPTIONS_GHC -Wall -O2
+  #-}
+
+module Graphics.UI.LUI.Widgets.Box
+    (Orientation(..)
+    ,Item(..)
+    ,Mutable(..)
+    ,Cursor
+    ,new
+    ,aMutableCursor
+    ,DelegatedMutable
+    ,delegatedMutable
+    ,aDelegatedMutableCursor
+    ,newDelegated
+    ,newDelegatedWith
+    ) where
+
+import qualified Graphics.UI.LUI.Widget as Widget
+import qualified Graphics.UI.LUI.Widgets.Grid as Grid
+import qualified Graphics.UI.LUI.Widgets.FocusDelegator as FocusDelegator
+import Graphics.UI.LUI.Widget(Widget)
+import Graphics.UI.LUI.Tuple(swap)
+import Graphics.UI.LUI.Accessor(Accessor, convertor, (^>))
+
+import Graphics.UI.HaskGame.Color(Color(..))
+
+import qualified Data.Map as Map
+
+data Orientation = Horizontal | Vertical
+
+data Item model = Item
+    {
+      itemChildWidget :: Widget model
+
+    -- See comment about Grid.Item alignments
+    , itemAlignment :: Double
+    }
+
+type Cursor = Int
+
+data Mutable = Mutable
+    {
+      mutableCursor :: Cursor
+    }
+-- TODO: Auto-TH for this
+aMutableCursor :: Accessor Mutable Cursor
+aMutableCursor = convertor mutableCursor Mutable
+
+new :: Orientation -> [Item model] -> Widget.New model Mutable
+new orientation items acc =
+    Grid.new gridSize gridItems $ acc ^> boxGridConvertor
+    where
+      gridSize = (maybeSwap (1, length items))
+      gridItems = (Map.fromList $
+                   [(maybeSwap (0, i),
+                     Grid.Item childWidget . maybeSwap $ (0, alignment))
+                    | (i, Item childWidget alignment) <- zip [0..] items])
+      maybeSwap = case orientation of
+                    Vertical -> id
+                    Horizontal -> swap
+      boxGridConvertor = convertor mutableToGridMutable gridMutableToMutable
+      mutableToGridMutable = Grid.Mutable . maybeSwap . (,) 0 . mutableCursor
+      gridMutableToMutable = Mutable . snd . maybeSwap . Grid.mutableCursor
+
+type DelegatedMutable = FocusDelegator.DelegatedMutable Mutable
+
+aDelegatedMutableCursor :: Accessor DelegatedMutable Cursor
+aDelegatedMutableCursor = FocusDelegator.aDelegatedMutable ^> aMutableCursor
+
+delegatedMutable :: Bool -> Cursor -> DelegatedMutable
+delegatedMutable startInside cursor =
+    (FocusDelegator.Mutable startInside, Mutable cursor)
+
+newDelegatedWith :: Color -> Orientation -> [Item model] ->
+                    Widget.New model DelegatedMutable
+newDelegatedWith focusColor orientation items acc =
+    let box = new orientation items $ acc ^> FocusDelegator.aDelegatedMutable
+    in FocusDelegator.newWith focusColor "Go in" "Go out" box $
+       acc ^> FocusDelegator.aFocusDelegatorMutable
+
+newDelegated :: Orientation -> [Item model] -> Widget.New model DelegatedMutable
+newDelegated = newDelegatedWith FocusDelegator.defaultFocusColor
diff --git a/src/Graphics/UI/LUI/Widgets/FocusDelegator.hs b/src/Graphics/UI/LUI/Widgets/FocusDelegator.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/UI/LUI/Widgets/FocusDelegator.hs
@@ -0,0 +1,98 @@
+{-# OPTIONS_GHC -Wall -O2
+  #-}
+
+module Graphics.UI.LUI.Widgets.FocusDelegator
+    (Mutable(..)
+    ,DelegatedMutable
+    ,aDelegatedMutable
+    ,aFocusDelegatorMutable
+    ,defaultFocusColor
+    ,newWith
+    ,new
+    )
+where
+
+import qualified Graphics.UI.LUI.Widget as Widget
+import qualified Graphics.UI.LUI.Draw as Draw
+import Graphics.UI.LUI.Widget(Widget, WidgetFuncs(..))
+
+import Graphics.UI.LUI.Func(result)
+import Graphics.UI.LUI.Accessor(Accessor, afirst, asecond, (^.), write)
+
+import qualified Graphics.UI.SDL as SDL
+import qualified Graphics.UI.HaskGame.Key as Key
+import Graphics.UI.HaskGame.Key(asKeyGroup, noMods)
+import Graphics.UI.HaskGame.Color(Color(..))
+
+import qualified Data.Map as Map
+import Control.Arrow(second)
+import Data.Maybe(fromMaybe)
+
+-- TODO: Use record instead of tuple so auto-TH creates the accessors:
+type DelegatedMutable mutable = (Mutable, mutable)
+aDelegatedMutable :: Accessor (DelegatedMutable mutable) mutable
+aDelegatedMutable = asecond
+aFocusDelegatorMutable :: Accessor (DelegatedMutable mutable) Mutable
+aFocusDelegatorMutable = afirst
+
+-- defaults:
+defaultFocusColor :: Color
+defaultFocusColor = Color 0 0 150
+
+data Mutable = Mutable
+    {
+      mutableDelegateFocus :: Bool
+    }
+
+buildKeymap :: SDL.SDLKey -> String -> Bool -> Widget.ActionHandlers Mutable
+buildKeymap key desc newDelegating =
+    Map.singleton (Widget.KeyDown, asKeyGroup noMods key)
+                  (desc, const $ Mutable newDelegating)
+
+delegatingKeyMap, nonDelegatingKeyMap ::
+    String -> Widget.ActionHandlers Mutable
+nonDelegatingKeyMap startStr = buildKeymap SDL.SDLK_RETURN startStr True
+delegatingKeyMap    stopStr  = buildKeymap SDL.SDLK_ESCAPE stopStr False
+
+newWith :: Color -> String -> String -> Widget model -> Widget.New model Mutable
+newWith focusColor startStr stopStr childWidget acc model =
+    let Mutable delegating = model ^. acc
+        childWidgetFuncs = childWidget model
+    in WidgetFuncs
+    {
+      widgetSize = \drawInfo -> widgetSize childWidgetFuncs drawInfo
+    , widgetDraw = \drawInfo -> do
+        let 
+            haveFocus = Widget.diHasFocus drawInfo
+            delegatorHasFocus = haveFocus && not delegating
+            childDrawInfo = Widget.DrawInfo
+                            {
+                              Widget.diHasFocus = haveFocus && delegating
+                            }
+        if delegatorHasFocus
+          then do
+            size <- Draw.computeToDraw $
+                    widgetSize childWidgetFuncs childDrawInfo
+            Draw.rect focusColor size
+            return ()
+          else
+            return ()
+        widgetDraw childWidgetFuncs childDrawInfo
+    , widgetGetKeymap =
+        let mChildKeymap = widgetGetKeymap childWidgetFuncs
+            childKeymap = fromMaybe Map.empty mChildKeymap
+            applyToModel newMutable = acc `write` newMutable $ model
+            wrapKeymap = (Map.map . second . result) applyToModel
+        in case delegating of
+             True ->
+                 Just $
+                 childKeymap `Map.union` (wrapKeymap $ delegatingKeyMap stopStr)
+             False ->
+                 const (wrapKeymap $ nonDelegatingKeyMap startStr)
+                 -- Only expose the nonDelegatingKeyMap if the child
+                 -- has a keymap:
+                 `fmap` mChildKeymap
+    }
+
+new :: String -> String -> Widget model -> Widget.New model Mutable
+new = newWith defaultFocusColor
diff --git a/src/Graphics/UI/LUI/Widgets/Grid.hs b/src/Graphics/UI/LUI/Widgets/Grid.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/UI/LUI/Widgets/Grid.hs
@@ -0,0 +1,230 @@
+{-# OPTIONS_GHC -Wall -O2
+  #-}
+
+module Graphics.UI.LUI.Widgets.Grid
+    (Item(..)
+    ,Mutable(..)
+    ,Items
+    ,Cursor
+    ,new
+    ,aMutableCursor
+    ,DelegatedMutable
+    ,delegatedMutable
+    ,aDelegatedMutableCursor
+    ,newDelegated
+    ,newDelegatedWith
+    )
+where
+
+import qualified Graphics.UI.LUI.Widget as Widget
+import qualified Graphics.UI.LUI.Widgets.FocusDelegator as FocusDelegator
+import qualified Graphics.UI.LUI.Draw as Draw
+
+import Graphics.UI.LUI.Widget(Widget, WidgetFuncs(..))
+
+import Graphics.UI.LUI.Func((~>), result)
+import Graphics.UI.LUI.List(isSorted)
+import Graphics.UI.LUI.Tuple(swap)
+import Graphics.UI.LUI.Accessor(Accessor, convertor, (^.), (^>), write)
+
+import qualified Graphics.UI.SDL as SDL
+import qualified Graphics.UI.HaskGame.Key as Key
+import Graphics.UI.HaskGame.Key(asKeyGroup, noMods, shift)
+import Graphics.UI.HaskGame.Color(Color)
+import Graphics.UI.HaskGame.Vector2(Vector2(..)
+                                   ,vector2fst,vector2snd)
+
+import qualified Data.Map as Map
+import Control.Monad(forM_, forM)
+import Control.Arrow(second, (***))
+import Data.List(transpose)
+import Data.Maybe(isJust, fromMaybe)
+import Data.Maybe(isNothing)
+
+data Item model = Item
+    {
+      itemWidget :: Widget model
+
+      -- alignments are a number between 0..1 that
+      -- represents where in the grid item's left..right
+      -- and top..down the item should be in
+    , itemAlignments :: (Double, Double)
+    }
+
+type Items model = Map.Map Cursor (Item model)
+type Cursor = (Int, Int)
+
+data Mutable = Mutable
+    {
+      mutableCursor :: Cursor
+    }
+-- TODO: Auto-TH for this
+aMutableCursor :: Accessor Mutable Cursor
+aMutableCursor = convertor mutableCursor Mutable
+
+selectedItem :: Mutable -> Items model -> Maybe (Item model)
+selectedItem (Mutable cursor) items = cursor `Map.lookup` items
+
+gridRows :: Cursor -> Items model -> [[(Cursor, Maybe (Item model))]]
+gridRows (sizex, sizey) items =
+    [[((x,y), (x,y) `Map.lookup` items) | x <- [0..sizex-1]] | y <- [0..sizey-1]]
+
+gridDrawInfo :: Mutable -> Cursor -> Widget.DrawInfo -> Widget.DrawInfo
+gridDrawInfo (Mutable cursor) itemIndex (Widget.DrawInfo drawInfo) =
+    Widget.DrawInfo (drawInfo && cursor==itemIndex)
+
+getRowColumnSizes :: model -> Mutable -> Items model -> Cursor -> Widget.DrawInfo ->
+                     Draw.Compute ([Int], [Int])
+getRowColumnSizes model mutable items size drawInfo = do
+  rowsSizes <- forM (gridRows size items) . mapM $ \(itemIndex, mItem) ->
+                   case mItem of
+                     Nothing -> return $ Vector2 0 0
+                     Just item ->
+                         widgetSize ((itemWidget item) model)
+                                    (gridDrawInfo mutable itemIndex drawInfo)
+
+  let rowsHeights = (map . map) vector2snd rowsSizes
+      rowsWidths = (map . map) vector2fst rowsSizes
+      rowHeights = map maximum $ rowsHeights
+      columnWidths = map maximum . transpose $ rowsWidths
+  return (rowHeights, columnWidths)
+
+mutableCursorApply :: (Cursor -> Cursor) -> Mutable -> Mutable
+mutableCursorApply func (Mutable oldCursor) = Mutable $ func oldCursor
+
+mutableMoveTo :: (Int, Int) -> Mutable -> Mutable
+mutableMoveTo newCursor = mutableCursorApply (const newCursor)
+
+itemSelectable :: model -> Item model -> Bool
+itemSelectable model (Item widget _) =
+    isJust . widgetGetKeymap $ widget model
+
+getSelectables :: ((Int, Int) -> (Int, Int)) ->
+                  (Int -> Int) ->
+                  Cursor -> model -> Mutable -> Items model ->
+                  [(Int, Int)]
+getSelectables toSwap cursorFunc size model (Mutable oldCursor) items =
+    let (sizeA,_) = toSwap size
+        (oldA,b) = toSwap oldCursor
+        nexts = takeWhile (\a -> isSorted [0, a, sizeA-1]) .
+                iterate cursorFunc $ oldA
+        coor a = toSwap (a, b)
+        itemAt a = (coor a, items Map.! coor a)
+    in map fst . filter (itemSelectable model . snd) . map itemAt . drop 1 $ nexts
+
+getSelectablesX, getSelectablesY :: (Int -> Int) ->
+                                    Cursor -> model -> Mutable -> Items model ->
+                                    [(Int, Int)]
+getSelectablesX = getSelectables id
+getSelectablesY = getSelectables swap
+
+getSelectablesXY :: [Int] -> (Int -> Int) ->
+                    Cursor -> model -> Mutable -> Items model ->
+                    [(Int, Int)]
+getSelectablesXY xrange cursorFunc size model (Mutable oldCursor) items =
+    let (sizeX,sizeY) = size
+        (oldX,oldY) = oldCursor
+        xnexts = drop 1 . takeWhile (\x -> isSorted [0, x, sizeX-1]) .
+                 iterate cursorFunc $ oldX
+        ynexts = drop 1 . takeWhile (\y -> isSorted [0, y, sizeY-1]) .
+                 iterate cursorFunc $ oldY
+        nexts = map (flip (,) oldY) xnexts ++
+                [(x, y) | y <- ynexts, x <- xrange]
+        itemAt cursor = (cursor, items Map.! cursor)
+    in map fst . filter (itemSelectable model . snd) . map itemAt $ nexts
+
+keysMap :: Cursor -> model -> Mutable -> Items model -> Widget.ActionHandlers Mutable
+keysMap size@(sizeX, _) model mutable items =
+    Map.fromList $
+       map (((,) Widget.KeyDown) ***
+            second const) . concat $
+           [let opts = axis size model mutable items
+            in cond opts (key, (desc, head opts `mutableMoveTo` mutable))
+            | (key, axis, desc) <-
+                [(asKeyGroup noMods SDL.SDLK_LEFT,
+                  getSelectablesX (subtract 1), "Move left")
+                ,(asKeyGroup noMods SDL.SDLK_RIGHT,
+                  getSelectablesX (+1), "Move right")
+                ,(asKeyGroup noMods SDL.SDLK_UP,
+                  getSelectablesY (subtract 1), "Move up")
+                ,(asKeyGroup noMods SDL.SDLK_DOWN,
+                  getSelectablesY (+1), "Move down")
+                ,(asKeyGroup noMods SDL.SDLK_TAB,
+                  getSelectablesXY [0..sizeX-1] (+1), "Move to next")
+                ,(asKeyGroup shift SDL.SDLK_TAB,
+                  getSelectablesXY [sizeX-1,sizeX-2..0] (subtract 1), "Move to prev")
+                ]
+           ]
+    where
+      cond p i = if not . null $ p then [i] else []
+
+inFrac :: (Integral a, RealFrac b) => (b -> b) -> a -> a
+inFrac = fromIntegral ~> floor
+
+posSizes :: [Int] -> [(Int, Int)]
+posSizes sizes =
+    let positions = scanl (+) 0 sizes
+    in zip positions sizes
+
+new :: Cursor -> Items model -> Widget.New model Mutable
+new size items acc model =
+    let mutable = model ^. acc
+        rows = gridRows size items
+        rowColumnSizes drawInfo = getRowColumnSizes model mutable items size drawInfo
+    in WidgetFuncs
+    {
+      widgetDraw = \drawInfo -> do
+        (rowHeights, columnWidths) <- Draw.computeToDraw . rowColumnSizes $ drawInfo
+        forM_ (zip (posSizes rowHeights) rows) $
+          \((ypos, height), row) -> do
+            forM_ (zip (posSizes columnWidths) row) $
+              \((xpos, width), (itemIndex, mItem)) ->
+                case mItem of
+                  Nothing -> return ()
+                  Just item -> do
+                    let Item childWidget (ax, ay) = item
+                        childDrawInfo = gridDrawInfo mutable itemIndex drawInfo
+                        childWidgetFuncs = childWidget model
+                    Vector2 w h <- Draw.computeToDraw $
+                                   widgetSize childWidgetFuncs childDrawInfo
+                    let pos = Vector2 (xpos + inFrac (*ax) (width-w))
+                                      (ypos + inFrac (*ay) (height-h))
+                    Draw.move pos $ widgetDraw childWidgetFuncs childDrawInfo
+                    return ()
+        return $ Vector2 (sum columnWidths) (sum rowHeights)
+
+    , widgetSize = \drawInfo -> do
+      (rowHeights, columnWidths) <- rowColumnSizes drawInfo
+      return $ Vector2 (sum columnWidths) (sum rowHeights)
+
+    , widgetGetKeymap =
+      if all isNothing .
+         map (widgetGetKeymap . ($model) . itemWidget) .
+         Map.elems $ items
+      then Nothing
+      else Just $
+        let childKeys = fromMaybe Map.empty $ do
+                          Item childWidget _ <- selectedItem mutable items
+                          widgetGetKeymap $ childWidget model
+            applyToModel newMutable = acc `write` newMutable $ model
+        in childKeys `Map.union` ((Map.map . second . result) applyToModel $
+                                  keysMap size model mutable items)
+    }
+
+type DelegatedMutable = FocusDelegator.DelegatedMutable Mutable
+
+aDelegatedMutableCursor :: Accessor DelegatedMutable Cursor
+aDelegatedMutableCursor = FocusDelegator.aDelegatedMutable ^> aMutableCursor
+
+delegatedMutable :: Bool -> Cursor -> DelegatedMutable
+delegatedMutable startInside cursor =
+    (FocusDelegator.Mutable startInside, Mutable cursor)
+
+newDelegatedWith :: Color -> Cursor -> Items model -> Widget.New model DelegatedMutable
+newDelegatedWith focusColor size items acc =
+    let grid = new size items $ acc ^> FocusDelegator.aDelegatedMutable
+    in FocusDelegator.newWith focusColor "Go in" "Go out" grid $
+           acc ^> FocusDelegator.aFocusDelegatorMutable
+
+newDelegated :: Cursor -> Items model -> Widget.New model DelegatedMutable
+newDelegated = newDelegatedWith FocusDelegator.defaultFocusColor
diff --git a/src/Graphics/UI/LUI/Widgets/KeysTable.hs b/src/Graphics/UI/LUI/Widgets/KeysTable.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/UI/LUI/Widgets/KeysTable.hs
@@ -0,0 +1,70 @@
+{-# OPTIONS_GHC -Wall -O2
+  #-}
+
+module Graphics.UI.LUI.Widgets.KeysTable
+    (defaultKeysColor
+    ,defaultDescColor
+    ,defaultSpaceWidth
+    ,new
+    ,newForWidget
+    )
+where
+
+import qualified Graphics.UI.LUI.Widget as Widget
+import qualified Graphics.UI.LUI.Widgets.Grid as Grid
+import qualified Graphics.UI.LUI.Widgets.TextView as TextView
+import qualified Graphics.UI.LUI.Widgets.Unfocusable as Unfocusable
+import qualified Graphics.UI.LUI.Widgets.Space as Space
+import qualified Graphics.UI.HaskGame.Key as Key
+import Graphics.UI.LUI.Widget(Widget, widgetGetKeymap)
+
+import Graphics.UI.LUI.Accessor(reader)
+
+import Graphics.UI.HaskGame.Color(Color(..))
+import Graphics.UI.HaskGame.Font(Font)
+
+import qualified Data.Map as Map
+import Control.Arrow(first, second)
+import Data.List(sort)
+import Data.Maybe(fromMaybe)
+
+-- Defaults:
+defaultKeysColor, defaultDescColor :: Color
+defaultKeysColor = Color 255 0 0
+defaultDescColor = Color 0 0 255
+defaultSpaceWidth :: Int
+defaultSpaceWidth = 10
+
+gItem :: Widget model -> Grid.Item model
+gItem = flip Grid.Item (0, 0.5)
+
+keyBindings :: Widget.ActionHandlers model -> [(Key.KeyGroup, String)]
+keyBindings = sort .
+              (map . first) snd .
+              (map . second) fst .
+              Map.assocs
+
+new :: Color -> Color -> Int -> Font -> Font -> Widget.ActionHandlers model ->
+       Widget model
+new keysColor descColor spaceWidth keysFont descFont handlers = Unfocusable.new grid
+    where
+      grid = Grid.new (3, Map.size handlers) gridItems gridAccessor
+      space = Space.newW spaceWidth
+      gridItems =
+          Map.fromList . concat $
+          [[((0, y), gItem keyGroupTextView),
+            ((1, y), gItem space),
+            ((2, y), gItem descTextView)]
+           | (y, (keyGroup, desc)) <- zip [0..] . keyBindings $ handlers
+           , let keyGroupTextView =
+                     TextView.new keysColor keysFont $ Key.keyGroupName keyGroup
+                 descTextView =
+                     TextView.new descColor descFont desc
+          ]
+      gridAccessor = reader $ Grid.Mutable (0,0)
+
+newForWidget :: Font -> Font -> Widget model -> Widget model
+newForWidget keysFont descFont widget model =
+    let handlers = fromMaybe Map.empty . widgetGetKeymap $ widget model
+    in new defaultKeysColor defaultDescColor defaultSpaceWidth
+           keysFont descFont handlers model
diff --git a/src/Graphics/UI/LUI/Widgets/Space.hs b/src/Graphics/UI/LUI/Widgets/Space.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/UI/LUI/Widgets/Space.hs
@@ -0,0 +1,27 @@
+{-# OPTIONS_GHC -Wall -O2
+  #-}
+
+module Graphics.UI.LUI.Widgets.Space(new, newWH, newW, newH)
+where
+
+import qualified Graphics.UI.LUI.Widget as Widget
+import Graphics.UI.LUI.Widget(Widget, WidgetFuncs(..))
+import Graphics.UI.LUI.Draw(Size)
+import Graphics.UI.HaskGame.Vector2(Vector2(..))
+
+new :: Size -> Widget model
+new size =
+    const $
+    WidgetFuncs
+    {
+      widgetGetKeymap = Nothing
+    , widgetDraw = \_ -> return size
+    , widgetSize = \_ -> return size
+    }
+
+newWH :: Int -> Int -> Widget model
+newWH w h = new $ Vector2 w h
+
+newW, newH :: Int -> Widget model
+newW w = newWH w 0
+newH h = newWH 0 h
diff --git a/src/Graphics/UI/LUI/Widgets/TextEdit.hs b/src/Graphics/UI/LUI/Widgets/TextEdit.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/UI/LUI/Widgets/TextEdit.hs
@@ -0,0 +1,187 @@
+{-# OPTIONS_GHC -Wall -O2
+  #-}
+
+module Graphics.UI.LUI.Widgets.TextEdit
+    (Mutable(..)
+    ,aMutableCursor
+    ,aMutableText
+    ,Cursor
+    ,defaultCursorWidth
+    ,new
+    ,DelegatedMutable
+    ,aDelegatedMutableCursor
+    ,aDelegatedMutableText
+    ,delegatedMutable
+    ,newDelegatedWith
+    ,newDelegated
+    )
+where
+
+import qualified Graphics.UI.LUI.Widget as Widget
+import qualified Graphics.UI.LUI.Draw as Draw
+import qualified Graphics.UI.LUI.Widgets.FocusDelegator as FocusDelegator
+import Graphics.UI.LUI.Widget(WidgetFuncs(..))
+
+import Graphics.UI.LUI.Func(result)
+import Graphics.UI.LUI.List(isSorted)
+import Graphics.UI.LUI.Accessor(Accessor, accessor, (^.), (^>), write)
+
+import qualified Graphics.UI.SDL as SDL
+import qualified Graphics.UI.HaskGame.Key as Key
+import qualified Graphics.UI.HaskGame.Keys as Keys
+import Graphics.UI.HaskGame.Key(asKeyGroup, noMods, ctrl)
+import Graphics.UI.HaskGame.Vector2(Vector2(..))
+import Graphics.UI.HaskGame.Color(Color)
+import Graphics.UI.HaskGame.Font(Font)
+
+import qualified Data.Map as Map
+import Data.Map((!))
+import Control.Arrow(first, second)
+
+type Cursor = Int
+
+defaultCursorWidth :: Int
+defaultCursorWidth = 2
+
+data Mutable = Mutable
+    {
+      mutableText :: String
+    , mutableCursor :: Cursor
+    }
+-- TODO: TH
+aMutableCursor :: Accessor Mutable Cursor
+aMutableCursor = accessor mutableCursor (\n x -> x{mutableCursor=n})
+aMutableText :: Accessor Mutable String
+aMutableText = accessor mutableText  (\n x -> x{mutableText=n})
+
+insert :: Mutable -> Key.ModKey -> Mutable
+insert (Mutable oldText oldCursor) key =
+    let iText = Keys.keysUnicode!key
+        (preOldText, postOldText) = splitAt oldCursor oldText
+        newText = concat [preOldText, iText, postOldText]
+        newCursor = oldCursor + length iText
+    in Mutable newText newCursor
+
+delBackward :: Int -> Mutable -> Mutable
+delBackward count (Mutable oldText oldCursor) =
+    let (oldPreText, oldPostText) = splitAt oldCursor oldText
+        newPreText = take (length oldPreText - count) oldPreText
+        newText = newPreText ++ oldPostText
+        newCursor = length newPreText
+    in Mutable newText newCursor
+
+delForward :: Int -> Mutable -> Mutable
+delForward count (Mutable oldText oldCursor) =
+    let (oldPreText, oldPostText) = splitAt oldCursor oldText
+        newPostText = drop count oldPostText
+        newText = oldPreText ++ newPostText
+    in Mutable newText oldCursor
+
+moveCursor :: (Cursor -> Cursor) -> Mutable -> Mutable
+moveCursor cursorFunc (Mutable text oldCursor) =
+    let newCursor = cursorFunc oldCursor
+    in Mutable text $ if isSorted [0, newCursor, length text]
+                            then newCursor
+                            else oldCursor
+
+goHome :: Mutable -> Mutable
+goHome (Mutable text _) = Mutable text 0
+
+goEnd :: Mutable -> Mutable
+goEnd (Mutable text _) = Mutable text (length text)
+
+actBackspace, actDelete, actMovePrev, actMoveNext, actHome, actEnd ::
+    (String, Mutable -> Mutable)
+
+actBackspace = ("Delete previous character", delBackward 1)
+actDelete = ("Delete next character",        delForward 1)
+actMovePrev = ("Move to previous character", moveCursor (subtract 1))
+actMoveNext = ("Move to next character",     moveCursor (+1))
+actHome = ("Move to beginning of text",      goHome)
+actEnd = ("Move to end of text",             goEnd)
+
+keysMap :: Mutable -> Widget.ActionHandlers Mutable
+keysMap mutable = Map.fromList . (map . first) ((,) Widget.KeyDown) $
+    (Keys.printableGroup, ("Insert", insert mutable)) :
+    (map . second . second) (const . ($mutable)) (normalActions mutable ++ ctrlActions mutable)
+
+cond :: Bool -> [a] -> [a]
+cond p i = if p then i else []
+
+normalActions :: Mutable -> [(Key.KeyGroup, (String, Mutable -> Mutable))]
+normalActions mutable =
+    let cursor = mutableCursor mutable
+        text = mutableText mutable
+    in (map . first) (asKeyGroup noMods) . concat $
+           [cond (cursor > 0)
+                     [(SDL.SDLK_BACKSPACE, actBackspace)
+                     ,(SDL.SDLK_LEFT, actMovePrev)
+                     ,(SDL.SDLK_HOME, actHome)]
+           ,cond (cursor < length text)
+                     [(SDL.SDLK_DELETE, actDelete)
+                     ,(SDL.SDLK_RIGHT, actMoveNext)
+                     ,(SDL.SDLK_END, actEnd)]
+           ]
+
+ctrlActions :: Mutable -> [(Key.KeyGroup, (String, Mutable -> Mutable))]
+ctrlActions mutable =
+    let cursor = mutableCursor mutable
+        text = mutableText mutable
+    in (map . first) (asKeyGroup ctrl) . concat $
+           [cond (cursor > 0)
+                     [(SDL.SDLK_h, actBackspace)
+                     ,(SDL.SDLK_a, actHome)]
+           ,cond (cursor < length text)
+                     [(SDL.SDLK_d, actDelete)
+                     ,(SDL.SDLK_e, actEnd)]
+           ]
+
+new :: Int -> Color -> Color -> Font -> Color -> Widget.New model Mutable
+new cursorWidth bgColor cursorColor font textColor acc model =
+  let mutable@(Mutable text cursor) = model ^. acc
+  in WidgetFuncs
+  {
+    widgetDraw = \drawInfo -> do
+    
+    if Widget.diHasFocus drawInfo
+      then do
+        textSize <- Draw.computeToDraw . Draw.textSize font $ text
+        Vector2 w h <- Draw.computeToDraw . Draw.textSize font $ take cursor text
+        let cursorSize = Vector2 cursorWidth h
+            cursorPos = Vector2 w 0
+        Draw.rect bgColor textSize
+        Draw.text textColor font text
+        Draw.move cursorPos $ Draw.rect cursorColor cursorSize
+        return textSize
+      else
+        Draw.text textColor font text
+
+  , widgetSize = \_ -> Draw.textSize font text
+
+  , widgetGetKeymap =
+    let applyToModel newMutable = acc `write` newMutable $ model
+    in Just $
+       (Map.map . second . result) applyToModel $ keysMap mutable
+  }
+
+type DelegatedMutable = FocusDelegator.DelegatedMutable Mutable
+
+aDelegatedMutableCursor :: Accessor DelegatedMutable Cursor
+aDelegatedMutableCursor = FocusDelegator.aDelegatedMutable ^> aMutableCursor
+aDelegatedMutableText :: Accessor DelegatedMutable String
+aDelegatedMutableText = FocusDelegator.aDelegatedMutable ^> aMutableText
+delegatedMutable :: Bool -> String -> Cursor -> DelegatedMutable
+delegatedMutable startInside text cursor =
+    (FocusDelegator.Mutable startInside, Mutable text cursor)
+
+newDelegatedWith :: Color -> Int -> Color -> Color -> Font -> Color ->
+                    Widget.New model DelegatedMutable
+newDelegatedWith focusColor cursorWidth bgColor cursorColor font textColor acc =
+    let textEdit = new cursorWidth bgColor cursorColor font textColor $
+                   acc ^> FocusDelegator.aDelegatedMutable
+    in FocusDelegator.newWith focusColor "Start editing" "Stop editing" textEdit $
+       acc ^> FocusDelegator.aFocusDelegatorMutable
+
+newDelegated :: Color -> Color -> Font -> Color ->
+                Widget.New model DelegatedMutable
+newDelegated = newDelegatedWith FocusDelegator.defaultFocusColor defaultCursorWidth
diff --git a/src/Graphics/UI/LUI/Widgets/TextView.hs b/src/Graphics/UI/LUI/Widgets/TextView.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/UI/LUI/Widgets/TextView.hs
@@ -0,0 +1,22 @@
+{-# OPTIONS_GHC -Wall -O2
+  #-}
+
+module Graphics.UI.LUI.Widgets.TextView(new)
+where
+
+import qualified Graphics.UI.LUI.Widget as Widget
+import qualified Graphics.UI.LUI.Draw as Draw
+import Graphics.UI.LUI.Widget(Widget, WidgetFuncs(..))
+
+import Graphics.UI.HaskGame.Font(Font)
+import Graphics.UI.HaskGame.Color(Color)
+
+new :: Color -> Font -> String -> Widget model
+new textColor textSize text =
+    const $
+    WidgetFuncs
+    {
+      widgetDraw = \_ -> Draw.text textColor textSize text
+    , widgetSize = \_ -> Draw.textSize textSize text
+    , widgetGetKeymap = Nothing
+    }
diff --git a/src/Graphics/UI/LUI/Widgets/Unfocusable.hs b/src/Graphics/UI/LUI/Widgets/Unfocusable.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/UI/LUI/Widgets/Unfocusable.hs
@@ -0,0 +1,21 @@
+{-# OPTIONS_GHC -Wall -O2
+  #-}
+
+module Graphics.UI.LUI.Widgets.Unfocusable(new)
+where
+
+import qualified Graphics.UI.LUI.Widget as Widget
+import Graphics.UI.LUI.Widget(Widget, WidgetFuncs(..))
+
+noFocusDrawInfo :: Widget.DrawInfo
+noFocusDrawInfo = Widget.DrawInfo False
+
+new :: Widget model -> Widget model
+new childWidget model =
+    let childWidgetFuncs = childWidget model
+    in WidgetFuncs
+    {
+      widgetSize = \_ -> widgetSize childWidgetFuncs noFocusDrawInfo
+    , widgetDraw = \_ -> widgetDraw childWidgetFuncs noFocusDrawInfo
+    , widgetGetKeymap = Nothing
+    }
