Ömer Sinan Ağacan | 2 Mar 20:54 2014
Picon

I'm trying to design a GUI library -- a design question

Hi all,

I'm designing a ncurses based GUI library.

I'm calling smallest GUI elements "widgets".

A widget can have internal state of any type. Let's call this type
`s`. I want to be able to have something like this:

> data Widget s = Widget
>    { handleKey :: Key -> s -> s
>    , draw :: Int -> Int -> Int -> Int -> IO ()
>    , getFocus :: s -> s
>    , loseFocus :: s -> s
>    -- more methods may be added in the future
>    }

I also want to be able to have container types like this:

> data Container = Container [Widget s]

but obviously this does not work because widgets may have different
types of states.

So problem I'm trying to solve here is that I should somehow abstract
internal state used in a Widget but still be able to keep track of
that state so that I can pass it to methods in sequential invocations
of that methods.

Also, once I have a container like this, updating widget states would
(Continue reading)

Daniel Trstenjak | 2 Mar 22:38 2014
Picon

Re: I'm trying to design a GUI library -- a design question


Hi Ömer,

> In a sense I'm like trying to emulate something like closures with
> mutable closed-over data in impure languages.

One way is to keep the specific data completely out of the Widget and
use a closure to hold the data:

    data Widget = Widget
       { handleKey :: Key -> Widget
       , draw      :: Int -> Int -> Int -> Int -> IO ()
       , getFocus  :: Widget
       , loseFocus :: Widget
       }

    someWidget :: SomeWidgetData -> Widget
    someWidget dat = Widget
       { handleKey = \key -> case key of 'A' -> someWidget $ dat { ... } ; ...
       , draw      = \x y w h -> ... 
       , getFocus  = someWidget $ dat { focus = True }
       , loseFocus = someWidget $ dat { focus = False }
       }

Greetings,
Daniel
Ömer Sinan Ağacan | 3 Mar 08:50 2014
Picon

Re: I'm trying to design a GUI library -- a design question

Hi Daniel,

This won't work. Let's say there has been two keypresses and I called
handleKey two times. First time it updates the `dat` and returns it
but how can I pass that updated `dat` object to the same function in
second invocation?

---
Ömer Sinan Ağacan
http://osa1.net

2014-03-02 23:38 GMT+02:00 Daniel Trstenjak <daniel.trstenjak <at> gmail.com>:
>
> Hi Ömer,
>
>> In a sense I'm like trying to emulate something like closures with
>> mutable closed-over data in impure languages.
>
> One way is to keep the specific data completely out of the Widget and
> use a closure to hold the data:
>
>     data Widget = Widget
>        { handleKey :: Key -> Widget
>        , draw      :: Int -> Int -> Int -> Int -> IO ()
>        , getFocus  :: Widget
>        , loseFocus :: Widget
>        }
>
>     someWidget :: SomeWidgetData -> Widget
>     someWidget dat = Widget
(Continue reading)

Daniel Trstenjak | 3 Mar 09:06 2014
Picon

Re: I'm trying to design a GUI library -- a design question


Hi Ömer,

On Mon, Mar 03, 2014 at 09:50:19AM +0200, Ömer Sinan Ağacan wrote:
> This won't work. Let's say there has been two keypresses and I called
> handleKey two times. First time it updates the `dat` and returns it
> but how can I pass that updated `dat` object to the same function in
> second invocation?

If you call 'handleKey', then it returns a new Widget with a new
'handleKey' function having a closure over the modified 'dat'.

data Counter = Counter
   { increase :: Counter
   , draw     :: IO ()
   }

someCounter :: Int -> Counter
someCounter count = Counter
   { increase = someCounter $ count + 1
   , draw     = print count
   }

*Main> let c = someCounter 0
*Main> draw c
0
*Main> let c' = increase someCounter 
*Main> draw c'
1

(Continue reading)

Daniel Trstenjak | 3 Mar 09:10 2014
Picon

Re: I'm trying to design a GUI library -- a design question


> *Main> let c = someCounter 0
> *Main> draw c
> 0
> *Main> let c' = increase someCounter 
> *Main> draw c'
> 1

Sorry, the example should have been:

*Main> let c = someCounter 0
*Main> draw c
0
*Main> let c' = increase c 
*Main> draw c'
1

Greetings,
Daniel
Ömer Sinan Ağacan | 3 Mar 10:17 2014
Picon

Re: I'm trying to design a GUI library -- a design question

Ops, sorry. I misunderstand your code. Now that looks like solving my
problem of updating widgets, and maybe I can use Data.Map.Map to keep
widgets and update them when methods are called.

Now this works but that explicit state updating and passing is not
ideal for me. I know I can always hide that kind of things behind a
state monad:

