SimpleGui.Mod 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  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*, btn*: INTEGER END;
  8. MouseDownMsg* = RECORD(Message) x*, y*, btn*: INTEGER END;
  9. MouseUpMsg* = RECORD(Message) x*, y*, btn*: INTEGER END;
  10. MouseEnterMsg* = RECORD(Message) END;
  11. MouseLeaveMsg* = RECORD(Message) END;
  12. ClickMsg* = RECORD(Message) END;
  13. GetFocusMsg* = RECORD(Message) END;
  14. LostFocusMsg* = RECORD(Message) END;
  15. KeyDownMsg* = RECORD(Message) key*: INTEGER END;
  16. KeyUpMsg* = RECORD(Message) key*: INTEGER END;
  17. CharMsg* = RECORD(Message)
  18. key*: INTEGER;
  19. ch*: CHAR;
  20. mod*: SET;
  21. repeat*: BOOLEAN
  22. END;
  23. Handler* = PROCEDURE (c: Widget; VAR msg: Message);
  24. WidgetDesc* = RECORD
  25. x*, y*, w*, h*: INTEGER;
  26. bgColor*, fgColor*: G.Color;
  27. focusable*: BOOLEAN; (** TRUE if widget can get focus *)
  28. focused*: BOOLEAN; (** TRUE if this widget is globally in focus *)
  29. hovered*: BOOLEAN; (** TRUE if mouse pointer is over the widget *)
  30. pressed*: BOOLEAN; (** TRUE if widget is held down with LMB *)
  31. body*: Widget; (** Ring *)
  32. prev*, next*, parent*: Widget;
  33. handle*: Handler;
  34. onMouseDown*: PROCEDURE (c: Widget; x, y, btn: INTEGER);
  35. onMouseUp*: PROCEDURE (c: Widget; x, y, btn: INTEGER);
  36. onMouseMove*: PROCEDURE (c: Widget; x, y, btn: INTEGER);
  37. onMouseEnter*: PROCEDURE (c: Widget);
  38. onMouseLeave*: PROCEDURE (c: Widget);
  39. onClick*: PROCEDURE (c: Widget);
  40. onKeyDown*: PROCEDURE (c: Widget; key: INTEGER);
  41. onKeyUp*: PROCEDURE (c: Widget; key: INTEGER);
  42. onChar*: PROCEDURE (c: Widget; key: INTEGER; ch: CHAR; mod: SET; repeat: BOOLEAN);
  43. END;
  44. Panel* = POINTER TO PanelDesc;
  45. PanelDesc* = RECORD(WidgetDesc) END;
  46. Form* = POINTER TO FormDesc;
  47. FormDesc* = RECORD(PanelDesc) END;
  48. Button* = POINTER TO ButtonDesc;
  49. ButtonDesc* = RECORD(WidgetDesc)
  50. caption*: ARRAY 64 OF CHAR
  51. ;X*, Y*: INTEGER
  52. END;
  53. Edit* = POINTER TO EditDesc;
  54. EditDesc* = RECORD(WidgetDesc)
  55. text*: ARRAY 256 OF CHAR;
  56. len*: INTEGER; (** Length of text in characters *)
  57. pos*: INTEGER; (** Position of text carret, in range [0; len] *)
  58. off*: INTEGER (** Used to slide text that does not fit, normal is 0 *)
  59. END;
  60. VAR
  61. Done*: BOOLEAN; (** FALSE after a failed opration and before the next Init *)
  62. forms*: Widget;
  63. focusedWidget*: Widget; (** The widget with focus = TRUE *)
  64. font*: G.Font;
  65. quit: BOOLEAN; (** Main loop in procedure Run ends when TRUE *)
  66. hoveredWidget: Widget;
  67. pressedWidget: Widget;
  68. pressedX, pressedY: INTEGER;
  69. (** Widget **)
  70. PROCEDURE FindHoveredInList(list: Widget; x, y: INTEGER;
  71. forMouseDown: BOOLEAN): Widget;
  72. VAR c: Widget;
  73. BEGIN
  74. IF list # NIL THEN
  75. c := list.prev;
  76. WHILE (c # NIL) &
  77. ~((c.x <= x) & (x < c.x + c.w) &
  78. (c.y <= y) & (y < c.y + c.h))
  79. DO
  80. IF c = list THEN c := NIL ELSE c := c.prev END
  81. END;
  82. IF forMouseDown & (c # NIL) THEN
  83. INC(pressedX, c.x); INC(pressedY, c.y)
  84. END
  85. ELSE c := NIL
  86. END
  87. RETURN c END FindHoveredInList;
  88. PROCEDURE WidgetOnMouseEnter*(c: Widget);
  89. VAR msg: MouseEnterMsg;
  90. BEGIN
  91. IF pressedWidget = c THEN c.pressed := TRUE END;
  92. c.handle(c, msg)
  93. END WidgetOnMouseEnter;
  94. PROCEDURE WidgetOnMouseLeave*(c: Widget);
  95. VAR msg: MouseLeaveMsg;
  96. BEGIN
  97. c.pressed := FALSE;
  98. c.handle(c, msg)
  99. END WidgetOnMouseLeave;
  100. PROCEDURE WidgetOnMouseMove*(c: Widget; x, y, btn: INTEGER);
  101. VAR msg: MouseMoveMsg;
  102. BEGIN
  103. IF (0 <= x) & (x < c.w) & (0 <= y) & (y < c.h) THEN
  104. IF c # hoveredWidget THEN
  105. IF hoveredWidget # NIL THEN WidgetOnMouseLeave(hoveredWidget) END;
  106. hoveredWidget := c;
  107. WidgetOnMouseEnter(hoveredWidget)
  108. END
  109. ELSIF c = hoveredWidget THEN
  110. WidgetOnMouseLeave(c);
  111. hoveredWidget := NIL
  112. END;
  113. msg.x := x; msg.y := y; msg.btn := btn;
  114. c.handle(c, msg);
  115. IF c.onMouseMove # NIL THEN c.onMouseMove(c, x, y, btn) END
  116. END WidgetOnMouseMove;
  117. PROCEDURE WidgetHandleMouseMove*(c: Widget; x, y, btn: INTEGER);
  118. VAR p: Widget;
  119. BEGIN
  120. IF pressedWidget # NIL THEN
  121. WidgetOnMouseMove(pressedWidget, x - pressedX, y - pressedY, btn)
  122. ELSE
  123. p := FindHoveredInList(c.body, x, y, FALSE);
  124. IF p # NIL THEN
  125. WidgetHandleMouseMove(p, x - p.x, y - p.y, btn)
  126. ELSE
  127. WidgetOnMouseMove(c, x, y, btn)
  128. END
  129. END
  130. END WidgetHandleMouseMove;
  131. PROCEDURE Focus*(c: Widget);
  132. VAR get: GetFocusMsg;
  133. lost: LostFocusMsg;
  134. BEGIN
  135. IF c.focusable THEN
  136. IF focusedWidget # NIL THEN
  137. focusedWidget.focused := FALSE;
  138. focusedWidget.handle(focusedWidget, lost)
  139. END;
  140. c.focused := TRUE;
  141. focusedWidget := c;
  142. focusedWidget.handle(focusedWidget, get)
  143. END
  144. END Focus;
  145. PROCEDURE WidgetOnMouseDown*(c: Widget; x, y, btn: INTEGER);
  146. VAR msg: MouseDownMsg;
  147. BEGIN
  148. pressedWidget := c;
  149. Focus(c);
  150. msg.x := x; msg.y := y; msg.btn := btn;
  151. c.handle(c, msg);
  152. IF c.onMouseDown # NIL THEN c.onMouseDown(c, x, y, btn) END
  153. END WidgetOnMouseDown;
  154. PROCEDURE WidgetHandleMouseDown*(c: Widget; x, y, btn: INTEGER);
  155. VAR p: Widget;
  156. BEGIN
  157. p := FindHoveredInList(c.body, x, y, TRUE);
  158. IF p # NIL THEN
  159. WidgetHandleMouseDown(p, x - p.x, y - p.y, btn)
  160. ELSE
  161. WidgetOnMouseDown(c, x, y, btn)
  162. END
  163. END WidgetHandleMouseDown;
  164. PROCEDURE WidgetOnMouseUp*(c: Widget; x, y, btn: INTEGER);
  165. VAR msg: MouseUpMsg;
  166. BEGIN
  167. pressedWidget := NIL;
  168. msg.x := x; msg.y := y; msg.btn := btn;
  169. c.handle(c, msg);
  170. IF c.onMouseUp # NIL THEN c.onMouseUp(c, x, y, btn) END
  171. END WidgetOnMouseUp;
  172. PROCEDURE WidgetOnClick*(c: Widget);
  173. VAR msg: ClickMsg;
  174. BEGIN c.handle(c, msg);
  175. IF c.onClick # NIL THEN c.onClick(c) END
  176. END WidgetOnClick;
  177. PROCEDURE WidgetHandler*(c: Widget; VAR msg: Message);
  178. VAR x, y: INTEGER;
  179. BEGIN
  180. IF msg IS DrawMsg THEN
  181. x := msg(DrawMsg).x; y := msg(DrawMsg).y;
  182. G.FillRect(x, y, x + c.w - 1, y + c.h - 1, c.bgColor);
  183. G.Rect(x, y, x + c.w - 1, y + c.h - 1, c.fgColor);
  184. G.Rect(x + 2, y + 2, x + c.w - 3, y + c.h - 3, c.fgColor)
  185. END
  186. END WidgetHandler;
  187. PROCEDURE DrawWidget*(c: Widget; x, y, w, h: INTEGER);
  188. VAR M: DrawMsg;
  189. BEGIN
  190. M.x := x; M.y := y; M.w := w; M.h := h;
  191. c.handle(c, M)
  192. END DrawWidget;
  193. PROCEDURE DrawBody*(c: Widget; x, y, w, h: INTEGER);
  194. VAR p: Widget;
  195. x2, y2, w2, h2: INTEGER;
  196. cx, cy, cw, ch: INTEGER;
  197. BEGIN
  198. p := c.body;
  199. IF p # NIL THEN
  200. REPEAT
  201. x2 := x + p.x; y2 := y + p.y;
  202. w2 := w - p.x; h2 := h - p.y;
  203. cx := x2; cy := y2; cw := p.w; ch := p.h;
  204. IF cx + cw > x + w THEN cw := x + w - cx END;
  205. IF cy + ch > y + h THEN ch := y + h - cy END;
  206. IF cx < x THEN DEC(cw, x - cx); cx := x END;
  207. IF cy < y THEN DEC(ch, y - cy); cy := y END;
  208. G.SetClip(cx, cy, cw, ch);
  209. DrawWidget(p, x2, y2, p.w, p.h);
  210. p := p.next
  211. UNTIL p = c.body;
  212. G.UnsetClip
  213. END
  214. END DrawBody;
  215. PROCEDURE SetBgColor*(c: Widget; color: G.Color);
  216. BEGIN c.bgColor := color
  217. END SetBgColor;
  218. PROCEDURE SetFgColor*(c: Widget; color: G.Color);
  219. BEGIN c.fgColor := color
  220. END SetFgColor;
  221. PROCEDURE SetOnMouseMove*(c: Widget; proc: PROCEDURE (c: Widget; x, y, btn: INTEGER));
  222. BEGIN c.onMouseMove := proc
  223. END SetOnMouseMove;
  224. PROCEDURE SetOnMouseDown*(c: Widget; proc: PROCEDURE (c: Widget; x, y, btn: INTEGER));
  225. BEGIN c.onMouseDown := proc
  226. END SetOnMouseDown;
  227. PROCEDURE SetOnMouseUp*(c: Widget; proc: PROCEDURE (c: Widget; x, y, btn: INTEGER));
  228. BEGIN c.onMouseUp := proc
  229. END SetOnMouseUp;
  230. PROCEDURE SetOnClick*(c: Widget; proc: PROCEDURE (c: Widget));
  231. BEGIN c.onClick := proc
  232. END SetOnClick;
  233. PROCEDURE InitWidget*(c: Widget; w, h: INTEGER);
  234. BEGIN c.x := 0; c.y := 0; c.w := w; c.h := h;
  235. c.focusable := FALSE; c.focused := FALSE;
  236. c.hovered := FALSE; c.pressed := FALSE;
  237. G.MakeCol(c.bgColor, 180, 180, 180);
  238. G.MakeCol(c.fgColor, 0, 0, 0);
  239. c.handle := WidgetHandler
  240. END InitWidget;
  241. PROCEDURE AppendToRing*(c: Widget; VAR ring: Widget);
  242. BEGIN
  243. IF ring = NIL THEN
  244. ring := c;
  245. c.prev := c; c.next := c
  246. ELSE
  247. c.next := ring; c.prev := ring.prev;
  248. ring.prev.next := c; ring.prev := c
  249. END
  250. END AppendToRing;
  251. PROCEDURE Put*(c, where: Widget; x, y: INTEGER);
  252. VAR p: Widget;
  253. BEGIN
  254. IF (c # NIL) & (where # NIL) THEN
  255. c.x := x; c.y := y;
  256. AppendToRing(c, where.body)
  257. END
  258. END Put;
  259. (** Panel **)
  260. PROCEDURE PanelHandler*(c: Widget; VAR msg: Message);
  261. VAR x, y: INTEGER;
  262. BEGIN
  263. IF msg IS DrawMsg THEN
  264. x := msg(DrawMsg).x; y := msg(DrawMsg).y;
  265. G.FillRect(x, y, x + c.w - 1, y + c.h - 1, c.bgColor);
  266. DrawBody(c, x, y, c.w, c.h)
  267. ELSE WidgetHandler(c, msg)
  268. END
  269. END PanelHandler;
  270. PROCEDURE InitPanel*(c: Panel; where: Widget; x, y, w, h: INTEGER);
  271. BEGIN InitWidget(c, w, h);
  272. c.handle := PanelHandler;
  273. Put(c, where, x, y)
  274. END InitPanel;
  275. PROCEDURE NewPanel*(where: Widget; x, y, w, h: INTEGER): Panel;
  276. VAR c: Panel;
  277. BEGIN NEW(c); InitPanel(c, where, x, y, w, h)
  278. RETURN c END NewPanel;
  279. (** Form **)
  280. PROCEDURE DrawForm*(c: Form);
  281. BEGIN
  282. G.FillRect(c.x, c.y, c.x + c.w - 1, c.y + c.h - 1, c.bgColor);
  283. DrawBody(c, c.x, c.y, c.w, c.h)
  284. END DrawForm;
  285. PROCEDURE FormHandler*(c: Widget; VAR msg: Message);
  286. BEGIN WidgetHandler(c, msg)
  287. END FormHandler;
  288. PROCEDURE InitForm*(c: Form; x, y, w, h: INTEGER);
  289. BEGIN InitPanel(c, NIL, x, y, w, h);
  290. c.handle := FormHandler;
  291. AppendToRing(c, forms)
  292. END InitForm;
  293. PROCEDURE NewForm*(x, y, w, h: INTEGER): Form;
  294. VAR c: Form;
  295. BEGIN NEW(c); InitForm(c, x, y, w, h);
  296. RETURN c END NewForm;
  297. (** Button **)
  298. PROCEDURE DrawButton*(c: Button; x, y, w, h: INTEGER);
  299. VAR fw, fh, tw, tx, ty: INTEGER;
  300. down: BOOLEAN;
  301. Z: G.Color;
  302. BEGIN
  303. down := c.pressed & c.hovered;
  304. G.FillRect(x, y, x + c.w - 1, y + c.h - 1, c.bgColor);
  305. ;G.MakeCol(Z, 255, 128, 0);
  306. ;G.Line(x + c.h DIV 4, y + c.h DIV 2, x + c.X, y + c.Y, Z);
  307. ;G.MakeCol(Z, 215, 0, 0);
  308. ;G.Line(x + c.h DIV 4, y + c.h DIV 2 + 1, x + c.X, y + c.Y + 1, Z);
  309. G.Rect(x, y, x + c.w - 1, y + c.h - 1, c.fgColor);
  310. IF ~down THEN
  311. G.Rect(x, y, x + c.w - 2, y + c.h - 2, c.fgColor)
  312. END;
  313. G.GetMonoFontSize(font, fw, fh);
  314. tw := Strings.Length(c.caption) * fw;
  315. tx := x + (c.w - tw) DIV 2;
  316. ty := y + (c.h - fh) DIV 2;
  317. IF down THEN INC(tx); INC(ty) END;
  318. G.DrawString(c.caption, tx, ty, font, c.fgColor)
  319. END DrawButton;
  320. PROCEDURE BMM(c: Button; x, y: INTEGER);
  321. BEGIN
  322. c.X := x; c.Y := y
  323. END BMM;
  324. PROCEDURE ButtonHandler*(c: Widget; VAR msg: Message);
  325. VAR b: Button;
  326. BEGIN b := c(Button);
  327. IF msg IS DrawMsg THEN
  328. DrawButton(b, msg(DrawMsg).x, msg(DrawMsg).y,
  329. msg(DrawMsg).w, msg(DrawMsg).h)
  330. ELSIF msg IS MouseMoveMsg THEN BMM(b, msg(MouseMoveMsg).x, msg(MouseMoveMsg).y)
  331. ELSIF msg IS MouseEnterMsg THEN b.hovered := TRUE
  332. ELSIF msg IS MouseLeaveMsg THEN b.hovered := FALSE
  333. ELSIF msg IS MouseDownMsg THEN
  334. IF msg(MouseDownMsg).btn = 1 THEN b.pressed := TRUE END
  335. ELSIF msg IS MouseUpMsg THEN b.pressed := FALSE
  336. ELSE WidgetHandler(c, msg)
  337. END
  338. END ButtonHandler;
  339. PROCEDURE InitButton*(c: Button; where: Widget;
  340. x, y, w, h: INTEGER; caption: ARRAY OF CHAR);
  341. BEGIN InitWidget(c, w, h);
  342. Strings.Copy(caption, c.caption);
  343. c.focusable := TRUE;
  344. c.handle := ButtonHandler;
  345. Put(c, where, x, y)
  346. ;c.X := 0; c.Y := 0;
  347. END InitButton;
  348. PROCEDURE NewButton*(where: Widget; x, y, w, h: INTEGER; caption: ARRAY OF CHAR): Button;
  349. VAR c: Button;
  350. BEGIN NEW(c); InitButton(c, where, x, y, w, h, caption)
  351. RETURN c END NewButton;
  352. (** Edit **)
  353. PROCEDURE DrawEdit*(c: Edit; x, y, w, h: INTEGER);
  354. VAR fw, fh, tw, tx, ty: INTEGER;
  355. down: BOOLEAN;
  356. red: G.Color;
  357. BEGIN
  358. G.FillRect(x, y, x + c.w - 1, y + c.h - 1, c.bgColor);
  359. G.GetMonoFontSize(font, fw, fh);
  360. tw := Strings.Length(c.text) * fw;
  361. tx := x + 2 - c.off;
  362. ty := y + (c.h - fh) DIV 2;
  363. G.DrawString(c.text, tx, ty, font, c.fgColor);
  364. IF c.focused THEN
  365. G.MakeCol(red, 250, 0, 0);
  366. INC(tx, fw * c.pos - 1);
  367. G.VLine(tx, ty, ty + fh - 1, red);
  368. G.HLine(tx - 1, ty, tx + 1, red);
  369. G.HLine(tx - 1, ty + fh - 1, tx + 1, red)
  370. END;
  371. G.Rect(x, y, x + c.w - 1, y + c.h - 1, c.fgColor)
  372. END DrawEdit;
  373. PROCEDURE EditOnMouseDown*(c: Edit; VAR msg: MouseDownMsg);
  374. VAR n, fw, fh: INTEGER;
  375. BEGIN
  376. IF (msg.btn = 1) & (msg.x > 0) & (msg.x < c.w - 1) &
  377. (msg.y > 0) & (msg.y < c.h - 1)
  378. THEN
  379. G.GetMonoFontSize(font, fw, fh);
  380. n := (msg.x - 2 + fw DIV 2) DIV fw;
  381. IF n < 0 THEN n := 0 ELSIF n > c.len THEN n := c.len END;
  382. c.pos := n
  383. END
  384. END EditOnMouseDown;
  385. PROCEDURE EditCheckOffset(c: Edit);
  386. VAR n, fw, fh: INTEGER;
  387. BEGIN
  388. G.GetMonoFontSize(font, fw, fh);
  389. n := c.pos * fw - c.off;
  390. IF c.len * fw <= c.w - 4 THEN c.off := 0
  391. ELSIF n < 0 THEN c.off := c.pos * fw
  392. ELSIF n >= c.w - 4 THEN c.off := c.pos * fw - c.w + 4
  393. ELSIF c.len * fw - c.off <= c.w - 4 THEN c.off := c.len * fw - c.w + 4
  394. END
  395. END EditCheckOffset;
  396. PROCEDURE EditOnChar*(c: Edit; VAR msg: CharMsg);
  397. VAR i: INTEGER;
  398. BEGIN
  399. IF msg.key = G.kBackspace THEN
  400. IF c.pos > 0 THEN
  401. Strings.Delete(c.text, c.pos - 1, 1);
  402. DEC(c.len); DEC(c.pos)
  403. END
  404. ELSIF msg.key = G.kDel THEN
  405. IF c.pos < c.len THEN
  406. Strings.Delete(c.text, c.pos, 1);
  407. DEC(c.len)
  408. END
  409. ELSIF msg.ch < ' ' THEN
  410. IF msg.key = G.kLeft THEN DEC(c.pos)
  411. ELSIF msg.key = G.kRight THEN INC(c.pos)
  412. ELSIF msg.key = G.kHome THEN c.pos := 0
  413. ELSIF msg.key = G.kEnd THEN c.pos := c.len
  414. END;
  415. IF c.pos < 0 THEN c.pos := 0 ELSIF c.pos > c.len THEN c.pos := c.len END
  416. ELSIF c.len < LEN(c.text) - 1 THEN
  417. c.text[c.len + 1] := 0X;
  418. i := c.len;
  419. WHILE i > c.pos DO
  420. c.text[i] := c.text[i - 1];
  421. DEC(i)
  422. END;
  423. c.text[c.pos] := msg.ch;
  424. INC(c.len); INC(c.pos)
  425. END;
  426. EditCheckOffset(c)
  427. END EditOnChar;
  428. PROCEDURE EditHandler*(c: Widget; VAR msg: Message);
  429. VAR e: Edit;
  430. BEGIN e := c(Edit);
  431. IF msg IS DrawMsg THEN
  432. DrawEdit(e, msg(DrawMsg).x, msg(DrawMsg).y,
  433. msg(DrawMsg).w, msg(DrawMsg).h)
  434. ELSIF msg IS MouseDownMsg THEN EditOnMouseDown(e, msg(MouseDownMsg))
  435. ELSIF msg IS CharMsg THEN EditOnChar(e, msg(CharMsg))
  436. ELSE WidgetHandler(c, msg)
  437. END
  438. END EditHandler;
  439. PROCEDURE InitEdit*(c: Edit; where: Widget; x, y, w, h: INTEGER);
  440. BEGIN InitWidget(c, w, h);
  441. c.focusable := TRUE;
  442. G.MakeCol(c.bgColor, 255, 255, 255);
  443. c.text := 'Привет'; c.len := 6; c.pos := 2; c.off := 0;
  444. c.handle := EditHandler;
  445. Put(c, where, x, y)
  446. END InitEdit;
  447. PROCEDURE NewEdit*(where: Widget; x, y, w, h: INTEGER): Edit;
  448. VAR c: Edit;
  449. BEGIN NEW(c); InitEdit(c, where, x, y, w, h)
  450. RETURN c END NewEdit;
  451. (** General **)
  452. PROCEDURE DrawAll*;
  453. VAR c: Widget;
  454. BEGIN
  455. c := forms;
  456. REPEAT
  457. DrawForm(c(Form));
  458. c := c.next
  459. UNTIL c = forms;
  460. G.Flip
  461. END DrawAll;
  462. PROCEDURE HandleMouseMove(VAR e: G.Event);
  463. VAR c: Widget;
  464. BEGIN
  465. c := FindHoveredInList(forms, e.x, e.y, FALSE);
  466. IF c # NIL THEN
  467. WidgetHandleMouseMove(c, e.x - c.x, e.y - c.y, e.button)
  468. END
  469. END HandleMouseMove;
  470. PROCEDURE HandleMouseDown(VAR e: G.Event);
  471. VAR c: Widget;
  472. BEGIN
  473. pressedX := 0; pressedY := 0;
  474. c := FindHoveredInList(forms, e.x, e.y, TRUE);
  475. IF c # NIL THEN
  476. WidgetHandleMouseDown(c, e.x - c.x, e.y - c.y, e.button)
  477. END
  478. END HandleMouseDown;
  479. PROCEDURE HandleMouseUp(VAR e: G.Event);
  480. VAR c: Widget;
  481. BEGIN
  482. IF pressedWidget # NIL THEN
  483. c := pressedWidget;
  484. IF ~c.hovered THEN c := NIL END;
  485. WidgetOnMouseUp(pressedWidget, e.x - pressedX, e.y - pressedY, e.button);
  486. IF (c # NIL) & (e.button = 1) THEN
  487. WidgetOnClick(c)
  488. END
  489. END
  490. END HandleMouseUp;
  491. PROCEDURE HandleKeyDown(VAR e: G.Event);
  492. VAR msg: KeyDownMsg;
  493. BEGIN
  494. IF focusedWidget # NIL THEN
  495. msg.key := e.key;
  496. focusedWidget.handle(focusedWidget, msg)
  497. END
  498. END HandleKeyDown;
  499. PROCEDURE HandleKeyUp(VAR e: G.Event);
  500. VAR msg: KeyUpMsg;
  501. BEGIN
  502. IF focusedWidget # NIL THEN
  503. msg.key := e.key;
  504. focusedWidget.handle(focusedWidget, msg)
  505. END
  506. END HandleKeyUp;
  507. PROCEDURE HandleChar(VAR e: G.Event);
  508. VAR msg: CharMsg;
  509. BEGIN
  510. IF focusedWidget # NIL THEN
  511. msg.key := e.key; msg.ch := e.ch;
  512. msg.mod := e.mod; msg.repeat := e.repeat;
  513. focusedWidget.handle(focusedWidget, msg)
  514. END
  515. END HandleChar;
  516. PROCEDURE HandleEvent(VAR e: G.Event);
  517. BEGIN
  518. IF e.type = G.quit THEN quit := TRUE
  519. ELSIF e.type = G.mouseMove THEN HandleMouseMove(e)
  520. ELSIF e.type = G.mouseDown THEN HandleMouseDown(e)
  521. ELSIF e.type = G.mouseUp THEN HandleMouseUp(e)
  522. ELSIF e.type = G.keyDown THEN HandleKeyDown(e)
  523. ELSIF e.type = G.keyUp THEN HandleKeyUp(e)
  524. ELSIF e.type = G.char THEN HandleChar(e)
  525. END
  526. END HandleEvent;
  527. PROCEDURE Run*;
  528. VAR e: G.Event;
  529. BEGIN
  530. quit := FALSE;
  531. REPEAT
  532. WHILE G.HasEvents() DO
  533. G.WaitEvent(e);
  534. HandleEvent(e)
  535. END;
  536. DrawAll
  537. UNTIL quit
  538. END Run;
  539. PROCEDURE Init*;
  540. BEGIN
  541. forms := NIL;
  542. font := G.LoadFont('Data/Fonts/Main');
  543. IF font = NIL THEN Out.String('SimpleGui: could not load font.'); Out.Ln END;
  544. Done := font # NIL;
  545. hoveredWidget := NIL; pressedWidget := NIL;
  546. pressedX := 0; pressedY := 0
  547. END Init;
  548. END SimpleGui.