SimpleGui.Mod 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  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. Z: G.Color;
  144. BEGIN
  145. p := c.body;
  146. WHILE p # NIL DO
  147. x2 := x + p.x; y2 := y + p.y;
  148. w2 := w - p.x; h2 := h - p.y;
  149. (* !FIXME clip x2,y2,w2,h2 to not more than x,y,w,h*)
  150. (*G.UnsetClip; G.MakeCol(Z, 255, 0, 0); G.Rect(x2-1, y2-1, x2 + w2, y2 + h2,Z);*)
  151. G.SetClip(x2, y2, p.w, p.h);
  152. DrawWidget(p, x2, y2, p.w, p.h);
  153. p := p.next
  154. END;
  155. G.UnsetClip
  156. END DrawBody;
  157. PROCEDURE SetBgColor*(c: Widget; color: G.Color);
  158. BEGIN c.bgColor := color
  159. END SetBgColor;
  160. PROCEDURE SetFgColor*(c: Widget; color: G.Color);
  161. BEGIN c.fgColor := color
  162. END SetFgColor;
  163. PROCEDURE InitWidget*(c: Widget; w, h: INTEGER);
  164. BEGIN c.x := 0; c.y := 0; c.w := w; c.h := h;
  165. G.MakeCol(c.bgColor, 180, 180, 180);
  166. G.MakeCol(c.fgColor, 0, 0, 0);
  167. c.handle := WidgetHandler;
  168. c.body := NIL; c.next := NIL
  169. END InitWidget;
  170. PROCEDURE Put*(c, where: Widget; x, y: INTEGER);
  171. BEGIN
  172. IF c # NIL THEN
  173. c.x := x; c.y := y;
  174. IF where # NIL THEN
  175. c.next := where.body;
  176. where.body := c
  177. END
  178. END
  179. END Put;
  180. (** Panel **)
  181. PROCEDURE PanelHandler*(c: Widget; VAR msg: Message);
  182. VAR x, y: INTEGER;
  183. BEGIN
  184. IF msg IS DrawMsg THEN
  185. x := msg(DrawMsg).x; y := msg(DrawMsg).y;
  186. G.FillRect(x, y, x + c.w - 1, y + c.h - 1, c.bgColor);
  187. DrawBody(c, x, y, c.w, c.h)
  188. ELSE WidgetHandler(c, msg)
  189. END
  190. END PanelHandler;
  191. PROCEDURE InitPanel*(c: Panel; where: Widget; x, y, w, h: INTEGER);
  192. BEGIN InitWidget(c, w, h);
  193. c.handle := PanelHandler;
  194. Put(c, where, x, y)
  195. END InitPanel;
  196. PROCEDURE NewPanel*(where: Widget; x, y, w, h: INTEGER): Panel;
  197. VAR c: Panel;
  198. BEGIN NEW(c); InitPanel(c, where, x, y, w, h)
  199. RETURN c END NewPanel;
  200. (** Form **)
  201. PROCEDURE DrawForm*(c: Form);
  202. BEGIN
  203. G.FillRect(c.x, c.y, c.x + c.w - 1, c.y + c.h - 1, c.bgColor);
  204. DrawBody(c, c.x, c.y, c.w, c.h)
  205. END DrawForm;
  206. PROCEDURE FormHandler*(c: Widget; VAR msg: Message);
  207. BEGIN WidgetHandler(c, msg)
  208. END FormHandler;
  209. PROCEDURE InitForm*(c: Form; x, y, w, h: INTEGER);
  210. BEGIN InitPanel(c, NIL, x, y, w, h);
  211. c.handle := FormHandler;
  212. c.next := forms; forms := c
  213. END InitForm;
  214. PROCEDURE NewForm*(x, y, w, h: INTEGER): Form;
  215. VAR c: Form;
  216. BEGIN NEW(c); InitForm(c, x, y, w, h);
  217. RETURN c END NewForm;
  218. (** Button **)
  219. PROCEDURE DrawButton*(c: Button; x, y, w, h: INTEGER);
  220. VAR cw, ch, tw, tx, ty: INTEGER;
  221. down: BOOLEAN;
  222. Z: G.Color;
  223. BEGIN
  224. down := c(Button).pressed & c(Button).hovered;
  225. G.FillRect(x, y, x + c.w - 1, y + c.h - 1, c.bgColor);
  226. ;G.MakeCol(Z, 255, 128, 0);
  227. ;G.Line(x + c.h DIV 4, y + c.h DIV 2, x + c(Button).X, y + c(Button).Y, Z);
  228. ;G.MakeCol(Z, 215, 0, 0);
  229. ;G.Line(x + c.h DIV 4, y + c.h DIV 2 + 1, x + c(Button).X, y + c(Button).Y + 1, Z);
  230. G.Rect(x, y, x + c.w - 1, y + c.h - 1, c.fgColor);
  231. IF ~down THEN
  232. G.Rect(x, y, x + c.w - 2, y + c.h - 2, c.fgColor)
  233. END;
  234. G.GetMonoFontSize(font, cw, ch);
  235. tw := Strings.Length(c.caption) * cw;
  236. tx := x + (c.w - tw) DIV 2;
  237. ty := y + (c.h - ch) DIV 2;
  238. IF down THEN INC(tx); INC(ty) END;
  239. G.DrawString(c.caption, tx, ty, font, c.fgColor)
  240. END DrawButton;
  241. PROCEDURE BMM(c: Button; x, y: INTEGER);
  242. BEGIN
  243. c.X := x; c.Y := y
  244. END BMM;
  245. PROCEDURE ButtonHandler*(c: Widget; VAR msg: Message);
  246. VAR b: Button;
  247. BEGIN b := c(Button);
  248. IF msg IS DrawMsg THEN
  249. DrawButton(b, msg(DrawMsg).x, msg(DrawMsg).y,
  250. msg(DrawMsg).w, msg(DrawMsg).h)
  251. ELSIF msg IS MouseMoveMsg THEN BMM(b, msg(MouseMoveMsg).x, msg(MouseMoveMsg).y)
  252. ELSIF msg IS MouseEnterMsg THEN b.hovered := TRUE
  253. ELSIF msg IS MouseLeaveMsg THEN b.hovered := FALSE
  254. ELSIF msg IS MouseDownMsg THEN b.pressed := TRUE
  255. ELSIF msg IS MouseUpMsg THEN b.pressed := FALSE
  256. ELSE WidgetHandler(c, msg)
  257. END
  258. END ButtonHandler;
  259. PROCEDURE InitButton*(c: Button; where: Widget;
  260. x, y, w, h: INTEGER; caption: ARRAY OF CHAR);
  261. BEGIN InitWidget(c, w, h);
  262. Strings.Copy(caption, c.caption);
  263. c.hovered := FALSE;
  264. c.pressed := FALSE;
  265. c.handle := ButtonHandler;
  266. Put(c, where, x, y)
  267. ;c.X := 0; c.Y := 0;
  268. END InitButton;
  269. PROCEDURE NewButton*(where: Widget; x, y, w, h: INTEGER; caption: ARRAY OF CHAR): Button;
  270. VAR c: Button;
  271. BEGIN NEW(c); InitButton(c, where, x, y, w, h, caption)
  272. RETURN c END NewButton;
  273. (** General **)
  274. PROCEDURE DrawAll*;
  275. VAR c: Widget;
  276. BEGIN
  277. c := forms;
  278. WHILE c # NIL DO
  279. DrawForm(c(Form));
  280. c := c.next
  281. END;
  282. G.Flip
  283. END DrawAll;
  284. PROCEDURE HandleMouseMove(VAR e: G.Event);
  285. VAR c: Widget;
  286. BEGIN
  287. c := FindHoveredInList(forms, e.x, e.y, FALSE);
  288. IF c # NIL THEN
  289. WidgetHandleMouseMove(c, e.x - c.x, e.y - c.y)
  290. END
  291. END HandleMouseMove;
  292. PROCEDURE HandleMouseDown(VAR e: G.Event);
  293. VAR c: Widget;
  294. BEGIN
  295. pressedX := 0; pressedY := 0;
  296. c := FindHoveredInList(forms, e.x, e.y, TRUE);
  297. IF c # NIL THEN
  298. WidgetHandleMouseDown(c, e.x - c.x, e.y - c.y)
  299. END
  300. END HandleMouseDown;
  301. PROCEDURE HandleMouseUp(VAR e: G.Event);
  302. VAR c: Widget;
  303. BEGIN
  304. c := FindHoveredInList(forms, e.x, e.y, FALSE);
  305. IF c # NIL THEN
  306. WidgetHandleMouseUp(c, e.x - c.x, e.y - c.y)
  307. END
  308. END HandleMouseUp;
  309. PROCEDURE HandleEvent(VAR e: G.Event);
  310. BEGIN
  311. IF e.type = G.quit THEN quit := TRUE
  312. ELSIF e.type = G.mouseMove THEN HandleMouseMove(e)
  313. ELSIF e.type = G.mouseDown THEN HandleMouseDown(e)
  314. ELSIF e.type = G.mouseUp THEN HandleMouseUp(e)
  315. END
  316. END HandleEvent;
  317. PROCEDURE Run*;
  318. VAR e: G.Event;
  319. BEGIN
  320. quit := FALSE;
  321. REPEAT
  322. WHILE G.HasEvents() DO
  323. G.WaitEvent(e);
  324. HandleEvent(e)
  325. END;
  326. DrawAll
  327. UNTIL quit
  328. END Run;
  329. PROCEDURE Init*;
  330. BEGIN
  331. forms := NIL;
  332. font := G.LoadFont('Data/Fonts/Main');
  333. IF font = NIL THEN Out.String('SimpleGui: could not load font.'); Out.Ln END;
  334. Done := font # NIL;
  335. hoveredWidget := NIL; pressedWidget := NIL;
  336. pressedX := 0; pressedY := 0
  337. END Init;
  338. END SimpleGui.