gooey (empty) → 0.0.0.0
raw patch · 4 files changed
+281/−0 lines, 4 filesdep +basedep +hashabledep +renderablesetup-changed
Dependencies added: base, hashable, renderable, transformers, varying
Files
- LICENSE +20/−0
- Setup.hs +2/−0
- gooey.cabal +75/−0
- src/Control/GUI.hs +184/−0
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2015 Schell Scivally++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ gooey.cabal view
@@ -0,0 +1,75 @@+-- Initial gooey.cabal generated by cabal init. For further documentation,+-- see http://haskell.org/cabal/users-guide/++-- The name of the package.+name: gooey++-- The package version. See the Haskell package versioning policy (PVP)+-- for standards guiding when and how versions should be incremented.+-- http://www.haskell.org/haskellwiki/Package_versioning_policy+-- PVP summary: +-+------- breaking API changes+-- | | +----- non-breaking API additions+-- | | | +--- code changes with no API change+version: 0.0.0.0++-- A short (one-line) description of the package.+synopsis: Graphical user interfaces that are renderable,+ change over time and eventually produce a value.++-- A longer description of the package.+description: Gooey provides a monadic interface on top of automaton+ based FRP. It is targeted towards controlling renderable+ interfaces that eventually produce values.++-- The license under which the package is released.+license: MIT++-- The file containing the license text.+license-file: LICENSE++-- The package author(s).+author: Schell Scivally++-- An email address to which users can send suggestions, bug reports, and+-- patches.+maintainer: efsubenovex@gmail.com++-- A copyright notice.+-- copyright:++category: Control++build-type: Simple++-- Extra files to be distributed with the package, such as examples or a+-- README.+-- extra-source-files:++-- Constraint on the version of Cabal needed to build this package.+cabal-version: >=1.10+++library+ ghc-options: -Wall+ -- Modules exported by the library.+ exposed-modules: Control.GUI++ -- Modules included in this library but not exported.+ -- other-modules:++ -- LANGUAGE extensions used by modules in this package.+ -- other-extensions:++ -- Other library packages from which modules are imported.+ build-depends: base >=4.8 && <4.9,+ varying >= 0.1.4 && < 0.2,+ renderable >= 0.0.0.2,+ transformers >= 0.4 && < 0.5,+ hashable >= 1.2 && < 1.3++ -- Directories containing source files.+ hs-source-dirs: src++ -- Base language which the package is written in.+ default-language: Haskell2010+
+ src/Control/GUI.hs view
@@ -0,0 +1,184 @@+-- |+-- Module: Control.GUI+-- Copyright: (c) 2015 Schell Scivally+-- License: MIT+-- Maintainer: Schell Scivally <efsubenovex@gmail.com>+--+-- Graphical user interfaces that are renderable, change over time and+-- eventually produce a value.+--+-- GUIs are comprised of event streams and renderable datatypes that change+-- over time. A GUI is a monadic layer on top of automaton varying values+-- provided by the 'varying' library.++{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE FlexibleContexts #-}+module Control.GUI (+ -- * Definition+ UX(..),+ GUI(..),+ -- * Creation+ -- $creation+ gui,+ -- * Transformation+ -- $transformation+ transformGUI,+ -- * Combination+ -- $combination+ combineGUI+) where++import Control.Varying+import Control.Monad.IO.Class+import Data.Renderable+import Data.Hashable+import Data.Monoid++--------------------------------------------------------------------------------+-- Defining a GUI+--------------------------------------------------------------------------------+-- | A discrete step in a "user experience". This is simply a type that+-- discretely describes an eventual value on the right and a renderable datatype+-- on the left. It is assumed that the left value is a datatype+-- that represents a user inteface and that the user is interacting with it+-- to eventually produce the datatype on the right.+data UX a b = UX a (Event b)++-- | A UX is a functor by applying a function to the contained event's value.+instance Functor (UX a) where+ fmap f (UX a b) = UX a $ (fmap f b)++-- | A UX is an applicative if its left datatype is a monoid. It replies to+-- 'pure' with an empty left value while the right value is the argument+-- wrapped in an event. It means "the argument happens instantly with no+-- user interface".+instance Monoid a => Applicative (UX a) where+ pure a = UX mempty $ Event a+ (UX uia f) <*> (UX uib b) = UX (uia <> uib) (f <*> b)++-- | A UX is renderable if its left value is also renderable. It inherits all+-- Renderable type variables from its left value and simply renders that+-- value.+instance Renderable a => Renderable (UX a b) where+ type RenderMonad (UX a b) = RenderMonad a+ type RenderRsrc (UX a b) = RenderRsrc a+ type RenderTfrm (UX a b) = RenderTfrm a+ nameOf (UX ui _) = nameOf ui+ cache rz rs (UX ui _) = cache rz rs ui+ composite (UX ui _) = composite ui++-- | A GUI is a UX that varies over some domain. What this means is that a+-- graphical user interface is essentially a user experience that eventually+-- produces a value. 'm' is the underlying monad. 'i' is the type of the user+-- input. 'a' is the renderable type - the interface itself. 'b' is the+-- eventual produced value.+newtype GUI m i a b = GUI { runGUI :: Var m i (UX a b) }++-- | A GUI can be a monoid if its UX\'s left and right types are monoids.+-- The identity is a GUI that has no user interface and immediately+-- produces an event who\'s value is the identity of its UX's right type.+-- The associative operation is to combine the two GUIs with 'combineGUI'.+instance (Monad m, Monoid a, Monoid b) => Monoid (GUI m i a b) where+ mempty = pure mempty+ mappend g h = combineGUI g h mappend++-- | A GUI is a functor by applying a function to the eventual produced+-- value.+instance Monad m => Functor (GUI m i a) where+ fmap f (GUI v) = GUI $ fmap (fmap f) v++-- | A GUI is applicative if its UX\'s left value is a monoid. It responds+-- to 'pure' by returning a GUI that has no user interface and immediately+-- produces the argument. It responds to '<*>' by applying the left+-- argument to the right. Each side\'s left UX value will be 'mappend' \'d.+instance (Monad m, Monoid a) => Applicative (GUI m i a) where+ pure = GUI . pure . pure+ (GUI vf) <*> (GUI va) = GUI $ ((<*>) <$> vf) <*> va++-- | A GUI is a monad if its UX's left value is a monoid. It responds to+-- '>>=' by returning a new GUI that runs until it produces a value, then+-- that value is used to create yet another GUI.+instance (Monad m, Monoid a) => Monad (GUI m i a) where+ (GUI v) >>= f = GUI $ Var $ \i -> do+ (UX a e, v') <- runVar v i+ case e of+ NoEvent -> return (UX a NoEvent, runGUI $ GUI v' >>= f)+ Event b -> runVar (runGUI $ f b) i++-- | A GUI can perform IO if its underlying monad can perform IO.+instance (MonadIO m, Monoid a) => MonadIO (GUI m i a) where+ liftIO f = GUI $ Var $ \_ -> do+ a <- liftIO f+ return (UX mempty (Event a), runGUI $ pure a)+--------------------------------------------------------------------------------+-- $creation+-- In order to create a GUI you must first have a datatype that is+-- 'Renderable'. Then you must create a 'varying' value of that datatype.+-- Also needed is an event stream that eventually ends the user's interaction.+-- The idea is that your interface varies over time and/or user input but+-- eventually produces a result value that can be used in a monadic+-- sequence.+--------------------------------------------------------------------------------+-- | Creates a new GUI displaying an interface that eventually produces a value.+-- The type used to represent the user interface must have a 'Decomposable'+-- instance, that way the resulting GUI\'s discrete values can be rendered.+gui :: (Monad m, Decomposable a m r t)+ => Var m i a+ -- ^ The stream of a changing user interface.+ -> Var m i (Event b)+ -- ^ The event stream that concludes a user\'s interaction. When this+ -- stream produces an event the interaction will end and the merging+ -- function will be used to create the GUI\'s return type.+ -> (a -> b -> c)+ -- ^ The merging function that combines the interface's final value with the+ -- value produced by the event stream.+ -> GUI m i [Element m r t] c+gui v ve f = GUI $ Var $ \i -> do+ (a, v') <- runVar v i+ (e, ve') <- runVar ve i+ let ui = decompose a+ case e of+ NoEvent -> return (UX ui NoEvent, runGUI $ gui v' ve' f)+ Event b -> return (UX ui (Event $ f a b), pure $ UX [] $ Event $ f a b)+--------------------------------------------------------------------------------+-- $transformation+-- Simply put - here we are applying some kind of transformation to your+-- renderable interface. This most likely a standard two or three dimensional+-- affine transformation. Since the transformation also changes over the+-- same domain it\'s possible to tween GUIs.+--------------------------------------------------------------------------------+-- | Transforms a GUI.+transformGUI :: (Monad m, Monad (RenderMonad t), Show t, Show (RenderTfrm t),+ Hashable t, Hashable (RenderTfrm t), Monoid (RenderTfrm t),+ Renderable t)+ => Var m i (RenderTfrm t)+ -- ^ The stream of a changing transformation.+ -> GUI m i t b+ -- ^ The GUI to transform.+ -> GUI m i [Element (RenderMonad t) (RenderRsrc t) (RenderTfrm t)] b+transformGUI vt g = GUI $ Var $ \i -> do+ (UX ui e, v) <- runVar (runGUI g) i+ (t, vt') <- runVar vt i+ return (UX [Element (t,ui)] e, runGUI $ transformGUI vt' $ GUI v)+--------------------------------------------------------------------------------+-- $combination+-- Combining two GUIs creates a new GUI.+--------------------------------------------------------------------------------+-- | Combines two GUIs. The resulting GUI will not produce a value until+-- both component GUIs have produced a value. At that moment a merging function+-- is used to combine the two values into the resulting GUI\'s return type.+-- The component GUIs\' graphical representations (the left UX values) are+-- 'mappend'\d together.+combineGUI :: (Monad m, Monoid u)+ => GUI m i u a+ -- ^ The first GUI.+ -> GUI m i u b+ -- ^ The second GUI.+ -> (a -> b -> c)+ -- ^ The merging function.+ -> GUI m i u c+combineGUI (GUI va) (GUI vb) f = GUI $ Var $ \i -> do+ (UX a ea, va') <- runVar va i+ (UX b eb, vb') <- runVar vb i+ return (UX (a <> b) (f <$> ea <*> eb),+ runGUI $ combineGUI (GUI va') (GUI vb') f)