Gui.Mod 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. MODULE Gui;
  2. IMPORT G := Graph, Strings, Out;
  3. CONST
  4. (* Widget.state set members *)
  5. hover* = 0;
  6. down* = 1;
  7. focus* = 2;
  8. active* = 3;
  9. TYPE
  10. Caption* = POINTER TO CaptionDesc;
  11. CaptionDesc* = RECORD
  12. s*: ARRAY 100 OF CHAR
  13. END;
  14. DrawHandler* = PROCEDURE (W: Widget; x, y: INTEGER);
  15. MouseMoveHandler* = PROCEDURE (W: Widget; x, y, btn: INTEGER);
  16. MouseDownHandler* = PROCEDURE (W: Widget; x, y, btn: INTEGER);
  17. MouseUpHandler* = PROCEDURE (W: Widget; x, y, btn: INTEGER);
  18. MouseEnterHandler* = PROCEDURE (W: Widget);
  19. MouseLeaveHandler* = PROCEDURE (W: Widget);
  20. ClickHandler* = PROCEDURE (W: Widget);
  21. Message* = POINTER TO MsgDesc;
  22. MsgDesc* = RECORD END;
  23. Handler* = PROCEDURE (W: Widget; VAR msg: Message);
  24. Widget* = POINTER TO WidgetDesc;
  25. WidgetDesc* = RECORD
  26. x*, y*, w*, h*: INTEGER;
  27. tag*: INTEGER;
  28. state*: SET;
  29. visible*: BOOLEAN;
  30. enabled*: BOOLEAN;
  31. default*: BOOLEAN;
  32. tabStop*: BOOLEAN;
  33. tabOrder*: INTEGER;
  34. body*: Widget;
  35. text*: Caption;
  36. bmp*: G.Bitmap;
  37. parent*: Widget;
  38. prev*, next*: Widget;
  39. (* Event Handlers *)
  40. onMouseMove*: MouseMoveHandler;
  41. onMouseDown*: MouseDownHandler;
  42. onMouseUp*: MouseUpHandler;
  43. onMouseEnter*: MouseEnterHandler;
  44. onMouseLeave*: MouseLeaveHandler;
  45. onClick*: ClickHandler;
  46. (* Message Handler *)
  47. handle*: Handler;
  48. draw*: DrawHandler
  49. END;
  50. Window* = POINTER TO WindowDesc;
  51. WindowDesc* = RECORD(WidgetDesc)
  52. win*: G.Window;
  53. curMouseDownWidget*: Widget; (* Widget under mouse down event is saved here *)
  54. curHoverWidget*: Widget (* Widget currently being pointed on by mouse *)
  55. END;
  56. VAR
  57. Done*: BOOLEAN;
  58. exitRunLoop: BOOLEAN; (* See procedure Run *)
  59. font: G.Font;
  60. newWindowSettings: SET;
  61. globalWin: Window; (* !FIXME *)
  62. (* Widget *)
  63. PROCEDURE SetOnMouseMove*(W: Widget; handler: MouseMoveHandler);
  64. BEGIN
  65. W.onMouseMove := handler
  66. END SetOnMouseMove;
  67. PROCEDURE SetOnMouseDown*(W: Widget; handler: MouseDownHandler);
  68. BEGIN
  69. W.onMouseDown := handler
  70. END SetOnMouseDown;
  71. PROCEDURE SetOnMouseUp*(W: Widget; handler: MouseUpHandler);
  72. BEGIN
  73. W.onMouseUp := handler
  74. END SetOnMouseUp;
  75. PROCEDURE SetOnMouseEnter*(W: Widget; handler: MouseEnterHandler);
  76. BEGIN
  77. W.onMouseEnter := handler
  78. END SetOnMouseEnter;
  79. PROCEDURE SetOnMouseLeave*(W: Widget; handler: MouseLeaveHandler);
  80. BEGIN
  81. W.onMouseLeave := handler
  82. END SetOnMouseLeave;
  83. PROCEDURE SetOnClick*(W: Widget; handler: ClickHandler);
  84. BEGIN
  85. W.onClick := handler;
  86. END SetOnClick;
  87. PROCEDURE InitWidget*(w: Widget);
  88. BEGIN
  89. w.x := 0; w.y := 0; w.w := 24; w.h := 24;
  90. w.tag := 0;
  91. w.state := {};
  92. w.visible := TRUE;
  93. w.enabled := TRUE;
  94. w.default := FALSE;
  95. w.tabStop := TRUE;
  96. w.tabOrder := 0;
  97. w.draw := NIL;
  98. w.onMouseMove := NIL;
  99. w.onMouseDown := NIL;
  100. w.onMouseUp := NIL;
  101. w.onMouseEnter := NIL;
  102. w.onMouseLeave := NIL;
  103. w.onClick := NIL;
  104. (* Замок *)
  105. NEW(w.body); w.body.prev := w.body; w.body.next := w.body
  106. END InitWidget;
  107. PROCEDURE SetText*(w: Widget; s: ARRAY OF CHAR);
  108. BEGIN
  109. IF w.text = NIL THEN NEW(w.text) END;
  110. Strings.Copy(s, w.text.s)
  111. END SetText;
  112. (** Returns parent (or w) with .parent=NIL *)
  113. PROCEDURE GetTopParent*(W: Widget): Widget;
  114. BEGIN
  115. WHILE W.parent # NIL DO W := W.parent END
  116. RETURN W END GetTopParent;
  117. PROCEDURE UnsetAllDefaultsInside*(W: Widget);
  118. VAR p: Widget;
  119. BEGIN
  120. W.default := FALSE;
  121. p := W.body.next;
  122. WHILE p # W.body DO
  123. UnsetAllDefaultsInside(p);
  124. p := p.next
  125. END
  126. END UnsetAllDefaultsInside;
  127. PROCEDURE UnsetAllDefaultsOutside*(W: Widget);
  128. BEGIN
  129. UnsetAllDefaultsInside(GetTopParent(W))
  130. END UnsetAllDefaultsOutside;
  131. PROCEDURE SetDefault*(W: Widget; value: BOOLEAN);
  132. BEGIN
  133. IF ~W.default & value THEN UnsetAllDefaultsOutside(W) END;
  134. W.default := value;
  135. INCL(W.state, active)
  136. END SetDefault;
  137. PROCEDURE SetEnabled*(w: Widget; value: BOOLEAN);
  138. BEGIN
  139. w.enabled := value
  140. END SetEnabled;
  141. PROCEDURE SetVisible*(w: Widget; value: BOOLEAN);
  142. BEGIN
  143. w.visible := value
  144. END SetVisible;
  145. PROCEDURE Place*(where, what: Widget; x, y: INTEGER);
  146. BEGIN
  147. what.x := x; what.y := y;
  148. (* Добавление в кольцо с замком *)
  149. what.prev := where.body.prev;
  150. what.next := where.body;
  151. where.body.prev.next := what;
  152. where.body.prev := what;
  153. what.parent := where;
  154. IF what.default THEN
  155. UnsetAllDefaultsOutside(what);
  156. what.default := TRUE
  157. END
  158. END Place;
  159. (* Window *)
  160. PROCEDURE NewWindowSettings*(settings: SET);
  161. BEGIN
  162. newWindowSettings := settings
  163. END NewWindowSettings;
  164. PROCEDURE DrawWidget(W: Widget; x, y: INTEGER);
  165. BEGIN
  166. W.draw(W, x + W.x, y + W.y)
  167. END DrawWidget;
  168. PROCEDURE DrawBody*(W: Widget; x, y: INTEGER);
  169. VAR p: Widget;
  170. BEGIN
  171. p := W.body.next;
  172. WHILE p # W.body DO
  173. DrawWidget(p, x, y);
  174. p := p.next
  175. END
  176. END DrawBody;
  177. PROCEDURE DrawWindow*(W: Widget; x, y: INTEGER);
  178. VAR c: G.Color;
  179. w, h: INTEGER;
  180. BEGIN
  181. G.MakeCol(c, 212, 208, 200);
  182. G.ClearToColor(c);
  183. DrawBody(W, x, y)
  184. END DrawWindow;
  185. PROCEDURE InitWindow*(win: Window; w, h: INTEGER);
  186. BEGIN
  187. InitWidget(win);
  188. win.win := G.NewWindow(-1, -1, w, h,
  189. 'Window', newWindowSettings);
  190. win.x := 0; win.y := 0;
  191. win.w := win.win.w; win.h := win.win.h;
  192. win.curMouseDownWidget := NIL;
  193. win.curHoverWidget := win;
  194. win.draw := DrawWindow
  195. END InitWindow;
  196. PROCEDURE NewWindow*(w, h: INTEGER): Window;
  197. VAR win: Window;
  198. BEGIN
  199. NEW(win);
  200. InitWindow(win, w, h);
  201. globalWin := win
  202. RETURN win END NewWindow;
  203. (* Draw *)
  204. PROCEDURE DrawAll;
  205. BEGIN
  206. globalWin.draw(globalWin, 0, 0);
  207. G.Flip;
  208. G.Delay(1)
  209. END DrawAll;
  210. (* Fonts *)
  211. PROCEDURE GetFont*(W: Widget): G.Font;
  212. RETURN font END GetFont;
  213. (* General *)
  214. PROCEDURE TriggerOnMouseMove*(W: Widget; x, y, btn: INTEGER);
  215. BEGIN
  216. IF (W # NIL) & (W.onMouseMove # NIL) THEN
  217. W.onMouseMove(W, x, y, btn)
  218. END
  219. END TriggerOnMouseMove;
  220. PROCEDURE TriggerOnMouseDown*(W: Widget; x, y, btn: INTEGER);
  221. BEGIN
  222. IF (W # NIL) & (W.onMouseDown # NIL) THEN
  223. W.onMouseDown(W, x, y, btn)
  224. END
  225. END TriggerOnMouseDown;
  226. PROCEDURE TriggerOnMouseUp*(W: Widget; x, y, btn: INTEGER);
  227. BEGIN
  228. IF (W # NIL) & (W.onMouseUp # NIL) THEN
  229. W.onMouseUp(W, x, y, btn)
  230. END
  231. END TriggerOnMouseUp;
  232. PROCEDURE TriggerOnMouseEnter*(W: Widget);
  233. BEGIN
  234. IF (W # NIL) & (W.onMouseEnter # NIL) THEN W.onMouseEnter(W) END
  235. END TriggerOnMouseEnter;
  236. PROCEDURE TriggerOnMouseLeave*(W: Widget);
  237. BEGIN
  238. IF (W # NIL) & (W.onMouseLeave # NIL) THEN W.onMouseLeave(W) END
  239. END TriggerOnMouseLeave;
  240. PROCEDURE TriggerOnClick*(W: Widget);
  241. BEGIN
  242. IF (W # NIL) & (W.onClick # NIL) THEN
  243. W.onClick(W)
  244. END
  245. END TriggerOnClick;
  246. PROCEDURE FindWidgetUnderMouse*(W: Widget; VAR x, y: INTEGER): Widget;
  247. VAR p: Widget;
  248. BEGIN
  249. IF W = NIL THEN p := NIL
  250. ELSIF W.body # NIL THEN
  251. p := W.body.prev;
  252. WHILE (p # W.body) &
  253. ~((p.x <= x) & (x < p.x + p.w) &
  254. (p.y <= y) & (y < p.y + p.h))
  255. DO p := p.prev
  256. END;
  257. IF p = W.body THEN p := W
  258. ELSE DEC(x, p.x); DEC(y, p.y);
  259. p := FindWidgetUnderMouse(p, x, y)
  260. END
  261. ELSE p := W
  262. END
  263. RETURN p END FindWidgetUnderMouse;
  264. (** Input: (x; y) relative to window.
  265. Output: (x; y) relative to widget. *)
  266. PROCEDURE WindowToWidgetXY*(W: Widget; VAR x, y: INTEGER);
  267. BEGIN
  268. WHILE (W # NIL) & ~(W IS Window) DO
  269. DEC(x, W.x); DEC(y, W.y);
  270. W := W.parent
  271. END
  272. END WindowToWidgetXY;
  273. (** Returns TRUE if first widget is parent of second. *)
  274. PROCEDURE IsParent*(a, b: Widget): BOOLEAN;
  275. BEGIN
  276. WHILE (b # NIL) & (a # b) DO b := b.parent END
  277. RETURN (a # NIL) & (a = b) END IsParent;
  278. PROCEDURE MouseLeaveCascade(from, to: Widget);
  279. VAR p: Widget;
  280. BEGIN
  281. p := from;
  282. WHILE (p # NIL) & (p # to) & ~IsParent(p, to) DO
  283. EXCL(p.state, hover);
  284. TriggerOnMouseLeave(p);
  285. p := p.parent
  286. END
  287. END MouseLeaveCascade;
  288. PROCEDURE MouseEnterCascade(from, to: Widget);
  289. VAR p: Widget;
  290. m: ARRAY 100 OF Widget;
  291. len: INTEGER;
  292. BEGIN
  293. len := 0;
  294. p := to;
  295. WHILE (p # NIL) & (p # from) & ~IsParent(p, from) DO
  296. INCL(p.state, hover);
  297. IF len < LEN(m) THEN
  298. m[len] := p;
  299. INC(len)
  300. END;
  301. p := p.parent
  302. END;
  303. WHILE len > 0 DO
  304. DEC(len);
  305. TriggerOnMouseEnter(m[len])
  306. END
  307. END MouseEnterCascade;
  308. PROCEDURE HandleMouseMoveEvent*(e: G.Event);
  309. VAR W: Widget;
  310. x, y: INTEGER;
  311. BEGIN
  312. x := e.x; y := e.y;
  313. IF globalWin.curMouseDownWidget # NIL THEN
  314. W := globalWin.curMouseDownWidget;
  315. WindowToWidgetXY(W, x, y);
  316. IF (x >= 0) & (y >= 0) & (W.w > x) & (W.h > y) & (1 IN e.buttons) THEN
  317. INCL(W.state, down)
  318. ELSE EXCL(W.state, down)
  319. END
  320. ELSE
  321. W := FindWidgetUnderMouse(globalWin, x, y);
  322. IF globalWin.curHoverWidget # W THEN
  323. MouseLeaveCascade(globalWin.curHoverWidget, W);
  324. MouseEnterCascade(globalWin.curHoverWidget, W);
  325. globalWin.curHoverWidget := W
  326. END
  327. END;
  328. IF W # NIL THEN
  329. TriggerOnMouseMove(W, x, y, e.button)
  330. END
  331. END HandleMouseMoveEvent;
  332. PROCEDURE HandleMouseDownEvent*(e: G.Event);
  333. VAR W: Widget;
  334. x, y: INTEGER;
  335. BEGIN
  336. x := e.x; y := e.y;
  337. W := FindWidgetUnderMouse(globalWin, x, y);
  338. IF W # NIL THEN
  339. IF e.button = 1 THEN
  340. INCL(W.state, down) (* Mark button as being left-mouse-button-down *)
  341. END;
  342. globalWin.curMouseDownWidget := W; (* Save for future mouse up event *)
  343. TriggerOnMouseDown(W, x, y, e.button)
  344. END
  345. END HandleMouseDownEvent;
  346. PROCEDURE HandleMouseUpEvent*(e: G.Event);
  347. VAR W: Widget;
  348. x, y: INTEGER;
  349. BEGIN
  350. x := e.x; y := e.y;
  351. W := globalWin.curMouseDownWidget;
  352. globalWin.curMouseDownWidget := NIL;
  353. IF W # NIL THEN
  354. WindowToWidgetXY(W, x, y);
  355. EXCL(W.state, down);
  356. TriggerOnMouseUp(W, x, y, e.button);
  357. IF (x >= 0) & (y >= 0) & (W.w > x) & (W.h > y) & (e.button = 1) THEN
  358. TriggerOnClick(W)
  359. END
  360. END
  361. END HandleMouseUpEvent;
  362. PROCEDURE HandleEvent(e: G.Event);
  363. BEGIN
  364. IF e.type = G.mouseMove THEN
  365. HandleMouseMoveEvent(e)
  366. ELSIF e.type = G.mouseDown THEN
  367. HandleMouseDownEvent(e)
  368. ELSIF e.type = G.mouseUp THEN
  369. HandleMouseUpEvent(e)
  370. ELSIF e.type = G.keyDown THEN
  371. IF e.key = G.kEsc THEN exitRunLoop := TRUE END;
  372. ELSIF e.type = G.quit THEN
  373. exitRunLoop := TRUE
  374. END
  375. END HandleEvent;
  376. PROCEDURE Run*;
  377. VAR e: G.Event;
  378. BEGIN
  379. exitRunLoop := FALSE;
  380. REPEAT
  381. WHILE G.HasEvents() DO
  382. G.WaitEvent(e);
  383. HandleEvent(e)
  384. END;
  385. DrawAll
  386. UNTIL exitRunLoop
  387. END Run;
  388. PROCEDURE Init*;
  389. BEGIN
  390. G.Settings(0, 0, {G.manual});
  391. G.Init;
  392. IF G.Done THEN
  393. font := G.LoadFont('../Data/Fonts/Main');
  394. IF font = NIL THEN
  395. Out.String('Gui: Could not load font.'); Out.Ln;
  396. Done := FALSE
  397. END
  398. ELSE Done := FALSE
  399. END
  400. END Init;
  401. PROCEDURE Close*;
  402. BEGIN
  403. G.Close
  404. END Close;
  405. BEGIN
  406. Done := TRUE
  407. END Gui.