Gui.Mod 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. MODULE Gui; (* My GUI Module *)
  2. IMPORT Out, G := Graph;
  3. CONST
  4. charW = 8; charH = 16;
  5. (* redrawState *)
  6. redrawNone* = 0;
  7. redrawAll* = 1;
  8. redrawSome* = 2;
  9. TYPE
  10. Method* = POINTER TO MethodDesc;
  11. Component* = POINTER TO ComponentDesc;
  12. Container* = POINTER TO ContainerDesc;
  13. Form* = POINTER TO FormDesc;
  14. Button* = POINTER TO ButtonDesc;
  15. Edit* = POINTER TO EditDesc;
  16. Circle* = POINTER TO CircleDesc;
  17. (* Handler Procedures *)
  18. OnDrawHandler = PROCEDURE (c: Component; bmp: G.Bitmap; x0, y0: INTEGER);
  19. OnMouseDownHandler = PROCEDURE (c: Component; x, y, btn: INTEGER);
  20. OnKeyDownHandler = PROCEDURE (c: Component; key: INTEGER);
  21. MethodDesc* = RECORD
  22. draw*: OnDrawHandler;
  23. mouseDown*: OnMouseDownHandler;
  24. keyDown*: OnKeyDownHandler;
  25. add*: PROCEDURE (c, toAdd: Component);
  26. updated*: PROCEDURE (c: Component; moved: BOOLEAN);
  27. redraw*: PROCEDURE (c: Component; bmp: G.Bitmap; x0, y0: INTEGER);
  28. (* Setters *)
  29. setX*: PROCEDURE (c: Component; x: INTEGER);
  30. setY*: PROCEDURE (c: Component; y: INTEGER);
  31. setW*: PROCEDURE (c: Component; w: INTEGER);
  32. setH*: PROCEDURE (c: Component; h: INTEGER);
  33. setBounds*: PROCEDURE (c: Component; x, y, w, h: INTEGER);
  34. setVisible*: PROCEDURE (c: Component; visible: BOOLEAN);
  35. (* Event Handler Setters *)
  36. setOnDraw*: PROCEDURE (c: Component; hn: OnDrawHandler);
  37. setOnMouseDown*: PROCEDURE (c: Component; hn: OnMouseDownHandler);
  38. setOnKeyDown*: PROCEDURE (c: Component; hn: OnKeyDownHandler);
  39. END;
  40. ComponentDesc* = RECORD
  41. x-, y-, w-, h-: INTEGER;
  42. visible-: BOOLEAN;
  43. redrawState-: INTEGER; (* see constants with prefix 'redraw' *)
  44. do*: Method;
  45. onDraw-: OnDrawHandler;
  46. onMouseDown-: OnMouseDownHandler;
  47. onKeyDown-: OnKeyDownHandler;
  48. parent*: Component;
  49. next*: Component
  50. END;
  51. ContainerDesc* = RECORD(ComponentDesc)
  52. first*: Component
  53. END;
  54. FormDesc* = RECORD(ContainerDesc)
  55. bgColor*: INTEGER
  56. END;
  57. ButtonDesc* = RECORD(ComponentDesc)
  58. caption-: ARRAY 100 OF CHAR
  59. END;
  60. EditDesc* = RECORD(ComponentDesc)
  61. text-: ARRAY 4096 OF CHAR;
  62. selStart-: INTEGER;
  63. selLen-: INTEGER
  64. END;
  65. CircleDesc* = RECORD(ComponentDesc)
  66. color*: INTEGER
  67. END;
  68. VAR
  69. screen: G.Bitmap;
  70. font: G.Font;
  71. needFlip: BOOLEAN;
  72. (* Methods *)
  73. method, formMethod, buttonMethod, circleMethod, editMethod: Method;
  74. form: Form;
  75. btnHello: Button;
  76. circle: Circle;
  77. edit: Edit;
  78. (* Component *)
  79. PROCEDURE InitComponent*(c: Component);
  80. BEGIN
  81. c.x := 0; c.y := 0; c.w := 0; c.h := 0;
  82. c.visible := TRUE;
  83. c.redrawState := redrawNone;
  84. c.do := method; c.next := NIL; c.parent := NIL;
  85. c.onDraw := NIL; c.onMouseDown := NIL
  86. END InitComponent;
  87. PROCEDURE UpdatedComponent*(c: Component; moved: BOOLEAN);
  88. BEGIN
  89. IF c.redrawState # redrawAll THEN
  90. c.redrawState := redrawAll;
  91. c := c.parent;
  92. IF moved & (c # NIL) & (c.redrawState # redrawAll) THEN
  93. c.redrawState := redrawAll;
  94. c := c.parent
  95. END;
  96. WHILE (c # NIL) & (c.redrawState = redrawNone) DO
  97. c.redrawState := redrawSome;
  98. c := c.parent
  99. END
  100. END
  101. END UpdatedComponent;
  102. PROCEDURE RedrawComponent*(c: Component; bmp: G.Bitmap; x0, y0: INTEGER);
  103. BEGIN
  104. IF (c.redrawState = redrawAll) & c.visible THEN
  105. c.do.draw(c, bmp, x0, y0);
  106. needFlip := TRUE
  107. END;
  108. c.redrawState := redrawNone
  109. END RedrawComponent;
  110. PROCEDURE ComponentSetX(c: Component; x: INTEGER);
  111. BEGIN c.x := x; c.do.updated(c, TRUE)
  112. END ComponentSetX;
  113. PROCEDURE ComponentSetY(c: Component; y: INTEGER);
  114. BEGIN c.y := y; c.do.updated(c, TRUE)
  115. END ComponentSetY;
  116. PROCEDURE ComponentSetW(c: Component; w: INTEGER);
  117. BEGIN c.w := w; c.do.updated(c, TRUE)
  118. END ComponentSetW;
  119. PROCEDURE ComponentSetH(c: Component; h: INTEGER);
  120. BEGIN c.h := h; c.do.updated(c, TRUE)
  121. END ComponentSetH;
  122. PROCEDURE ComponentSetBounds(c: Component; x, y, w, h: INTEGER);
  123. BEGIN c.x := x; c.y := y; c.w := w; c.h := h; c.do.updated(c, TRUE)
  124. END ComponentSetBounds;
  125. PROCEDURE ComponentSetVisible(c: Component; visible: BOOLEAN);
  126. BEGIN
  127. IF c.visible # visible THEN
  128. c.visible := visible;
  129. c.do.updated(c, ~visible)
  130. END
  131. END ComponentSetVisible;
  132. PROCEDURE ComponentSetOnDraw(c: Component; hn: OnDrawHandler);
  133. BEGIN c.onDraw := hn
  134. END ComponentSetOnDraw;
  135. PROCEDURE ComponentSetOnMouseDown(c: Component; hn: OnMouseDownHandler);
  136. BEGIN c.onMouseDown := hn
  137. END ComponentSetOnMouseDown;
  138. PROCEDURE ComponentSetOnKeyDown(c: Component; hn: OnKeyDownHandler);
  139. BEGIN c.onKeyDown := hn
  140. END ComponentSetOnKeyDown;
  141. (* Container *)
  142. PROCEDURE InitContainer*(c: Container);
  143. BEGIN
  144. InitComponent(c);
  145. c.first := NIL
  146. END InitContainer;
  147. PROCEDURE RedrawContainer*(c: Component; bmp: G.Bitmap; x0, y0: INTEGER);
  148. VAR x: Component;
  149. BEGIN
  150. IF c.visible THEN
  151. IF c.redrawState = redrawAll THEN
  152. c.do.draw(c, bmp, x0, y0);
  153. needFlip := TRUE
  154. ELSIF c.redrawState = redrawSome THEN
  155. INC(x0, c.x); INC(y0, c.y);
  156. x := c(Container).first;
  157. WHILE x # NIL DO
  158. IF x.redrawState # redrawNone THEN
  159. x.do.redraw(x, bmp, x0, y0)
  160. END;
  161. x := x.next
  162. END;
  163. needFlip := TRUE
  164. END
  165. END;
  166. c.redrawState := redrawNone
  167. END RedrawContainer;
  168. PROCEDURE DrawContainerChildren(c: Component; bmp: G.Bitmap; x0, y0: INTEGER);
  169. VAR d: Component;
  170. BEGIN
  171. INC(x0, c.x); INC(y0, c.y);
  172. d := c(Container).first;
  173. WHILE d # NIL DO
  174. IF d.visible THEN d.do.draw(d, bmp, x0, y0) END;
  175. d.redrawState := redrawNone;
  176. d := d.next
  177. END
  178. END DrawContainerChildren;
  179. PROCEDURE MouseDownContainer(c: Component; x, y, btn: INTEGER);
  180. VAR d: Component;
  181. BEGIN
  182. d := c(Container).first;
  183. WHILE (d # NIL) &
  184. ~(d.visible &
  185. (x >= d.x) & (x < d.x + d.w) &
  186. (y >= d.y) & (y < d.y + d.h))
  187. DO d := d.next
  188. END;
  189. IF d # NIL THEN d.do.mouseDown(d, x - d.x, y - d.y, btn) END
  190. END MouseDownContainer;
  191. PROCEDURE AddToContainer(c, toAdd: Component);
  192. BEGIN
  193. toAdd.next := c(Container).first;
  194. c(Container).first := toAdd;
  195. toAdd.parent := c;
  196. toAdd.do.updated(c, TRUE)
  197. END AddToContainer;
  198. (* Form *)
  199. PROCEDURE InitForm*(f: Form);
  200. BEGIN
  201. InitContainer(f);
  202. f.do := formMethod;
  203. f.bgColor := G.MakeCol(180, 180, 180)
  204. END InitForm;
  205. PROCEDURE NewForm*(): Form;
  206. VAR f: Form;
  207. BEGIN
  208. NEW(f); InitForm(f); RETURN f
  209. END NewForm;
  210. PROCEDURE DrawForm(c: Component; bmp: G.Bitmap; x0, y0: INTEGER);
  211. VAR x, y: INTEGER;
  212. BEGIN
  213. x := x0 + c.x; y := y0 + c.y;
  214. G.RectFill(bmp, x, y, x + c.w - 1, y + c.h - 1, c(Form).bgColor);
  215. DrawContainerChildren(c, bmp, x0, y0)
  216. END DrawForm;
  217. (* Button *)
  218. PROCEDURE InitButton*(b: Button; caption: ARRAY OF CHAR);
  219. BEGIN
  220. InitComponent(b);
  221. b.do := buttonMethod;
  222. b.caption := caption
  223. END InitButton;
  224. PROCEDURE NewButton*(caption: ARRAY OF CHAR): Button;
  225. VAR b: Button;
  226. BEGIN
  227. NEW(b); InitButton(b, caption); RETURN b
  228. END NewButton;
  229. PROCEDURE DrawButton(c: Component; bmp: G.Bitmap; x0, y0: INTEGER);
  230. VAR i, len, c1, c2, c3: INTEGER;
  231. BEGIN
  232. INC(x0, c.x); INC(y0, c.y);
  233. c1 := G.MakeCol(255, 255, 255);
  234. c2 := G.MakeCol(160, 160, 160);
  235. c3 := G.MakeCol(100, 100, 100);
  236. len := 0; WHILE c(Button).caption[len] # 0X DO INC(len) END;
  237. G.RectFill(bmp, x0 + 1, y0 + 1, x0 + c.w - 3, y0 + c.h - 3,
  238. G.MakeCol(200, 200, 200));
  239. G.HLine(bmp, x0, y0, x0 + c.w - 2, c1);
  240. G.VLine(bmp, x0, y0, y0 + c.h - 2, c1);
  241. G.HLine(bmp, x0 + 1, y0 + c.h - 2, x0 + c.w - 3, c2);
  242. G.VLine(bmp, x0 + c.w - 2, y0 + 1, y0 + c.h - 2, c2);
  243. G.HLine(bmp, x0, y0 + c.h - 1, x0 + c.w - 1, c3);
  244. G.VLine(bmp, x0 + c.w - 1, y0, y0 + c.h - 2, c3);
  245. FOR i := 0 TO len - 1 DO
  246. G.DrawCharacter(screen, font, x0 + (c.w - charW * len) DIV 2,
  247. y0 + (c.h - charH) DIV 2, c(Button).caption[i],
  248. G.MakeCol(0, 0, 0));
  249. INC(x0, charW)
  250. END
  251. END DrawButton;
  252. PROCEDURE MouseDownButton(c: Component; x, y, btn: INTEGER);
  253. BEGIN
  254. IF c.onMouseDown # NIL THEN c.onMouseDown(c, x, y, btn) END
  255. END MouseDownButton;
  256. (* Edit *)
  257. PROCEDURE InitEdit*(e: Edit);
  258. BEGIN
  259. InitComponent(e);
  260. e.do := editMethod;
  261. e.text[0] := 0X;
  262. e.selStart := 0;
  263. e.selLen := 0
  264. END InitEdit;
  265. PROCEDURE NewEdit*(): Edit;
  266. VAR e: Edit;
  267. BEGIN
  268. NEW(e); InitEdit(e); RETURN e
  269. END NewEdit;
  270. PROCEDURE DrawEdit(c: Component; bmp: G.Bitmap; x0, y0: INTEGER);
  271. VAR i, len, c1, c2, c3: INTEGER;
  272. BEGIN
  273. INC(x0, c.x); INC(y0, c.y);
  274. c1 := G.MakeCol(255, 255, 255);
  275. c2 := G.MakeCol(160, 160, 160);
  276. c3 := G.MakeCol(100, 100, 100);
  277. len := 0; WHILE c(Edit).text[len] # 0X DO INC(len) END;
  278. G.RectFill(bmp, x0 + 1, y0 + 1, x0 + c.w - 3, y0 + c.h - 3,
  279. G.MakeCol(200, 200, 200));
  280. G.HLine(bmp, x0, y0, x0 + c.w - 2, c1);
  281. G.VLine(bmp, x0, y0, y0 + c.h - 2, c1);
  282. G.HLine(bmp, x0 + 1, y0 + c.h - 2, x0 + c.w - 3, c2);
  283. G.VLine(bmp, x0 + c.w - 2, y0 + 1, y0 + c.h - 2, c2);
  284. G.HLine(bmp, x0, y0 + c.h - 1, x0 + c.w - 1, c3);
  285. G.VLine(bmp, x0 + c.w - 1, y0, y0 + c.h - 2, c3);
  286. FOR i := 0 TO len - 1 DO
  287. G.DrawCharacter(screen, font, x0 + (c.w - charW * len) DIV 2,
  288. y0 + (c.h - charH) DIV 2, c(Edit).text[i],
  289. G.MakeCol(0, 0, 0));
  290. INC(x0, charW)
  291. END
  292. END DrawEdit;
  293. PROCEDURE MouseDownEdit(c: Component; x, y, btn: INTEGER);
  294. BEGIN
  295. IF c.onMouseDown # NIL THEN c.onMouseDown(c, x, y, btn) END
  296. END MouseDownEdit;
  297. PROCEDURE KeyDownEdit(c: Component; key: INTEGER);
  298. BEGIN
  299. IF c.onKeyDown # NIL THEN c.onKeyDown(c, key) END
  300. END KeyDownEdit;
  301. (* Circle *)
  302. PROCEDURE InitCircle*(c: Circle);
  303. BEGIN
  304. InitComponent(c);
  305. c.do := circleMethod;
  306. c.color := G.MakeCol(0, 0, 0)
  307. END InitCircle;
  308. PROCEDURE NewCircle*(): Circle;
  309. VAR c: Circle;
  310. BEGIN
  311. NEW(c); InitCircle(c); RETURN c
  312. END NewCircle;
  313. PROCEDURE DrawCircle(c: Component; bmp: G.Bitmap; x0, y0: INTEGER);
  314. VAR r, x, y, err, color: INTEGER;
  315. BEGIN
  316. INC(x0, c.x); INC(y0, c.y);
  317. IF c.w <= c.h THEN r := (c.w - 1) DIV 2 ELSE r := (c.h - 1) DIV 2 END;
  318. IF r > 0 THEN
  319. INC(x0, c.w DIV 2); INC(y0, c.h DIV 2);
  320. x := 0; y := r; err := 3 - 2 * r; color := c(Circle).color;
  321. WHILE y >= x DO
  322. G.PutPixel(screen, x0 + x, y0 + y, color);
  323. G.PutPixel(screen, x0 + y, y0 + x, color);
  324. G.PutPixel(screen, x0 - y, y0 + x, color);
  325. G.PutPixel(screen, x0 - x, y0 + y, color);
  326. G.PutPixel(screen, x0 - x, y0 - y, color);
  327. G.PutPixel(screen, x0 - y, y0 - x, color);
  328. G.PutPixel(screen, x0 + y, y0 - x, color);
  329. G.PutPixel(screen, x0 + x, y0 - y, color);
  330. IF err < 0 THEN INC(err, 4 * x + 6); INC(x, 1)
  331. ELSE INC(err, 4 * (x - y)); INC(x); DEC(y)
  332. END
  333. END
  334. END
  335. END DrawCircle;
  336. PROCEDURE MouseDownCircle(c: Component; x, y, btn: INTEGER);
  337. BEGIN
  338. c(Circle).color := G.MakeCol(G.Random(256), G.Random(256), G.Random(256));
  339. DEC(c.x); DEC(c.w);
  340. c.do.updated(c, TRUE)
  341. END MouseDownCircle;
  342. (* Other *)
  343. PROCEDURE btnHelloMouseDown(c: Component; x, y, btn: INTEGER);
  344. VAR i: INTEGER;
  345. BEGIN
  346. c(Button).caption[1] := CHR(33 + (ORD(c(Button).caption[1]) + y * c.w + x) MOD 64);
  347. i := 0; WHILE c(Button).caption[i] # 0X DO INC(i) END;
  348. IF i < LEN(c(Button).caption) - 1 THEN
  349. c(Button).caption[i] := CHR(i+ORD('0')); c(Button).caption[i+1] := 0X
  350. END;
  351. IF c.next # NIL THEN c.next.do.setVisible(c.next, ~c.next.visible) END;
  352. c.do.setX(c, c.x + 1)
  353. END btnHelloMouseDown;
  354. PROCEDURE Act;
  355. BEGIN
  356. form.do.redraw(form, screen, 0, 0);
  357. IF needFlip THEN G.Flip; needFlip := FALSE ELSE G.RepeatFlip END
  358. END Act;
  359. PROCEDURE Run;
  360. VAR e: G.Event;
  361. quit: BOOLEAN;
  362. BEGIN
  363. quit := FALSE;
  364. REPEAT
  365. G.WaitEvents(50);
  366. WHILE G.PollEvent(e) DO
  367. CASE e.type OF
  368. G.quit: quit := TRUE
  369. | G.keyDown: IF e.key.code = G.kEsc THEN quit := TRUE END
  370. | G.mouseDown: form.do.mouseDown(form, e.x, e.y, e.button)
  371. | G.mouseMove: needFlip := TRUE
  372. ELSE
  373. END
  374. END;
  375. Act
  376. UNTIL quit
  377. END Run;
  378. PROCEDURE Init;
  379. VAR i: INTEGER;
  380. BEGIN
  381. form := NewForm();
  382. form.w := screen.w;
  383. form.h := screen.h;
  384. FOR i := 0 TO 4 DO
  385. btnHello := NewButton('Hello');
  386. btnHello.x := 10;
  387. btnHello.y := 1 + i * 21;
  388. btnHello.w := 70;
  389. btnHello.h := 20;
  390. btnHello.onMouseDown := btnHelloMouseDown;
  391. btnHello.visible := i MOD 2 = 0;
  392. form.do.add(form, btnHello)
  393. END;
  394. FOR i := 1 TO G.Random(10) + 10 DO
  395. circle := NewCircle();
  396. circle.w := 10 + G.Random(100);
  397. circle.h := circle.w;
  398. circle.x := 100 + G.Random(screen.w - circle.w - 101);
  399. circle.y := G.Random(screen.h - 1 - circle.h);
  400. form.do.add(form, circle)
  401. END;
  402. edit := NewEdit();
  403. edit.do.setBounds(edit, 10, screen.h - 30, 80, 20);
  404. edit.text := 'World';
  405. form.do.add(form, edit);
  406. G.ClearScreen;
  407. needFlip := TRUE
  408. END Init;
  409. BEGIN
  410. NEW(method);
  411. method.draw := NIL;
  412. method.mouseDown := NIL;
  413. method.keyDown := NIL;
  414. method.add := NIL;
  415. method.updated := UpdatedComponent;
  416. method.redraw := RedrawComponent;
  417. method.setX := ComponentSetX;
  418. method.setY := ComponentSetY;
  419. method.setW := ComponentSetW;
  420. method.setH := ComponentSetH;
  421. method.setBounds := ComponentSetBounds;
  422. method.setVisible := ComponentSetVisible;
  423. method.setOnDraw := ComponentSetOnDraw;
  424. method.setOnMouseDown := ComponentSetOnMouseDown;
  425. method.setOnKeyDown := ComponentSetOnKeyDown;
  426. NEW(formMethod);
  427. formMethod^ := method^;
  428. formMethod.draw := DrawForm;
  429. formMethod.mouseDown := MouseDownContainer;
  430. formMethod.add := AddToContainer;
  431. formMethod.redraw := RedrawContainer;
  432. NEW(buttonMethod);
  433. buttonMethod^ := method^;
  434. buttonMethod.draw := DrawButton;
  435. buttonMethod.mouseDown := MouseDownButton;
  436. NEW (editMethod);
  437. editMethod^ := method^;
  438. editMethod.draw := DrawEdit;
  439. editMethod.mouseDown := MouseDownEdit;
  440. editMethod.keyDown := KeyDownEdit;
  441. NEW(circleMethod);
  442. circleMethod^ := method^;
  443. circleMethod.draw := DrawCircle;
  444. circleMethod.mouseDown := MouseDownCircle;
  445. circleMethod.add := NIL;
  446. circleMethod.updated := UpdatedComponent;
  447. circleMethod.redraw := RedrawComponent;
  448. G.Settings(320, 200, {G.fullscreen, G.spread, G.sharpPixels, G.initMouse});
  449. (* G.Settings(640, 480, {G.fullscreen, G.spread, G.sharpPixels, G.initMouse});*)
  450. screen := G.Init();
  451. IF screen # NIL THEN
  452. font := G.LoadFont('data/images/font.bmp', charW, charH);
  453. IF font # NIL THEN
  454. Init;
  455. Run
  456. ELSE
  457. Out.String('Could not load font.'); Out.Ln
  458. END
  459. ELSE Out.String('Graphics init failed.'); Out.Ln
  460. END;
  461. G.Close()
  462. END Gui.