> data Program = Program
>     { ...
>     , widgets :: (Map Int Widget, Int)
>     , ...
>     }
>
>
> handleKey' :: Key -> State Program ()
> handleKey' key = do
>   programState <at> Program{(widgets, focusIdx)=widgets} <- get
>   let widget = fromJust $ lookup focusIdx widgets
>       widget' = handlekey widget key
>
>   put programState{widgets=(M.insert focusIdx widget', focusIdx)}

and I can even create a typeclass like `HasWidgets` which provides
required methods for updating widget states and that would be even
more flexible:

> {-# LANGUAGE PackageImports,
>              MultiParamTypeClasses,
>              FlexibleInstance #-}
(Continue reading)

Ömer Sinan Ağacan | 3 Mar 10:37 2014
Picon

Re: I'm trying to design a GUI library -- a design question

Again, sorry, just pasted wrong code, correct version should be:

> {-# LANGUAGE PackageImports,
>              MultiParamTypeClasses,
>              FlexibleInstances,
>              FlexibleContexts #-}
>
> import "mtl" Control.Monad.State
> import "mtl" Control.Monad.Identity
> import qualified Data.Map as M
> import Data.Maybe
>
>
> type Key = Int
> type Widget = Int -- placeholder
> type FocusIdx = Int
>
>
> data Program = Program
>     { widgets :: (M.Map Int Int, Int)
>     }
>
>
> class HasWidgets s where
>     getWidgets :: s -> (M.Map Int Widget, FocusIdx)
>     updateWidgets :: (M.Map Int Widget, FocusIdx) -> s -> s
>
>
> instance HasWidgets Program where
>     getWidgets p = widgets p
(Continue reading)

Daniel Trstenjak | 3 Mar 11:14 2014
Picon

Re: I'm trying to design a GUI library -- a design question


Hi Ömer,

I don't think that you need the 'HasWidgets' class, it really doesn't give you a lot.
Something like a function 'withFocused' would be a lot more useful:

   withFocused :: (Widget -> Widget) -> State Program ()

Greetings,
Daniel
Ömer Sinan Ağacan | 3 Mar 11:22 2014
Picon

Re: I'm trying to design a GUI library -- a design question

I think HasWidgets is useful because when focus is moved to another
widget, I somehow need to find the widget that just got the focus in
collection of widgets.. So I need to somehow search in widget map.

I need a typeclass because I may have different types of widget
containers and I want to be able to update them uniformly.

---
Ömer Sinan Ağacan
http://osa1.net

2014-03-03 12:14 GMT+02:00 Daniel Trstenjak <daniel.trstenjak <at> gmail.com>:
>
> Hi Ömer,
>
> I don't think that you need the 'HasWidgets' class, it really doesn't give you a lot.
> Something like a function 'withFocused' would be a lot more useful:
>
>    withFocused :: (Widget -> Widget) -> State Program ()
>
>
> Greetings,
> Daniel
> _______________________________________________
> Haskell-Cafe mailing list
> Haskell-Cafe <at> haskell.org
> http://www.haskell.org/mailman/listinfo/haskell-cafe
_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe <at> haskell.org
(Continue reading)

Niklas Haas | 3 Mar 13:56 2014
Picon

Re: I'm trying to design a GUI library -- a design question

	<CAMQQO3mLWxjR_cydOdDZR_i6=Mgi3asTWsNDY8tzQcRc95uYtA <at> mail.gmail.com>
	<20140302213802.GA17085 <at> machine>
	<CAMQQO3mRBzzjCCboLPcFKXcQfSuS39BV99zL8+ox=v-Mo+Gdiw <at> mail.gmail.com>
	<20140303080645.GB2069 <at> machine> <20140303081048.GC2069 <at> machine>
	<CAMQQO3kgL+stx9OL0JCn6CmvS4h3Us1-vm9ZDjvMRiuUEQZGGw <at> mail.gmail.com>
	<CAMQQO3nAZBV5ZDoVuwKWO+aRP94702CjpWG4DZ8x6+LuwPMPPg <at> mail.gmail.com>
	<20140303101441.GA4636 <at> machine>

On Mon, 3 Mar 2014 11:14:41 +0100, Daniel Trstenjak <daniel.trstenjak <at> gmail.com> wrote:
> 
> Hi Ömer,
> 
> I don't think that you need the 'HasWidgets' class, it really doesn't give you a lot.
> Something like a function 'withFocused' would be a lot more useful:
> 
>    withFocused :: (Widget -> Widget) -> State Program ()
> 
> 
> Greetings,
> Daniel
> _______________________________________________
> Haskell-Cafe mailing list
> Haskell-Cafe <at> haskell.org
> http://www.haskell.org/mailman/listinfo/haskell-cafe

This looks like a use case for a lens to me.

> focused :: Lens' Program Widget
> over focused :: (Widget -> Widget) -> Program -> Program

(Continue reading)

Stephen Tetley | 3 Mar 13:28 2014
Picon

Re: I'm trying to design a GUI library -- a design question

Hi Ömer

A bit tangential, but you might find looking at Wolfram Kahl and his
co-author's "Editor combinators" helpful:

http://www.cas.mcmaster.ca/~kahl/Publications/TR/2000-01/

As you are using records to encapsulate functional "objects", your
code strongly reminded me of editor combinators.

Best wishes

Stephen

On 2 March 2014 19:54, Ömer Sinan Ağacan <omeragacan <at> gmail.com> wrote:
> Hi all,
>
> I'm designing a ncurses based GUI library.
>
> I'm calling smallest GUI elements "widgets".
>
> A widget can have internal state of any type. Let's call this type
> `s`. I want to be able to have something like this:
>
>> data Widget s = Widget
>>    { handleKey :: Key -> s -> s
>>    , draw :: Int -> Int -> Int -> Int -> IO ()
>>    , getFocus :: s -> s
>>    , loseFocus :: s -> s
>>    -- more methods may be added in the future
(Continue reading)


Gmane