2
0

SimpleGui.Mod 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. MODULE SimpleGui;
  2. IMPORT G := Graph, Strings, Out;
  3. TYPE
  4. Widget* = POINTER TO WidgetDesc;
  5. Message* = RECORD END;
  6. DrawMsg* = RECORD(Message) x*, y*, w*, h*: INTEGER END;
  7. MouseMoveMsg* = RECORD(Message) x*, y*: INTEGER END;
  8. MouseEnterMsg* = RECORD(Message) END;
  9. MouseLeaveMsg* = RECORD(Message) END;
  10. MouseDownMsg* = RECORD(Message) x*, y*: INTEGER END;
  11. MouseUpMsg* = RECORD(Message) x*, y*: INTEGER END;
  12. Handler* = PROCEDURE (c: Widget; VAR msg: Message);
  13. WidgetDesc* = RECORD
  14. x*, y*, w*, h*: INTEGER;
  15. bgColor*, fgColor*: G.Color;
  16. body*: Widget;
  17. next*: Widget;
  18. handle*: Handler;
  19. END;
  20. Panel* = POINTER TO PanelDesc;
  21. PanelDesc* = RECORD(WidgetDesc) END;
  22. Form* = POINTER TO FormDesc;
  23. FormDesc* = RECORD(PanelDesc) END;
  24. Button* = POINTER TO ButtonDesc;
  25. ButtonDesc* = RECORD(WidgetDesc)
  26. caption*: ARRAY 64 OF CHAR;
  27. hovered*: BOOLEAN; (** TRUE if mouse pointer is over the button *)
  28. pressed*: BOOLEAN (** TRUE if button is held down with LMB *)
  29. ;X*, Y*: INTEGER
  30. END;
  31. VAR
  32. Done*: BOOLEAN; (** FALSE after a failed opration and before the next Init *)
  33. forms*: Form;
  34. font*: G.Font;
  35. quit: BOOLEAN; (** Main loop in procedure Run ends when TRUE *)
  36. hoveredWidget: Widget;
  37. pressedWidget: Widget;
  38. pressedX, pressedY: INTEGER;
  39. (** Widget **)
  40. PROCEDURE FindHoveredInList(list: Widget; x, y: INTEGER;
  41. forMouseDown: BOOLEAN): Widget;
  42. VAR c: Widget;
  43. BEGIN c := list;
  44. WHILE (c # NIL) &
  45. ~((c.x <= x) & (x < c.x + c.w) &
  46. (c.y <= y) & (y < c.y + c.h))
  47. DO c := c.next
  48. END;
  49. IF forMouseDown & (c # NIL) THEN
  50. INC(pressedX, c.x); INC(pressedY, c.y)
  51. END
  52. RETURN c END FindHoveredInList;
  53. PROCEDURE WidgetOnMouseEnter*(c: Widget);
  54. VAR msg: MouseEnterMsg;
  55. BEGIN c.handle(c, msg)
  56. END WidgetOnMouseEnter;
  57. PROCEDURE WidgetOnMouseLeave*(c: Widget);
  58. VAR msg: MouseLeaveMsg;
  59. BEGIN c.handle(c, msg)
  60. END WidgetOnMouseLeave;
  61. PROCEDURE WidgetOnMouseMove*(c: Widget; x, y: INTEGER);
  62. VAR msg: MouseMoveMsg;
  63. BEGIN
  64. IF (hoveredWidget = NIL) OR (c # hoveredWidget) THEN
  65. IF hoveredWidget # NIL THEN WidgetOnMouseLeave(hoveredWidget) END;
  66. hoveredWidget := c;
  67. WidgetOnMouseEnter(hoveredWidget)
  68. END;
  69. msg.x := x; msg.y := y;
  70. c.handle(c, msg)
  71. END WidgetOnMouseMove;
  72. PROCEDURE WidgetHandleMouseMove*(c: Widget; x, y: INTEGER);
  73. VAR p: Widget;
  74. BEGIN
  75. IF pressedWidget # NIL THEN
  76. WidgetOnMouseMove(pressedWidget, x - pressedX, y - pressedY)
  77. ELSE
  78. p := FindHoveredInList(c.body, x, y, FALSE);
  79. IF p # NIL THEN
  80. WidgetHandleMouseMove(p, x - p.x, y - p.y)
  81. ELSE
  82. WidgetOnMouseMove(c, x, y)
  83. END
  84. END
  85. END WidgetHandleMouseMove;
  86. PROCEDURE WidgetOnMouseDown*(c: Widget; x, y: INTEGER);
  87. VAR msg: MouseDownMsg;
  88. BEGIN
  89. pressedWidget := c;
  90. msg.x := x; msg.y := y;
  91. c.handle(c, msg)
  92. END WidgetOnMouseDown;
  93. PROCEDURE WidgetHandleMouseDown*(c: Widget; x, y: INTEGER);
  94. VAR p: Widget;
  95. BEGIN
  96. p := FindHoveredInList(c.body, x, y, TRUE);
  97. IF p # NIL THEN
  98. WidgetHandleMouseDown(p, x - p.x, y - p.y)
  99. ELSE
  100. WidgetOnMouseDown(c, x, y)
  101. END
  102. END WidgetHandleMouseDown;
  103. PROCEDURE WidgetOnMouseUp*(c: Widget; x, y: INTEGER);
  104. VAR msg: MouseUpMsg;
  105. BEGIN
  106. pressedWidget := NIL;
  107. msg.x := x; msg.y := y;
  108. c.handle(c, msg)
  109. END WidgetOnMouseUp;
  110. PROCEDURE WidgetHandleMouseUp*(c: Widget; x, y: INTEGER);
  111. VAR p: Widget;
  112. BEGIN
  113. IF pressedWidget # NIL THEN
  114. WidgetOnMouseUp(pressedWidget, x - pressedX, y - pressedY)
  115. ELSE
  116. p := FindHoveredInList(c.body, x, y, FALSE);
  117. IF p # NIL THEN
  118. WidgetHandleMouseUp(p, x - p.x, y - p.y)
  119. ELSE
  120. WidgetOnMouseUp(c, x, y)
  121. END
  122. END
  123. END WidgetHandleMouseUp;
  124. PROCEDURE WidgetHandler*(c: Widget; VAR msg: Message);
  125. VAR x, y: INTEGER;
  126. BEGIN
  127. IF msg IS DrawMsg THEN
  128. x := msg(DrawMsg).x; y := msg(DrawMsg).y;
  129. G.FillRect(x, y, x + c.w - 1, y + c.h - 1, c.bgColor);
  130. G.Rect(x, y, x + c.w - 1, y + c.h - 1, c.fgColor);
  131. G.Rect(x + 2, y + 2, x + c.w - 3, y + c.h - 3, c.fgColor)
  132. END
  133. END WidgetHandler;
  134. PROCEDURE DrawWidget*(c: Widget; x, y, w, h: INTEGER);
  135. VAR M: DrawMsg;
  136. BEGIN
  137. M.x := x; M.y := y; M.w := w; M.h := h;
  138. c.handle(c, M)
  139. END DrawWidget;
  140. PROCEDURE DrawBody*(c: Widget; x, y, w, h: INTEGER);
  141. VAR p: Widget;
  142. x2, y2, w2, h2: INTEGER;
  143. BEGIN
  144. p := c.body;
  145. WHILE p # NIL DO
  146. x2 := x + p.x; y2 := y + p.y;
  147. w2 := w - p.x; h2 := h - p.y;
  148. DrawWidget(p, x2, y2, w2, h2);
  149. p := p.next
  150. END
  151. END DrawBody;
  152. PROCEDURE InitWidget*(c: Widget; w, h: INTEGER);
  153. BEGIN c.x := 0; c.y := 0; c.w := w; c.h := h;
  154. G.MakeCol(c.bgColor, 180, 180, 180);
  155. G.MakeCol(c.fgColor, 0, 0, 0);
  156. c.handle := WidgetHandler;
  157. c.body := NIL; c.next := NIL
  158. END InitWidget;
  159. PROCEDURE Put*(c, where: Widget; x, y: INTEGER);
  160. BEGIN
  161. c.x := x; c.y := y;
  162. c.next := where.body;
  163. where.body := c
  164. END Put;
  165. (** Panel **)
  166. PROCEDURE PanelHandler*(c: Widget; VAR msg: Message);
  167. VAR x, y: INTEGER;
  168. BEGIN
  169. IF msg IS DrawMsg THEN
  170. x := msg(DrawMsg).x; y := msg(DrawMsg).y;
  171. G.FillRect(x, y, x + c.w - 1, y + c.h - 1, c.bgColor);
  172. DrawBody(c, x, y, c.w, c.h)
  173. ELSE WidgetHandler(c, msg)
  174. END
  175. END PanelHandler;
  176. PROCEDURE InitPanel*(c: Panel; w, h: INTEGER);
  177. BEGIN InitWidget(c, w, h);
  178. c.handle := PanelHandler
  179. END InitPanel;
  180. PROCEDURE NewPanel*(x, y, w, h: INTEGER): Panel;
  181. VAR c: Panel;
  182. BEGIN NEW(c); InitPanel(c, w, h)
  183. RETURN c END NewPanel;
  184. (** Form **)
  185. PROCEDURE DrawForm*(c: Form);
  186. BEGIN
  187. G.FillRect(c.x, c.y, c.x + c.w - 1, c.y + c.h - 1, c.bgColor);
  188. DrawBody(c, c.x, c.y, c.w, c.h)
  189. END DrawForm;
  190. PROCEDURE FormHandler*(c: Widget; VAR msg: Message);
  191. BEGIN WidgetHandler(c, msg)
  192. END FormHandler;
  193. PROCEDURE InitForm*(c: Form; x, y, w, h: INTEGER);
  194. BEGIN InitPanel(c, w, h);
  195. c.x := x; c.y := y;
  196. c.handle := FormHandler;
  197. c.next := forms; forms := c
  198. END InitForm;
  199. PROCEDURE NewForm*(x, y, w, h: INTEGER): Form;
  200. VAR c: Form;
  201. BEGIN NEW(c); InitForm(c, x, y, w, h);
  202. RETURN c END NewForm;
  203. (** Button **)
  204. PROCEDURE DrawButton*(c: Button; x, y, w, h: INTEGER);
  205. VAR cw, ch, tw, tx, ty: INTEGER;
  206. down: BOOLEAN;
  207. BEGIN
  208. down := c(Button).pressed & c(Button).hovered;
  209. G.FillRect(x, y, x + c.w - 1, y + c.h - 1, c.bgColor);
  210. G.Rect(x, y, x + c.w - 1, y + c.h - 1, c.fgColor);
  211. IF ~down THEN
  212. G.Rect(x, y, x + c.w - 2, y + c.h - 2, c.fgColor)
  213. END;
  214. G.GetMonoFontSize(font, cw, ch);
  215. tw := Strings.Length(c.caption) * cw;
  216. tx := x + (c.w - tw) DIV 2;
  217. ty := y + (c.h - ch) DIV 2;
  218. IF down THEN INC(tx); INC(ty) END;
  219. G.DrawString(c.caption, tx, ty, font, c.fgColor)
  220. ;G.Line(x + c.w DIV 2, y + c.h DIV 2, x + c(Button).X, y + c(Button).Y, c.fgColor)
  221. END DrawButton;
  222. PROCEDURE BMM(c: Button; x, y: INTEGER);
  223. BEGIN
  224. c.X := x; c.Y := y
  225. END BMM;
  226. PROCEDURE ButtonHandler*(c: Widget; VAR msg: Message);
  227. VAR b: Button;
  228. BEGIN b := c(Button);
  229. IF msg IS DrawMsg THEN
  230. DrawButton(b, msg(DrawMsg).x, msg(DrawMsg).y,
  231. msg(DrawMsg).w, msg(DrawMsg).h)
  232. ELSIF msg IS MouseMoveMsg THEN BMM(b, msg(MouseMoveMsg).x, msg(MouseMoveMsg).y)
  233. ELSIF msg IS MouseEnterMsg THEN b.hovered := TRUE
  234. ELSIF msg IS MouseLeaveMsg THEN b.hovered := FALSE
  235. ELSIF msg IS MouseDownMsg THEN b.pressed := TRUE
  236. ELSIF msg IS MouseUpMsg THEN b.pressed := FALSE
  237. ELSE WidgetHandler(c, msg)
  238. END
  239. END ButtonHandler;
  240. PROCEDURE InitButton*(c: Button; where: Widget;
  241. x, y, w, h: INTEGER; caption: ARRAY OF CHAR);
  242. BEGIN InitWidget(c, w, h);
  243. Strings.Copy(caption, c.caption);
  244. c.hovered := FALSE;
  245. c.pressed := FALSE;
  246. c.handle := ButtonHandler;
  247. Put(c, where, x, y)
  248. ;c.X := 0; c.Y := 0;
  249. END InitButton;
  250. PROCEDURE NewButton*(where: Widget; x, y, w, h: INTEGER; caption: ARRAY OF CHAR): Button;
  251. VAR c: Button;
  252. BEGIN NEW(c); InitButton(c, where, x, y, w, h, caption)
  253. RETURN c END NewButton;
  254. (** General **)
  255. PROCEDURE DrawAll*;
  256. VAR c: Widget;
  257. BEGIN
  258. c := forms;
  259. WHILE c # NIL DO
  260. DrawForm(c(Form));
  261. c := c.next
  262. END;
  263. G.Flip
  264. END DrawAll;
  265. PROCEDURE HandleMouseMove(VAR e: G.Event);
  266. VAR c: Widget;
  267. BEGIN
  268. c := FindHoveredInList(forms, e.x, e.y, FALSE);
  269. IF c # NIL THEN
  270. WidgetHandleMouseMove(c, e.x - c.x, e.y - c.y)
  271. END
  272. END HandleMouseMove;
  273. PROCEDURE HandleMouseDown(VAR e: G.Event);
  274. VAR c: Widget;
  275. BEGIN
  276. pressedX := 0; pressedY := 0;
  277. c := FindHoveredInList(forms, e.x, e.y, TRUE);
  278. IF c # NIL THEN
  279. WidgetHandleMouseDown(c, e.x - c.x, e.y - c.y)
  280. END
  281. END HandleMouseDown;
  282. PROCEDURE HandleMouseUp(VAR e: G.Event);
  283. VAR c: Widget;
  284. BEGIN
  285. c := FindHoveredInList(forms, e.x, e.y, FALSE);
  286. IF c # NIL THEN
  287. WidgetHandleMouseUp(c, e.x - c.x, e.y - c.y)
  288. END
  289. END HandleMouseUp;
  290. PROCEDURE HandleEvent(VAR e: G.Event);
  291. BEGIN
  292. IF e.type = G.quit THEN quit := TRUE
  293. ELSIF e.type = G.mouseMove THEN HandleMouseMove(e)
  294. ELSIF e.type = G.mouseDown THEN HandleMouseDown(e)
  295. ELSIF e.type = G.mouseUp THEN HandleMouseUp(e)
  296. END
  297. END HandleEvent;
  298. PROCEDURE Run*;
  299. VAR e: G.Event;
  300. BEGIN
  301. quit := FALSE;
  302. REPEAT
  303. WHILE G.HasEvents() DO
  304. G.WaitEvent(e);
  305. HandleEvent(e)
  306. END;
  307. DrawAll
  308. UNTIL quit
  309. END Run;
  310. PROCEDURE Init*;
  311. BEGIN
  312. forms := NIL;
  313. font := G.LoadFont('Data/Fonts/Main');
  314. IF font = NIL THEN Out.String('SimpleGui: could not load font.'); Out.Ln END;
  315. Done := font # NIL;
  316. hoveredWidget := NIL; pressedWidget := NIL;
  317. pressedX := 0; pressedY := 0
  318. END Init;
  319. END SimpleGui.