Some more random thoughts:
I am now into "yet another" rewrite.
Here are some random design thoughts.
When an X event occurs you have to do something.
How should event processing be written:
-----------------
method 1)
The internal code in the display server did the following
To get an event to be processed I did the following:
Display ! {onEvent, Win, Type, Fun/1}
The server remembers the fun. then when event Type with argument Args
occurs in Win the server did
Fun(Args)
This is the good 'old "callback" style of programming
But what happens if Fun does not terminate or crashes - bad news
-----------------
method 2)
Same as above but I do
spawn(fun() -> Fun() end)
when an event occurs
This *almost* worked but there are two problems
a) What happens if the Fun is something like
Fun(Event) ->
Pid !! op
end.
(answer possible deadlock - so funs must be carefully written)
b) How can I synchronise execution of several callbacks
annswer - you can't you must do it yourself
-----------------
method 3)
Junk the callbacks.
I now send a
{onevent, Win, Type, Pid, Tag}
message to the server
This means:
If event Type occurs in Win then execute
Pid ! {Tag, Event}
This *cannot* fail for any reason
With this method a button widget looks like this
-module(swButton).
-export([make/8]).
-include("sw.hrl").
-import(ex11_lib, [ePolyText8/5, rpc/2, sleep/1,
xClearArea/1,
xDo/2, xFlush/1,
xVar/2]).
make(Parent, X, Y, Width, Ht, Border, Color, Str) ->
Wargs = #win{x=X, y=Y, border=Border,width=Width,ht=Ht,color=Color,
type=button, mask = ?EVENT_EXPOSURE bor
?EVENT_BUTTON_PRESS},
sw:startWidget(Parent, Wargs, fun(D,W) -> init1(D,W,Str) end).
init1(Display, Wargs, Str) ->
Win = Wargs#win.win,
Bin = ePolyText8(Win, xVar(Display, sysFontId), 10, 18, Str),
+------------------------------------------------------------+
| Display ! {onEvent, Win, expose, self(), expose1}, |
| Display ! {onEvent, Win, buttonPress, self(), click}, |
| |
+------------------------------------------------------------+
loop(Bin, Display, Wargs, fun(_) -> void end).
loop(B, Display, Wargs, Fun) ->
receive
{click, X} ->
flash(Display, Wargs),
Fun(X),
loop(B, Display, Wargs, Fun);
{expose1, _} ->
xDo(Display, B),
xFlush(Display),
loop(B, Display, Wargs, Fun);
{onClick, Fun1} ->
loop(B, Display, Wargs, Fun1);
{set, Str} ->
Win = Wargs#win.win,
xDo(Display, xClearArea(Win)),
Bin = ePolyText8(Win, xVar(Display, sysFontId), 10, 18, Str),
loop(Bin, Display, Wargs, Fun);
Any ->
io:format("Top loop got:~p~n", [Any]),
%% Now we call the generic operators
loop(B, Display, Wargs, Fun)
end.
flash(Display, Wargs) ->
S = self(),
Win=Wargs#win.win,
spawn(fun() ->
xDo(Display, xClearArea(Win)),
xFlush(Display),
sleep(200),
S ! {expose1, void}
end).
So far I can see no disadvanges with this.
I am re-writing all the widgets in this style.
Post by unknownHi,
I thought I'd share some random and unsorted thoughts I had about a widget
framework. Nothing revolutionary, but why let them go to waste? :-)
* widget users should see only generic events, not X ones. By that I mean there
should be a generic protocol (as is sketched in ex11_widget_button) for example
onClick, onDoubleClick, onEnter, onKeyPressed, onMouseMove, onMouseButtonDown,
etc. Maybe even onCreate, onDestroy?
* the event handlers should get the widget pid as an argument, so that they can
react according to the current state. For example draw_button() paints slightly
different if the button is pressed.
* the !! operator is cool, but it should be used with care. Maybe the
implementation of sysd:rpc should be refined to include timeouts and the
possibility to get the reply later (? la gen_server). The reason is that if
there are several widgets interacting (i.e. their states are interdependent)
it's very easy to get a deadlock.
For example radio buttons work as a unit.
* there is some GUI functionality that isn't local to a widget (even if it may
seem to be). For example tab order is known at parent window level, so the
parent window should get the opportunity to see keypresses before the child,
filtering tabs out (unless the child specifically wants to get them).
* there will be quite a bit of functionality common for many widgets. I think
there should be a way to reuse it without resorting to cut-and-paste techniques
;-) I won't mention inheritance (oops, I just did!) because there are other
ways - like for example providing plenty of hook points and functionality for
adding/removing custom hooks (? la Emacs).
For example implementing a tri-state button would require very little
changes to a regular button.
* It would be good if we could find an existing framework from which to map the
functionality. There's no need to make all mistakes from scratch. Most
frameworks are OO and make heavy use of inheritance, which is not a good
starting point, but see above.
That's it for now. Cheers!
/Vlad