123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470 |
- MODULE Gui;
- IMPORT G := Graph, Strings, Out;
- CONST
- (* Widget.state set members *)
- hover* = 0;
- down* = 1;
- focus* = 2;
- active* = 3;
- TYPE
- Caption* = POINTER TO CaptionDesc;
- CaptionDesc* = RECORD
- s*: ARRAY 100 OF CHAR
- END;
- DrawHandler* = PROCEDURE (W: Widget; x, y: INTEGER);
- MouseMoveHandler* = PROCEDURE (W: Widget; x, y, btn: INTEGER);
- MouseDownHandler* = PROCEDURE (W: Widget; x, y, btn: INTEGER);
- MouseUpHandler* = PROCEDURE (W: Widget; x, y, btn: INTEGER);
- MouseEnterHandler* = PROCEDURE (W: Widget);
- MouseLeaveHandler* = PROCEDURE (W: Widget);
- ClickHandler* = PROCEDURE (W: Widget);
- Message* = POINTER TO MsgDesc;
- MsgDesc* = RECORD END;
- Handler* = PROCEDURE (W: Widget; VAR msg: Message);
- Widget* = POINTER TO WidgetDesc;
- WidgetDesc* = RECORD
- x*, y*, w*, h*: INTEGER;
- tag*: INTEGER;
- state*: SET;
- visible*: BOOLEAN;
- enabled*: BOOLEAN;
- default*: BOOLEAN;
- tabStop*: BOOLEAN;
- tabOrder*: INTEGER;
- body*: Widget;
- text*: Caption;
- bmp*: G.Bitmap;
- parent*: Widget;
- prev*, next*: Widget;
- (* Event Handlers *)
- onMouseMove*: MouseMoveHandler;
- onMouseDown*: MouseDownHandler;
- onMouseUp*: MouseUpHandler;
- onMouseEnter*: MouseEnterHandler;
- onMouseLeave*: MouseLeaveHandler;
- onClick*: ClickHandler;
- (* Message Handler *)
- handle*: Handler;
- draw*: DrawHandler
- END;
- Window* = POINTER TO WindowDesc;
- WindowDesc* = RECORD(WidgetDesc)
- win*: G.Window;
- curMouseDownWidget*: Widget; (* Widget under mouse down event is saved here *)
- curHoverWidget*: Widget (* Widget currently being pointed on by mouse *)
- END;
- VAR
- Done*: BOOLEAN;
- exitRunLoop: BOOLEAN; (* See procedure Run *)
- font: G.Font;
- newWindowSettings: SET;
- globalWin: Window; (* !FIXME *)
- (* Widget *)
- PROCEDURE SetOnMouseMove*(W: Widget; handler: MouseMoveHandler);
- BEGIN
- W.onMouseMove := handler
- END SetOnMouseMove;
- PROCEDURE SetOnMouseDown*(W: Widget; handler: MouseDownHandler);
- BEGIN
- W.onMouseDown := handler
- END SetOnMouseDown;
- PROCEDURE SetOnMouseUp*(W: Widget; handler: MouseUpHandler);
- BEGIN
- W.onMouseUp := handler
- END SetOnMouseUp;
- PROCEDURE SetOnMouseEnter*(W: Widget; handler: MouseEnterHandler);
- BEGIN
- W.onMouseEnter := handler
- END SetOnMouseEnter;
- PROCEDURE SetOnMouseLeave*(W: Widget; handler: MouseLeaveHandler);
- BEGIN
- W.onMouseLeave := handler
- END SetOnMouseLeave;
- PROCEDURE SetOnClick*(W: Widget; handler: ClickHandler);
- BEGIN
- W.onClick := handler;
- END SetOnClick;
- PROCEDURE InitWidget*(w: Widget);
- BEGIN
- w.x := 0; w.y := 0; w.w := 24; w.h := 24;
- w.tag := 0;
- w.state := {};
- w.visible := TRUE;
- w.enabled := TRUE;
- w.default := FALSE;
- w.tabStop := TRUE;
- w.tabOrder := 0;
- w.draw := NIL;
- w.onMouseMove := NIL;
- w.onMouseDown := NIL;
- w.onMouseUp := NIL;
- w.onMouseEnter := NIL;
- w.onMouseLeave := NIL;
- w.onClick := NIL;
- (* Замок *)
- NEW(w.body); w.body.prev := w.body; w.body.next := w.body
- END InitWidget;
- PROCEDURE SetText*(w: Widget; s: ARRAY OF CHAR);
- BEGIN
- IF w.text = NIL THEN NEW(w.text) END;
- Strings.Copy(s, w.text.s)
- END SetText;
- (** Returns parent (or w) with .parent=NIL *)
- PROCEDURE GetTopParent*(W: Widget): Widget;
- BEGIN
- WHILE W.parent # NIL DO W := W.parent END
- RETURN W END GetTopParent;
- PROCEDURE UnsetAllDefaultsInside*(W: Widget);
- VAR p: Widget;
- BEGIN
- W.default := FALSE;
- p := W.body.next;
- WHILE p # W.body DO
- UnsetAllDefaultsInside(p);
- p := p.next
- END
- END UnsetAllDefaultsInside;
- PROCEDURE UnsetAllDefaultsOutside*(W: Widget);
- BEGIN
- UnsetAllDefaultsInside(GetTopParent(W))
- END UnsetAllDefaultsOutside;
- PROCEDURE SetDefault*(W: Widget; value: BOOLEAN);
- BEGIN
- IF ~W.default & value THEN UnsetAllDefaultsOutside(W) END;
- W.default := value;
- INCL(W.state, active)
- END SetDefault;
- PROCEDURE SetEnabled*(w: Widget; value: BOOLEAN);
- BEGIN
- w.enabled := value
- END SetEnabled;
- PROCEDURE SetVisible*(w: Widget; value: BOOLEAN);
- BEGIN
- w.visible := value
- END SetVisible;
- PROCEDURE Place*(where, what: Widget; x, y: INTEGER);
- BEGIN
- what.x := x; what.y := y;
- (* Добавление в кольцо с замком *)
- what.prev := where.body.prev;
- what.next := where.body;
- where.body.prev.next := what;
- where.body.prev := what;
- what.parent := where;
- IF what.default THEN
- UnsetAllDefaultsOutside(what);
- what.default := TRUE
- END
- END Place;
- (* Window *)
- PROCEDURE NewWindowSettings*(settings: SET);
- BEGIN
- newWindowSettings := settings
- END NewWindowSettings;
- PROCEDURE DrawWidget(W: Widget; x, y: INTEGER);
- BEGIN
- W.draw(W, x + W.x, y + W.y)
- END DrawWidget;
- PROCEDURE DrawBody*(W: Widget; x, y: INTEGER);
- VAR p: Widget;
- BEGIN
- p := W.body.next;
- WHILE p # W.body DO
- DrawWidget(p, x, y);
- p := p.next
- END
- END DrawBody;
- PROCEDURE DrawWindow*(W: Widget; x, y: INTEGER);
- VAR c: G.Color;
- w, h: INTEGER;
- BEGIN
- G.MakeCol(c, 212, 208, 200);
- G.ClearToColor(c);
- DrawBody(W, x, y)
- END DrawWindow;
- PROCEDURE InitWindow*(win: Window; w, h: INTEGER);
- BEGIN
- InitWidget(win);
- win.win := G.NewWindow(-1, -1, w, h,
- 'Window', newWindowSettings);
- win.x := 0; win.y := 0;
- win.w := win.win.w; win.h := win.win.h;
- win.curMouseDownWidget := NIL;
- win.curHoverWidget := win;
- win.draw := DrawWindow
- END InitWindow;
- PROCEDURE NewWindow*(w, h: INTEGER): Window;
- VAR win: Window;
- BEGIN
- NEW(win);
- InitWindow(win, w, h);
- globalWin := win
- RETURN win END NewWindow;
- (* Draw *)
- PROCEDURE DrawAll;
- BEGIN
- globalWin.draw(globalWin, 0, 0);
- G.Flip;
- G.Delay(1)
- END DrawAll;
- (* Fonts *)
- PROCEDURE GetFont*(W: Widget): G.Font;
- RETURN font END GetFont;
- (* General *)
- PROCEDURE TriggerOnMouseMove*(W: Widget; x, y, btn: INTEGER);
- BEGIN
- IF (W # NIL) & (W.onMouseMove # NIL) THEN
- W.onMouseMove(W, x, y, btn)
- END
- END TriggerOnMouseMove;
- PROCEDURE TriggerOnMouseDown*(W: Widget; x, y, btn: INTEGER);
- BEGIN
- IF (W # NIL) & (W.onMouseDown # NIL) THEN
- W.onMouseDown(W, x, y, btn)
- END
- END TriggerOnMouseDown;
- PROCEDURE TriggerOnMouseUp*(W: Widget; x, y, btn: INTEGER);
- BEGIN
- IF (W # NIL) & (W.onMouseUp # NIL) THEN
- W.onMouseUp(W, x, y, btn)
- END
- END TriggerOnMouseUp;
- PROCEDURE TriggerOnMouseEnter*(W: Widget);
- BEGIN
- IF (W # NIL) & (W.onMouseEnter # NIL) THEN W.onMouseEnter(W) END
- END TriggerOnMouseEnter;
- PROCEDURE TriggerOnMouseLeave*(W: Widget);
- BEGIN
- IF (W # NIL) & (W.onMouseLeave # NIL) THEN W.onMouseLeave(W) END
- END TriggerOnMouseLeave;
- PROCEDURE TriggerOnClick*(W: Widget);
- BEGIN
- IF (W # NIL) & (W.onClick # NIL) THEN
- W.onClick(W)
- END
- END TriggerOnClick;
- PROCEDURE FindWidgetUnderMouse*(W: Widget; VAR x, y: INTEGER): Widget;
- VAR p: Widget;
- BEGIN
- IF W = NIL THEN p := NIL
- ELSIF W.body # NIL THEN
- p := W.body.prev;
- WHILE (p # W.body) &
- ~((p.x <= x) & (x < p.x + p.w) &
- (p.y <= y) & (y < p.y + p.h))
- DO p := p.prev
- END;
- IF p = W.body THEN p := W
- ELSE DEC(x, p.x); DEC(y, p.y);
- p := FindWidgetUnderMouse(p, x, y)
- END
- ELSE p := W
- END
- RETURN p END FindWidgetUnderMouse;
- (** Input: (x; y) relative to window.
- Output: (x; y) relative to widget. *)
- PROCEDURE WindowToWidgetXY*(W: Widget; VAR x, y: INTEGER);
- BEGIN
- WHILE (W # NIL) & ~(W IS Window) DO
- DEC(x, W.x); DEC(y, W.y);
- W := W.parent
- END
- END WindowToWidgetXY;
- (** Returns TRUE if first widget is parent of second. *)
- PROCEDURE IsParent*(a, b: Widget): BOOLEAN;
- BEGIN
- WHILE (b # NIL) & (a # b) DO b := b.parent END
- RETURN (a # NIL) & (a = b) END IsParent;
- PROCEDURE MouseLeaveCascade(from, to: Widget);
- VAR p: Widget;
- BEGIN
- p := from;
- WHILE (p # NIL) & (p # to) & ~IsParent(p, to) DO
- EXCL(p.state, hover);
- TriggerOnMouseLeave(p);
- p := p.parent
- END
- END MouseLeaveCascade;
- PROCEDURE MouseEnterCascade(from, to: Widget);
- VAR p: Widget;
- m: ARRAY 100 OF Widget;
- len: INTEGER;
- BEGIN
- len := 0;
- p := to;
- WHILE (p # NIL) & (p # from) & ~IsParent(p, from) DO
- INCL(p.state, hover);
- IF len < LEN(m) THEN
- m[len] := p;
- INC(len)
- END;
- p := p.parent
- END;
- WHILE len > 0 DO
- DEC(len);
- TriggerOnMouseEnter(m[len])
- END
- END MouseEnterCascade;
- PROCEDURE HandleMouseMoveEvent*(e: G.Event);
- VAR W: Widget;
- x, y: INTEGER;
- BEGIN
- x := e.x; y := e.y;
- IF globalWin.curMouseDownWidget # NIL THEN
- W := globalWin.curMouseDownWidget;
- WindowToWidgetXY(W, x, y);
- IF (x >= 0) & (y >= 0) & (W.w > x) & (W.h > y) & (1 IN e.buttons) THEN
- INCL(W.state, down)
- ELSE EXCL(W.state, down)
- END
- ELSE
- W := FindWidgetUnderMouse(globalWin, x, y);
- IF globalWin.curHoverWidget # W THEN
- MouseLeaveCascade(globalWin.curHoverWidget, W);
- MouseEnterCascade(globalWin.curHoverWidget, W);
- globalWin.curHoverWidget := W
- END
- END;
- IF W # NIL THEN
- TriggerOnMouseMove(W, x, y, e.button)
- END
- END HandleMouseMoveEvent;
- PROCEDURE HandleMouseDownEvent*(e: G.Event);
- VAR W: Widget;
- x, y: INTEGER;
- BEGIN
- x := e.x; y := e.y;
- W := FindWidgetUnderMouse(globalWin, x, y);
- IF W # NIL THEN
- IF e.button = 1 THEN
- INCL(W.state, down) (* Mark button as being left-mouse-button-down *)
- END;
- globalWin.curMouseDownWidget := W; (* Save for future mouse up event *)
- TriggerOnMouseDown(W, x, y, e.button)
- END
- END HandleMouseDownEvent;
- PROCEDURE HandleMouseUpEvent*(e: G.Event);
- VAR W: Widget;
- x, y: INTEGER;
- BEGIN
- x := e.x; y := e.y;
- W := globalWin.curMouseDownWidget;
- globalWin.curMouseDownWidget := NIL;
- IF W # NIL THEN
- WindowToWidgetXY(W, x, y);
- EXCL(W.state, down);
- TriggerOnMouseUp(W, x, y, e.button);
- IF (x >= 0) & (y >= 0) & (W.w > x) & (W.h > y) & (e.button = 1) THEN
- TriggerOnClick(W)
- END
- END
- END HandleMouseUpEvent;
- PROCEDURE HandleEvent(e: G.Event);
- BEGIN
- IF e.type = G.mouseMove THEN
- HandleMouseMoveEvent(e)
- ELSIF e.type = G.mouseDown THEN
- HandleMouseDownEvent(e)
- ELSIF e.type = G.mouseUp THEN
- HandleMouseUpEvent(e)
- ELSIF e.type = G.keyDown THEN
- IF e.key = G.kEsc THEN exitRunLoop := TRUE END;
- ELSIF e.type = G.quit THEN
- exitRunLoop := TRUE
- END
- END HandleEvent;
- PROCEDURE Run*;
- VAR e: G.Event;
- BEGIN
- exitRunLoop := FALSE;
- REPEAT
- WHILE G.HasEvents() DO
- G.WaitEvent(e);
- HandleEvent(e)
- END;
- DrawAll
- UNTIL exitRunLoop
- END Run;
- PROCEDURE Init*;
- BEGIN
- G.Settings(0, 0, {G.manual});
- G.Init;
- IF G.Done THEN
- font := G.LoadFont('../Data/Fonts/Main');
- IF font = NIL THEN
- Out.String('Gui: Could not load font.'); Out.Ln;
- Done := FALSE
- END
- ELSE Done := FALSE
- END
- END Init;
- PROCEDURE Close*;
- BEGIN
- G.Close
- END Close;
- BEGIN
- Done := TRUE
- END Gui.
|