SimpleGui.Mod 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132
  1. MODULE SimpleGui;
  2. IMPORT G := Graph, Strings, Out;
  3. TYPE
  4. Widget* = POINTER TO WidgetDesc;
  5. Message* = RECORD END;
  6. PutMsg* = RECORD(Message) what*: Widget; x*, y*: INTEGER END;
  7. DrawMsg* = RECORD(Message) x*, y*, w*, h*: INTEGER END;
  8. MouseMoveMsg* = RECORD(Message) x*, y*: INTEGER; btns*: SET END;
  9. MouseDownMsg* = RECORD(Message) x*, y*, btn*: INTEGER END;
  10. MouseUpMsg* = RECORD(Message) x*, y*, btn*: INTEGER END;
  11. MouseEnterMsg* = RECORD(Message) END;
  12. MouseLeaveMsg* = RECORD(Message) END;
  13. ClickMsg* = RECORD(Message) END;
  14. GetFocusMsg* = RECORD(Message) END;
  15. LostFocusMsg* = RECORD(Message) END;
  16. KeyDownMsg* = RECORD(Message) key*: INTEGER END;
  17. KeyUpMsg* = RECORD(Message) key*: INTEGER END;
  18. CharMsg* = RECORD(Message)
  19. key*: INTEGER;
  20. ch*: CHAR;
  21. mod*: SET;
  22. repeat*: BOOLEAN
  23. END;
  24. Handler* = PROCEDURE (c: Widget; VAR msg: Message);
  25. WidgetDesc* = RECORD
  26. x*, y*, w*, h*: INTEGER;
  27. bgColor*, fgColor*: G.Color;
  28. focusable*: BOOLEAN; (** TRUE if widget can get focus *)
  29. focused*: BOOLEAN; (** TRUE if this widget is globally in focus *)
  30. hovered*: BOOLEAN; (** TRUE if mouse pointer is over the widget *)
  31. pressed*: BOOLEAN; (** TRUE if widget is held down with LMB *)
  32. body*: Widget; (** A ring of widgets that this widget contains *)
  33. parent*: Widget; (** A widget that this widget is contained in *)
  34. prev*, next*: Widget;
  35. handle*: Handler;
  36. onPaint*: PROCEDURE (c: Widget; x, y, w, h: INTEGER);
  37. onMouseDown*: PROCEDURE (c: Widget; x, y, btn: INTEGER);
  38. onMouseUp*: PROCEDURE (c: Widget; x, y, btn: INTEGER);
  39. onMouseMove*: PROCEDURE (c: Widget; x, y: INTEGER; btns: SET);
  40. onMouseEnter*: PROCEDURE (c: Widget);
  41. onMouseLeave*: PROCEDURE (c: Widget);
  42. onClick*: PROCEDURE (c: Widget);
  43. onKeyDown*: PROCEDURE (c: Widget; key: INTEGER);
  44. onKeyUp*: PROCEDURE (c: Widget; key: INTEGER);
  45. onChar*: PROCEDURE (c: Widget; key: INTEGER; ch: CHAR; mod: SET; repeat: BOOLEAN);
  46. END;
  47. App* = POINTER TO AppDesc;
  48. AppDesc* = RECORD(WidgetDesc) END;
  49. Form* = POINTER TO FormDesc;
  50. FormDesc* = RECORD(WidgetDesc) END;
  51. Panel* = POINTER TO PanelDesc;
  52. PanelDesc* = RECORD(WidgetDesc)
  53. noBg*: BOOLEAN
  54. END;
  55. Button* = POINTER TO ButtonDesc;
  56. ButtonDesc* = RECORD(WidgetDesc)
  57. caption*: ARRAY 64 OF CHAR
  58. END;
  59. Edit* = POINTER TO EditDesc;
  60. EditDesc* = RECORD(WidgetDesc)
  61. text*: ARRAY 256 OF CHAR;
  62. len*: INTEGER; (** Length of text in characters *)
  63. pos*: INTEGER; (** Position of text carret, in range [0; len] *)
  64. off*: INTEGER (** Used to slide text that does not fit, normal is 0 *)
  65. END;
  66. ScrollBar* = POINTER TO ScrollBarDesc;
  67. ScrollBarDesc* = RECORD(WidgetDesc)
  68. vertical*: BOOLEAN; (** TRUE for vertical scroll, FALSE for horizontal *)
  69. min*, max*: INTEGER;
  70. value*: INTEGER; (** The position of the scroll, in range [min; max] *)
  71. inc*, bigInc*: INTEGER; (** A single increment of value, and a big one *)
  72. btnSize*: INTEGER; (** Width or height (depending on vertical) of buttons *)
  73. handlePos*, handleSize*: INTEGER; (** Size and position of handle, px *)
  74. handlePressed*: BOOLEAN;
  75. handlePressPos*: INTEGER; (** Where handle was pressed, offset in px *)
  76. btnPressed*: INTEGER; (** 0-nothing, 1-less btn, 2-more btn, 3-handle *)
  77. onScroll*: PROCEDURE (c: ScrollBar; value: INTEGER);
  78. END;
  79. ScrollBox* = POINTER TO ScrollBoxDesc;
  80. ScrollBoxDesc* = RECORD(WidgetDesc)
  81. noBg*: BOOLEAN;
  82. outer*, inner*: Panel;
  83. scbHoriz*, scbVert*: ScrollBar
  84. END;
  85. Canvas* = POINTER TO CanvasDesc;
  86. CanvasDesc = RECORD(WidgetDesc)
  87. bmp*: G.Bitmap
  88. END;
  89. VAR
  90. Done*: BOOLEAN; (** FALSE after a failed opration and before the next Init *)
  91. app*: App;
  92. focusedWidget*: Widget; (** The widget with focus = TRUE *)
  93. font*: G.Font;
  94. quit: BOOLEAN; (** Main loop in procedure Run ends when TRUE *)
  95. hoveredWidget: Widget;
  96. pressedWidget: Widget;
  97. pressedX, pressedY: INTEGER;
  98. mouseCursor: G.Bitmap;
  99. mouseX, mouseY: INTEGER;
  100. (** Widget **)
  101. PROCEDURE FindHoveredInRing(list: Widget; x, y: INTEGER;
  102. forMouseDown: BOOLEAN): Widget;
  103. VAR c: Widget;
  104. BEGIN
  105. IF list # NIL THEN
  106. c := list.prev;
  107. WHILE (c # NIL) &
  108. ~((c.x <= x) & (x < c.x + c.w) &
  109. (c.y <= y) & (y < c.y + c.h))
  110. DO
  111. IF c = list THEN c := NIL ELSE c := c.prev END
  112. END;
  113. IF forMouseDown & (c # NIL) THEN
  114. INC(pressedX, c.x); INC(pressedY, c.y)
  115. END
  116. ELSE c := NIL
  117. END
  118. RETURN c END FindHoveredInRing;
  119. PROCEDURE WidgetOnMouseEnter*(c: Widget);
  120. VAR msg: MouseEnterMsg;
  121. BEGIN
  122. IF pressedWidget = c THEN c.pressed := TRUE END;
  123. c.hovered := TRUE;
  124. c.handle(c, msg)
  125. END WidgetOnMouseEnter;
  126. PROCEDURE WidgetOnMouseLeave*(c: Widget);
  127. VAR msg: MouseLeaveMsg;
  128. BEGIN
  129. c.hovered := FALSE;
  130. c.pressed := FALSE;
  131. c.handle(c, msg)
  132. END WidgetOnMouseLeave;
  133. PROCEDURE WidgetOnMouseMove*(c: Widget; x, y: INTEGER; btns: SET);
  134. VAR msg: MouseMoveMsg;
  135. BEGIN
  136. IF (0 <= x) & (x < c.w) & (0 <= y) & (y < c.h) THEN
  137. IF c # hoveredWidget THEN
  138. IF hoveredWidget # NIL THEN WidgetOnMouseLeave(hoveredWidget) END;
  139. hoveredWidget := c;
  140. WidgetOnMouseEnter(hoveredWidget)
  141. END
  142. ELSIF c = hoveredWidget THEN
  143. WidgetOnMouseLeave(c);
  144. hoveredWidget := NIL
  145. END;
  146. msg.x := x; msg.y := y; msg.btns := btns;
  147. c.handle(c, msg);
  148. IF c.onMouseMove # NIL THEN c.onMouseMove(c, x, y, btns) END
  149. END WidgetOnMouseMove;
  150. PROCEDURE WidgetHandleMouseMove*(c: Widget; x, y: INTEGER; btns: SET);
  151. VAR p: Widget;
  152. BEGIN
  153. IF pressedWidget # NIL THEN
  154. WidgetOnMouseMove(pressedWidget, x - pressedX, y - pressedY, btns)
  155. ELSE
  156. p := FindHoveredInRing(c.body, x, y, FALSE);
  157. IF p # NIL THEN
  158. WidgetHandleMouseMove(p, x - p.x, y - p.y, btns)
  159. ELSE
  160. WidgetOnMouseMove(c, x, y, btns)
  161. END
  162. END
  163. END WidgetHandleMouseMove;
  164. PROCEDURE Resize*(c: Widget; w, h: INTEGER);
  165. BEGIN
  166. c.w := w;
  167. c.h := h
  168. END Resize;
  169. PROCEDURE Focus*(c: Widget);
  170. VAR get: GetFocusMsg;
  171. lost: LostFocusMsg;
  172. BEGIN
  173. IF c.focusable THEN
  174. IF focusedWidget # NIL THEN
  175. focusedWidget.focused := FALSE;
  176. focusedWidget.handle(focusedWidget, lost)
  177. END;
  178. c.focused := TRUE;
  179. focusedWidget := c;
  180. focusedWidget.handle(focusedWidget, get)
  181. END
  182. END Focus;
  183. PROCEDURE Detach*(c: Widget);
  184. VAR p: Widget;
  185. BEGIN
  186. IF c.parent # NIL THEN
  187. IF c.prev = c THEN
  188. c.parent.body := NIL
  189. ELSE
  190. c.prev.next := c.next;
  191. c.next.prev := c.prev
  192. END;
  193. c.parent := NIL
  194. END;
  195. c.prev := NIL; c.next := NIL
  196. END Detach;
  197. PROCEDURE AppendTo*(c: Widget; container: Widget);
  198. VAR r: Widget;
  199. BEGIN
  200. Detach(c);
  201. c.parent := container;
  202. r := container.body;
  203. IF r = NIL THEN
  204. container.body := c;
  205. c.prev := c; c.next := c
  206. ELSE
  207. c.next := r; c.prev := r.prev;
  208. r.prev.next := c; r.prev := c
  209. END
  210. END AppendTo;
  211. PROCEDURE DirectPut*(c, where: Widget; x, y: INTEGER);
  212. BEGIN
  213. IF c # NIL THEN
  214. c.x := x; c.y := y;
  215. IF where # NIL THEN
  216. AppendTo(c, where)
  217. END
  218. END
  219. END DirectPut;
  220. PROCEDURE Put*(c, where: Widget; x, y: INTEGER);
  221. VAR msg: PutMsg;
  222. BEGIN
  223. IF c # NIL THEN
  224. c.x := x; c.y := y;
  225. IF where # NIL THEN
  226. msg.what := c;
  227. msg.x := x;
  228. msg.y := y;
  229. where.handle(where, msg)
  230. END
  231. END
  232. END Put;
  233. PROCEDURE WidgetOnMouseDown*(c: Widget; x, y, btn: INTEGER);
  234. VAR msg: MouseDownMsg;
  235. BEGIN
  236. pressedWidget := c;
  237. Focus(c);
  238. msg.x := x; msg.y := y; msg.btn := btn;
  239. c.handle(c, msg);
  240. IF c.onMouseDown # NIL THEN c.onMouseDown(c, x, y, btn) END
  241. END WidgetOnMouseDown;
  242. PROCEDURE WidgetHandleMouseDown*(c: Widget; x, y, btn: INTEGER);
  243. VAR p: Widget;
  244. BEGIN
  245. p := FindHoveredInRing(c.body, x, y, TRUE);
  246. IF p # NIL THEN
  247. WidgetHandleMouseDown(p, x - p.x, y - p.y, btn)
  248. ELSE
  249. WidgetOnMouseDown(c, x, y, btn)
  250. END
  251. END WidgetHandleMouseDown;
  252. PROCEDURE WidgetOnMouseUp*(c: Widget; x, y, btn: INTEGER);
  253. VAR msg: MouseUpMsg;
  254. BEGIN
  255. pressedWidget := NIL;
  256. msg.x := x; msg.y := y; msg.btn := btn;
  257. c.handle(c, msg);
  258. IF c.onMouseUp # NIL THEN c.onMouseUp(c, x, y, btn) END
  259. END WidgetOnMouseUp;
  260. PROCEDURE WidgetOnClick*(c: Widget);
  261. VAR msg: ClickMsg;
  262. BEGIN c.handle(c, msg);
  263. IF c.onClick # NIL THEN c.onClick(c) END
  264. END WidgetOnClick;
  265. PROCEDURE WidgetHandler*(c: Widget; VAR msg: Message);
  266. VAR x, y: INTEGER;
  267. BEGIN
  268. IF msg IS DrawMsg THEN
  269. x := msg(DrawMsg).x; y := msg(DrawMsg).y;
  270. IF c.onPaint # NIL THEN
  271. c.onPaint(c, x, y, msg(DrawMsg).w, msg(DrawMsg).h)
  272. ELSE
  273. G.FillRect(x, y, x + c.w - 1, y + c.h - 1, c.bgColor);
  274. G.Rect(x, y, x + c.w - 1, y + c.h - 1, c.fgColor);
  275. G.Rect(x + 2, y + 2, x + c.w - 3, y + c.h - 3, c.fgColor)
  276. END
  277. ELSIF msg IS MouseDownMsg THEN
  278. IF msg(MouseDownMsg).btn = 1 THEN c.pressed := TRUE END
  279. ELSIF msg IS MouseUpMsg THEN c.pressed := FALSE
  280. ELSIF msg IS PutMsg THEN
  281. DirectPut(msg(PutMsg).what, c, msg(PutMsg).x, msg(PutMsg).y)
  282. END
  283. END WidgetHandler;
  284. PROCEDURE DrawWidget*(c: Widget; x, y, w, h: INTEGER);
  285. VAR M: DrawMsg;
  286. BEGIN
  287. M.x := x; M.y := y; M.w := w; M.h := h;
  288. c.handle(c, M)
  289. END DrawWidget;
  290. PROCEDURE DrawBody*(c: Widget; x, y, w, h: INTEGER);
  291. VAR p: Widget;
  292. x2, y2, w2, h2: INTEGER;
  293. cx, cy, cw, ch: INTEGER;
  294. CX, CY, CW, CH: INTEGER;
  295. BEGIN
  296. p := c.body;
  297. IF p # NIL THEN
  298. G.GetClip(CX, CY, CW, CH);
  299. IF CX + CW > x + w THEN CW := x + w - CX END;
  300. IF CY + CH > y + h THEN CH := y + h - CY END;
  301. IF CX < x THEN DEC(CW, x - CX); CX := x END;
  302. IF CY < y THEN DEC(CH, y - CY); CY := y END;
  303. REPEAT
  304. x2 := x + p.x; y2 := y + p.y;
  305. w2 := w - p.x; h2 := h - p.y;
  306. cx := x2; cy := y2; cw := p.w; ch := p.h;
  307. IF cx + cw > CX + CW THEN cw := CX + CW - cx END;
  308. IF cy + ch > CY + CH THEN ch := CY + CH - cy END;
  309. IF cx < CX THEN DEC(cw, CX - cx); cx := CX END;
  310. IF cy < CY THEN DEC(ch, CY - cy); cy := CY END;
  311. G.SetClip(cx, cy, cw, ch);
  312. DrawWidget(p, x2, y2, p.w, p.h);
  313. p := p.next
  314. UNTIL p = c.body;
  315. G.UnsetClip
  316. END
  317. END DrawBody;
  318. PROCEDURE SetBgColor*(c: Widget; color: G.Color);
  319. BEGIN c.bgColor := color
  320. END SetBgColor;
  321. PROCEDURE SetFgColor*(c: Widget; color: G.Color);
  322. BEGIN c.fgColor := color
  323. END SetFgColor;
  324. PROCEDURE SetOnPaint*(c: Widget; proc: PROCEDURE (c: Widget; x, y, w, h: INTEGER));
  325. BEGIN c.onPaint := proc
  326. END SetOnPaint;
  327. PROCEDURE SetOnMouseMove*(c: Widget; proc: PROCEDURE (c: Widget; x, y: INTEGER; btns: SET));
  328. BEGIN c.onMouseMove := proc
  329. END SetOnMouseMove;
  330. PROCEDURE SetOnMouseDown*(c: Widget; proc: PROCEDURE (c: Widget; x, y, btn: INTEGER));
  331. BEGIN c.onMouseDown := proc
  332. END SetOnMouseDown;
  333. PROCEDURE SetOnMouseUp*(c: Widget; proc: PROCEDURE (c: Widget; x, y, btn: INTEGER));
  334. BEGIN c.onMouseUp := proc
  335. END SetOnMouseUp;
  336. PROCEDURE SetOnClick*(c: Widget; proc: PROCEDURE (c: Widget));
  337. BEGIN c.onClick := proc
  338. END SetOnClick;
  339. PROCEDURE InitWidget*(c: Widget; w, h: INTEGER);
  340. BEGIN c.x := 0; c.y := 0; c.w := w; c.h := h;
  341. c.focusable := FALSE; c.focused := FALSE;
  342. c.hovered := FALSE; c.pressed := FALSE;
  343. G.MakeCol(c.bgColor, 180, 180, 180);
  344. G.MakeCol(c.fgColor, 0, 0, 0);
  345. c.handle := WidgetHandler
  346. END InitWidget;
  347. (** Creates and returns a new custom widget *)
  348. PROCEDURE NewWidget*(where: Widget; x, y, w, h: INTEGER): Widget;
  349. VAR c: Widget;
  350. BEGIN NEW(c); InitWidget(c, w, h);
  351. Put(c, where, x, y)
  352. RETURN c END NewWidget;
  353. (** Panel **)
  354. PROCEDURE PanelSetNoBg*(c: Panel; noBg: BOOLEAN);
  355. BEGIN c.noBg := noBg
  356. END PanelSetNoBg;
  357. PROCEDURE PanelHandler*(c: Widget; VAR msg: Message);
  358. VAR x, y: INTEGER;
  359. BEGIN
  360. IF msg IS DrawMsg THEN
  361. x := msg(DrawMsg).x; y := msg(DrawMsg).y;
  362. IF ~c(Panel).noBg THEN
  363. G.FillRect(x, y, x + c.w - 1, y + c.h - 1, c.bgColor)
  364. END;
  365. DrawBody(c, x, y, c.w, c.h)
  366. ELSE WidgetHandler(c, msg)
  367. END
  368. END PanelHandler;
  369. PROCEDURE InitPanel*(c: Panel; where: Widget; x, y, w, h: INTEGER);
  370. BEGIN InitWidget(c, w, h);
  371. c.noBg := FALSE;
  372. c.handle := PanelHandler;
  373. Put(c, where, x, y)
  374. END InitPanel;
  375. PROCEDURE NewPanel*(where: Widget; x, y, w, h: INTEGER): Panel;
  376. VAR c: Panel;
  377. BEGIN NEW(c); InitPanel(c, where, x, y, w, h)
  378. RETURN c END NewPanel;
  379. (** App **)
  380. PROCEDURE InitApp*(c: App);
  381. VAR W, H: INTEGER;
  382. BEGIN
  383. G.GetScreenSize(W, H);
  384. InitWidget(c, W, H)
  385. END InitApp;
  386. PROCEDURE NewApp*(): App;
  387. VAR c: App;
  388. BEGIN NEW(c); InitApp(c)
  389. RETURN c END NewApp;
  390. (** Form **)
  391. PROCEDURE DrawForm*(c: Form);
  392. BEGIN
  393. G.FillRect(c.x, c.y, c.x + c.w - 1, c.y + c.h - 1, c.bgColor);
  394. DrawBody(c, c.x, c.y, c.w, c.h)
  395. END DrawForm;
  396. PROCEDURE FormHandler*(c: Widget; VAR msg: Message);
  397. BEGIN WidgetHandler(c, msg)
  398. END FormHandler;
  399. PROCEDURE InitForm*(c: Form; x, y, w, h: INTEGER);
  400. BEGIN InitWidget(c, w, h);
  401. c.x := x; c.y := y;
  402. c.handle := FormHandler;
  403. AppendTo(c, app)
  404. END InitForm;
  405. PROCEDURE NewForm*(x, y, w, h: INTEGER): Form;
  406. VAR c: Form;
  407. BEGIN NEW(c); InitForm(c, x, y, w, h)
  408. RETURN c END NewForm;
  409. (** Button **)
  410. PROCEDURE MakeOrAndYw(bg: G.Color; VAR or, yw: G.Color);
  411. VAR r, g, b: INTEGER;
  412. BEGIN
  413. G.ColorToRGB(bg, r, g, b);
  414. G.MakeCol(yw, (r + 255 * 2) DIV 3, (g + 255 * 3) DIV 4, (b * 3 + 255) DIV 4);
  415. IF (r <= g) & (r <= b) THEN
  416. g := (g * 2 + 255 * 3) DIV 5;
  417. b := (b * 3 + 255) DIV 4
  418. ELSIF (g <= r) & (g <= b) THEN
  419. r := (r * 2 + 255 * 3) DIV 5;
  420. b := (b * 3 + 255) DIV 4
  421. ELSE
  422. r := (r * 2 + 255 * 3) DIV 5;
  423. g := (g * 3 + 255) DIV 4
  424. END;
  425. G.MakeCol(or, r, g, b)
  426. END MakeOrAndYw;
  427. PROCEDURE DrawButtonBox(x, y, w, h: INTEGER; bg, parentBg: G.Color;
  428. down, glow: BOOLEAN);
  429. VAR wh, bl, g1, g2, or, yw: G.Color;
  430. X, Y: INTEGER;
  431. BEGIN
  432. G.MakeCol(bl, 0, 0, 0);
  433. G.MakeCol(wh, 255, 255, 255);
  434. G.MakeCol(g1, 140, 140, 140);
  435. G.MakeCol(g2, 80, 80, 80);
  436. MakeOrAndYw(parentBg, or, yw);
  437. X := x + w - 1; Y := y + h - 1;
  438. G.FillRect(x + 1, y + 1, X - 2, Y - 2, bg);
  439. G.HLine(x + 2, y, X - 1, bl);
  440. G.HLine(x, Y - 1, X - 4, bl);
  441. G.VLine(x, y + 2, Y - 1, bl);
  442. G.VLine(X - 1, y + 1, Y - 4, bl);
  443. IF ~down THEN
  444. G.HLine(x + 3, y + 1, X - 2, wh);
  445. G.HLine(x + 2, Y - 2, X - 4, g1);
  446. G.VLine(x + 1, y + 3, Y - 2, wh);
  447. G.VLine(X - 2, y + 2, Y - 4, g1);
  448. G.PutPixel(X - 3, Y - 3, g1);
  449. G.Line(X - 4, Y - 3, X - 3, Y - 4, g1);
  450. G.PutPixel(x + 2, y + 2, wh)
  451. END;
  452. G.Line(X - 3, Y - 2, X - 2, Y - 3, bl);
  453. G.Line(X - 3, Y - 1, X - 1, Y - 3, g2);
  454. G.Line(x + 1, y + 2, x + 2, y + 1, g1);
  455. G.PutPixel(x + 1, y + 1, bl);
  456. IF glow THEN
  457. G.Line(X - 2, Y - 1, X - 1, Y - 2, yw);
  458. G.HLine(x + 1, Y, X - 2, or);
  459. G.VLine(X, y + 1, Y - 2, or);
  460. G.PutPixel(X - 1, Y - 1, or)
  461. END
  462. END DrawButtonBox;
  463. PROCEDURE DrawButton*(c: Button; x, y, w, h: INTEGER);
  464. VAR fw, fh, tw, tx, ty: INTEGER;
  465. BEGIN
  466. DrawButtonBox(x, y, c.w, c.h, c.bgColor, c.parent.bgColor,
  467. c.pressed & c.hovered, TRUE);
  468. G.GetMonoFontSize(font, fw, fh);
  469. tw := Strings.Length(c.caption) * fw;
  470. tx := x + (c.w - tw) DIV 2;
  471. ty := y + (c.h - fh) DIV 2;
  472. IF c.pressed & c.hovered THEN INC(tx); INC(ty) END;
  473. G.DrawString(c.caption, tx, ty, font, c.fgColor)
  474. END DrawButton;
  475. PROCEDURE ButtonHandler*(c: Widget; VAR msg: Message);
  476. VAR b: Button;
  477. BEGIN b := c(Button);
  478. IF msg IS DrawMsg THEN
  479. DrawButton(b, msg(DrawMsg).x, msg(DrawMsg).y,
  480. msg(DrawMsg).w, msg(DrawMsg).h)
  481. ELSE WidgetHandler(c, msg)
  482. END
  483. END ButtonHandler;
  484. PROCEDURE InitButton*(c: Button; where: Widget;
  485. x, y, w, h: INTEGER; caption: ARRAY OF CHAR);
  486. BEGIN InitWidget(c, w, h);
  487. Strings.Copy(caption, c.caption);
  488. c.focusable := TRUE;
  489. c.handle := ButtonHandler;
  490. Put(c, where, x, y)
  491. END InitButton;
  492. PROCEDURE NewButton*(where: Widget; x, y, w, h: INTEGER; caption: ARRAY OF CHAR): Button;
  493. VAR c: Button;
  494. BEGIN NEW(c); InitButton(c, where, x, y, w, h, caption)
  495. RETURN c END NewButton;
  496. (** Edit **)
  497. PROCEDURE DrawEdit*(c: Edit; x, y, w, h: INTEGER);
  498. VAR fw, fh, tw, tx, ty: INTEGER;
  499. or, yw: G.Color;
  500. BEGIN
  501. MakeOrAndYw(c.parent.bgColor, or, yw);
  502. G.FillRect(x, y, x + c.w - 1, y + c.h - 1, c.bgColor);
  503. G.GetMonoFontSize(font, fw, fh);
  504. tw := Strings.Length(c.text) * fw;
  505. tx := x + 2 - c.off;
  506. ty := y + (c.h - fh) DIV 2;
  507. G.DrawString(c.text, tx, ty, font, c.fgColor);
  508. IF c.focused THEN
  509. INC(tx, fw * c.pos - 1);
  510. G.VLine(tx, ty, ty + fh - 1, or);
  511. G.HLine(tx - 1, ty, tx + 1, or);
  512. G.HLine(tx - 1, ty + fh - 1, tx + 1, or)
  513. END;
  514. G.HLine(x, y, x + c.w - 2, c.fgColor);
  515. G.VLine(x, y, y + c.h - 1, c.fgColor);
  516. G.HLine(x + 1, y + c.h - 1, x + c.w - 1, or);
  517. G.VLine(x + c.w - 1, y, y + c.h - 1, or)
  518. END DrawEdit;
  519. PROCEDURE EditOnMouseDown*(c: Edit; VAR msg: MouseDownMsg);
  520. VAR n, fw, fh: INTEGER;
  521. BEGIN
  522. IF (msg.btn = 1) & (msg.x > 0) & (msg.x < c.w - 1) &
  523. (msg.y > 0) & (msg.y < c.h - 1)
  524. THEN
  525. G.GetMonoFontSize(font, fw, fh);
  526. n := (msg.x - 2 + fw DIV 2) DIV fw;
  527. IF n < 0 THEN n := 0 ELSIF n > c.len THEN n := c.len END;
  528. c.pos := n
  529. END
  530. END EditOnMouseDown;
  531. PROCEDURE EditCheckOffset(c: Edit);
  532. VAR n, fw, fh: INTEGER;
  533. BEGIN
  534. G.GetMonoFontSize(font, fw, fh);
  535. n := c.pos * fw - c.off;
  536. IF c.len * fw <= c.w - 4 THEN c.off := 0
  537. ELSIF n < 0 THEN c.off := c.pos * fw
  538. ELSIF n >= c.w - 4 THEN c.off := c.pos * fw - c.w + 4
  539. ELSIF c.len * fw - c.off <= c.w - 4 THEN c.off := c.len * fw - c.w + 4
  540. END
  541. END EditCheckOffset;
  542. PROCEDURE EditOnChar*(c: Edit; VAR msg: CharMsg);
  543. VAR i: INTEGER;
  544. BEGIN
  545. IF msg.key = G.kBackspace THEN
  546. IF c.pos > 0 THEN
  547. Strings.Delete(c.text, c.pos - 1, 1);
  548. DEC(c.len); DEC(c.pos)
  549. END
  550. ELSIF msg.key = G.kDel THEN
  551. IF c.pos < c.len THEN
  552. Strings.Delete(c.text, c.pos, 1);
  553. DEC(c.len)
  554. END
  555. ELSIF msg.ch < ' ' THEN
  556. IF msg.key = G.kLeft THEN DEC(c.pos)
  557. ELSIF msg.key = G.kRight THEN INC(c.pos)
  558. ELSIF msg.key = G.kHome THEN c.pos := 0
  559. ELSIF msg.key = G.kEnd THEN c.pos := c.len
  560. END;
  561. IF c.pos < 0 THEN c.pos := 0 ELSIF c.pos > c.len THEN c.pos := c.len END
  562. ELSIF c.len < LEN(c.text) - 1 THEN
  563. c.text[c.len + 1] := 0X;
  564. i := c.len;
  565. WHILE i > c.pos DO
  566. c.text[i] := c.text[i - 1];
  567. DEC(i)
  568. END;
  569. c.text[c.pos] := msg.ch;
  570. INC(c.len); INC(c.pos)
  571. END;
  572. EditCheckOffset(c)
  573. END EditOnChar;
  574. PROCEDURE EditHandler*(c: Widget; VAR msg: Message);
  575. VAR e: Edit;
  576. BEGIN e := c(Edit);
  577. IF msg IS DrawMsg THEN
  578. DrawEdit(e, msg(DrawMsg).x, msg(DrawMsg).y,
  579. msg(DrawMsg).w, msg(DrawMsg).h)
  580. ELSIF msg IS MouseDownMsg THEN EditOnMouseDown(e, msg(MouseDownMsg))
  581. ELSIF msg IS CharMsg THEN EditOnChar(e, msg(CharMsg))
  582. ELSE WidgetHandler(c, msg)
  583. END
  584. END EditHandler;
  585. PROCEDURE InitEdit*(c: Edit; where: Widget; x, y, w, h: INTEGER);
  586. BEGIN InitWidget(c, w, h);
  587. c.focusable := TRUE;
  588. G.MakeCol(c.bgColor, 255, 255, 255);
  589. c.text := 'Привет'; c.len := 6; c.pos := 2; c.off := 0;
  590. c.handle := EditHandler;
  591. Put(c, where, x, y)
  592. END InitEdit;
  593. PROCEDURE NewEdit*(where: Widget; x, y, w, h: INTEGER): Edit;
  594. VAR c: Edit;
  595. BEGIN NEW(c); InitEdit(c, where, x, y, w, h)
  596. RETURN c END NewEdit;
  597. PROCEDURE EditSetText*(c: Edit; text: ARRAY OF CHAR);
  598. BEGIN
  599. Strings.Copy(text, c.text);
  600. c.len := Strings.Length(text);
  601. c.pos := 0;
  602. c.off := 0
  603. END EditSetText;
  604. (** ScrollBar **)
  605. PROCEDURE DrawBox(x, y, w, h: INTEGER; bg, fg: G.Color);
  606. BEGIN
  607. G.FillRect(x, y, x + w - 1, y + h - 1, bg);
  608. G.Rect(x, y, x + w - 1, y + h - 1, fg)
  609. END DrawBox;
  610. PROCEDURE DrawHorizScrollBar(c: ScrollBar; x, y, w, h: INTEGER);
  611. VAR fw, fh, X, Y, hs, maxHs, pos, range: INTEGER;
  612. bs: INTEGER; (** Button size *)
  613. grey: G.Color;
  614. BEGIN
  615. G.MakeCol(grey, 80, 80, 80);
  616. DrawButtonBox(x, y, c.w, c.h, c.bgColor, c.parent.bgColor, TRUE, TRUE);
  617. hs := c.handleSize;
  618. bs := c.h;
  619. IF bs > 20 THEN bs := 20 END;
  620. c.btnSize := bs;
  621. maxHs := c.w - bs * 2 + 4;
  622. IF hs > maxHs THEN hs := maxHs END;
  623. c.handleSize := hs;
  624. range := c.max - c.min;
  625. pos := c.value;
  626. IF pos < c.min THEN pos := c.min ELSIF pos > c.max THEN pos := c.max END;
  627. c.handlePos := bs - 2 + (pos * (maxHs - hs) + range DIV 2) DIV range;
  628. DrawButtonBox(x, y, bs, c.h, c.bgColor,
  629. c.parent.bgColor, c.btnPressed = 1, FALSE);
  630. DrawButtonBox(x + c.w - bs, y, bs, c.h, c.bgColor,
  631. c.parent.bgColor, c.btnPressed = 2, TRUE);
  632. X := x + (bs - 1) DIV 2; Y := y + (bs - 1) DIV 2;
  633. IF c.btnPressed = 1 THEN INC(X); INC(Y) END;
  634. G.HLine(X - 4, Y, X + 4, c.fgColor);
  635. G.Line(X - 4, Y, X - 1, Y + 3, c.fgColor);
  636. G.Line(X - 4, Y, X - 1, Y - 3, c.fgColor);
  637. X := x + c.w - bs DIV 2 - 1;
  638. IF c.btnPressed = 1 THEN DEC(Y) END;
  639. IF c.btnPressed = 2 THEN INC(X); INC(Y) END;
  640. G.HLine(X - 4, Y, X + 4, c.fgColor);
  641. G.Line(X + 4, Y, X + 1, Y + 3, c.fgColor);
  642. G.Line(X + 4, Y, X + 1, Y - 3, c.fgColor);
  643. DrawButtonBox(x + c.handlePos, y, hs, c.h, c.bgColor,
  644. c.parent.bgColor, c.btnPressed = 3, FALSE)
  645. END DrawHorizScrollBar;
  646. PROCEDURE DrawVertScrollBar(c: ScrollBar; x, y, w, h: INTEGER);
  647. VAR fw, fh, X, Y, hs, maxHs, pos, range: INTEGER;
  648. bs: INTEGER; (** Button size *)
  649. grey: G.Color;
  650. BEGIN
  651. G.MakeCol(grey, 80, 80, 80);
  652. DrawButtonBox(x, y, c.w, c.h, c.bgColor, c.parent.bgColor, TRUE, TRUE);
  653. hs := c.handleSize;
  654. bs := c.w;
  655. IF bs > 20 THEN bs := 20 END;
  656. c.btnSize := bs;
  657. maxHs := c.h - bs * 2 + 4;
  658. IF hs > maxHs THEN hs := maxHs END;
  659. range := c.max - c.min;
  660. pos := c.value;
  661. IF pos < c.min THEN pos := c.min ELSIF pos > c.max THEN pos := c.max END;
  662. c.handlePos := bs - 2 + ((maxHs - hs) * pos + range DIV 2) DIV range;
  663. DrawButtonBox(x, y, c.w, bs, c.bgColor,
  664. c.parent.bgColor, c.btnPressed = 1, FALSE);
  665. DrawButtonBox(x, y + c.h - bs, c.w, bs, c.bgColor,
  666. c.parent.bgColor, c.btnPressed = 2, TRUE);
  667. X := x + (bs - 1) DIV 2; Y := y + (bs - 1) DIV 2;
  668. IF c.btnPressed = 1 THEN INC(X); INC(Y) END;
  669. G.VLine(X, Y - 4, Y + 4, c.fgColor);
  670. G.Line(X, Y - 4, X + 3, Y - 1, c.fgColor);
  671. G.Line(X, Y - 4, X - 3, Y - 1, c.fgColor);
  672. Y := y + c.h - bs DIV 2 - 1;
  673. IF c.btnPressed = 1 THEN DEC(X) END;
  674. IF c.btnPressed = 2 THEN INC(X); INC(Y) END;
  675. G.VLine(X, Y - 4, Y + 4, c.fgColor);
  676. G.Line(X, Y + 4, X + 3, Y + 1, c.fgColor);
  677. G.Line(X, Y + 4, X - 3, Y + 1, c.fgColor);
  678. DrawButtonBox(x, y + c.handlePos, c.w, hs, c.bgColor,
  679. c.parent.bgColor, c.btnPressed = 3, FALSE)
  680. END DrawVertScrollBar;
  681. PROCEDURE DrawScrollBar*(c: ScrollBar; x, y, w, h: INTEGER);
  682. BEGIN
  683. IF c.vertical THEN
  684. DrawVertScrollBar(c, x, y, w, h)
  685. ELSE
  686. DrawHorizScrollBar(c, x, y, w, h)
  687. END
  688. END DrawScrollBar;
  689. PROCEDURE ScrollBarSetVertical*(c: ScrollBar; vertical: BOOLEAN);
  690. BEGIN c.vertical := vertical
  691. END ScrollBarSetVertical;
  692. PROCEDURE ScrollBarSetValue*(c: ScrollBar; value: INTEGER);
  693. BEGIN
  694. IF value < c.min THEN value := c.min
  695. ELSIF value > c.max THEN value := c.max
  696. END;
  697. IF c.value # value THEN
  698. c.value := value;
  699. IF c.onScroll # NIL THEN c.onScroll(c, value) END
  700. END
  701. END ScrollBarSetValue;
  702. PROCEDURE HandleScrollBarMouseMove(c: ScrollBar; VAR msg: MouseMoveMsg);
  703. VAR n, x, size, w: INTEGER;
  704. BEGIN
  705. IF c.handlePressed THEN
  706. IF c.vertical THEN x := msg.y; size := c.h
  707. ELSE x := msg.x; size := c.w
  708. END;
  709. w := size - c.btnSize * 2 - c.handleSize;
  710. n := x - c.handlePressPos - c.btnSize;
  711. n := (n * (c.max - c.min) + w DIV 2) DIV w + c.min;
  712. ScrollBarSetValue(c, n)
  713. END
  714. END HandleScrollBarMouseMove;
  715. PROCEDURE HandleScrollBarMouseDown(c: ScrollBar; VAR msg: MouseDownMsg);
  716. VAR x, d, size: INTEGER;
  717. BEGIN
  718. IF c.vertical THEN x := msg.y; size := c.h
  719. ELSE x := msg.x; size := c.w
  720. END;
  721. IF msg.btn = 2 THEN d := 1 ELSE d := c.inc END;
  722. IF x < c.btnSize THEN
  723. c.btnPressed := 1(*Less btn*);
  724. ScrollBarSetValue(c, c.value - d);
  725. ELSIF x >= size - c.btnSize THEN
  726. c.btnPressed := 2(*More btn*);
  727. ScrollBarSetValue(c, c.value + d)
  728. ELSIF msg.btn = 1 THEN
  729. IF (c.handlePos <= x) & (x < c.handlePos + c.handleSize) THEN
  730. c.btnPressed := 3(*Handle*);
  731. c.handlePressed := TRUE;
  732. c.handlePressPos := x - c.handlePos
  733. ELSIF x < c.handlePos THEN
  734. ScrollBarSetValue(c, c.value - c.bigInc)
  735. ELSE
  736. ScrollBarSetValue(c, c.value + c.bigInc)
  737. END
  738. ELSE c.btnPressed := 0(*Nothing*);
  739. END;
  740. WidgetHandler(c, msg)
  741. END HandleScrollBarMouseDown;
  742. PROCEDURE ScrollBarHandler*(c: Widget; VAR msg: Message);
  743. VAR s: ScrollBar;
  744. BEGIN s := c(ScrollBar);
  745. IF msg IS DrawMsg THEN
  746. DrawScrollBar(s, msg(DrawMsg).x, msg(DrawMsg).y,
  747. msg(DrawMsg).w, msg(DrawMsg).h)
  748. ELSIF msg IS MouseMoveMsg THEN HandleScrollBarMouseMove(s, msg(MouseMoveMsg))
  749. ELSIF msg IS MouseDownMsg THEN HandleScrollBarMouseDown(s, msg(MouseDownMsg))
  750. ELSIF msg IS MouseUpMsg THEN s.handlePressed := FALSE; s.btnPressed := 0(*Nothing*)
  751. ELSE WidgetHandler(c, msg)
  752. END
  753. END ScrollBarHandler;
  754. PROCEDURE InitScrollBar*(c: ScrollBar; where: Widget;
  755. x, y, w, h: INTEGER);
  756. BEGIN InitWidget(c, w, h);
  757. c.handle := ScrollBarHandler;
  758. c.value := 0; c.min := 0; c.max := 100; c.inc := 5; c.bigInc := 20;
  759. c.handlePos := 0; c.handleSize := 24; c.btnSize := 0;
  760. c.btnPressed := 0(*Nothing*);
  761. Put(c, where, x, y)
  762. END InitScrollBar;
  763. PROCEDURE NewScrollBar*(where: Widget; x, y, w, h: INTEGER): ScrollBar;
  764. VAR c: ScrollBar;
  765. BEGIN NEW(c); InitScrollBar(c, where, x, y, w, h)
  766. RETURN c END NewScrollBar;
  767. PROCEDURE ScrollBarSetOnScroll*(c: ScrollBar; proc: PROCEDURE (c: ScrollBar; value: INTEGER));
  768. BEGIN c.onScroll := proc
  769. END ScrollBarSetOnScroll;
  770. (** ScrollBox **)
  771. PROCEDURE ScrollBoxSetNoBg*(c: ScrollBox; noBg: BOOLEAN);
  772. BEGIN PanelSetNoBg(c.inner, noBg)
  773. END ScrollBoxSetNoBg;
  774. PROCEDURE ScrollBoxHandler*(c: Widget; VAR msg: Message);
  775. VAR x, y: INTEGER;
  776. BEGIN
  777. IF msg IS DrawMsg THEN
  778. x := msg(DrawMsg).x; y := msg(DrawMsg).y;
  779. DrawBody(c, x, y, c.w, c.h)
  780. ELSIF msg IS PutMsg THEN
  781. DirectPut(msg(PutMsg).what, c(ScrollBox).inner,
  782. msg(PutMsg).x, msg(PutMsg).y)
  783. ELSE WidgetHandler(c, msg)
  784. END
  785. END ScrollBoxHandler;
  786. PROCEDURE ScrollBoxSetInnerSize*(c: ScrollBox; w, h: INTEGER);
  787. BEGIN
  788. Resize(c.inner, w, h);
  789. c.scbHoriz.max := w - c.outer.w;
  790. c.scbVert.max := h - c.outer.h
  791. END ScrollBoxSetInnerSize;
  792. PROCEDURE ScrollBoxOnHorizScroll*(c: ScrollBar; value: INTEGER);
  793. VAR sbx: ScrollBox;
  794. BEGIN
  795. sbx := c.parent(ScrollBox);
  796. sbx.inner.x := -value
  797. END ScrollBoxOnHorizScroll;
  798. PROCEDURE ScrollBoxOnVertScroll*(c: ScrollBar; value: INTEGER);
  799. VAR sbx: ScrollBox;
  800. BEGIN
  801. sbx := c.parent(ScrollBox);
  802. sbx.inner.y := -value
  803. END ScrollBoxOnVertScroll;
  804. PROCEDURE InitScrollBox*(c: ScrollBox; where: Widget; x, y, w, h: INTEGER);
  805. BEGIN InitWidget(c, w, h);
  806. c.handle := ScrollBoxHandler;
  807. c.scbHoriz := NewScrollBar(NIL, 0, 0, w - 16, 16);
  808. DirectPut(c.scbHoriz, c, 0, h - 16);
  809. ScrollBarSetOnScroll(c.scbHoriz, ScrollBoxOnHorizScroll);
  810. c.scbVert := NewScrollBar(NIL, 0, 0, 16, h - 16);
  811. ScrollBarSetVertical(c.scbVert, TRUE);
  812. DirectPut(c.scbVert, c, w - 16, 0);
  813. ScrollBarSetOnScroll(c.scbVert, ScrollBoxOnVertScroll);
  814. c.outer := NewPanel(NIL, 0, 0, w - 16, h - 16);
  815. DirectPut(c.outer, c, 0, 0);
  816. PanelSetNoBg(c.outer, TRUE);
  817. c.inner := NewPanel(c.outer, 0, 0, 1, 1);
  818. ScrollBoxSetInnerSize(c, w * 2, h * 3);
  819. Put(c, where, x, y)
  820. END InitScrollBox;
  821. PROCEDURE NewScrollBox*(where: Widget; x, y, w, h: INTEGER): ScrollBox;
  822. VAR c: ScrollBox;
  823. BEGIN NEW(c); InitScrollBox(c, where, x, y, w, h)
  824. RETURN c END NewScrollBox;
  825. (** Canvas **)
  826. PROCEDURE CanvasHandler*(c: Widget; VAR msg: Message);
  827. VAR x, y: INTEGER;
  828. BEGIN
  829. IF msg IS DrawMsg THEN
  830. x := msg(DrawMsg).x; y := msg(DrawMsg).y;
  831. G.Draw(c(Canvas).bmp, x, y);
  832. DrawBody(c, x, y, c.w, c.h)
  833. ELSE WidgetHandler(c, msg)
  834. END
  835. END CanvasHandler;
  836. PROCEDURE InitCanvas*(c: Canvas; where: Widget; x, y, w, h: INTEGER);
  837. VAR wh: G.Color;
  838. BEGIN InitWidget(c, w, h);
  839. c.bmp := G.NewBitmap(w, h);
  840. G.MakeCol(wh, 255, 255, 255);
  841. G.ClearBitmapToColor(c.bmp, wh);
  842. c.handle := CanvasHandler;
  843. Put(c, where, x, y)
  844. END InitCanvas;
  845. PROCEDURE NewCanvas*(where: Widget; x, y, w, h: INTEGER): Canvas;
  846. VAR c: Canvas;
  847. BEGIN NEW(c); InitCanvas(c, where, x, y, w, h)
  848. RETURN c END NewCanvas;
  849. (** General **)
  850. PROCEDURE DrawCursor;
  851. BEGIN
  852. IF mouseX >= 0 THEN
  853. G.Draw(mouseCursor, mouseX, mouseY)
  854. END
  855. END DrawCursor;
  856. PROCEDURE DrawAll;
  857. VAR c: Widget;
  858. BEGIN
  859. G.TargetScreen;
  860. c := app.body;
  861. REPEAT
  862. DrawForm(c(Form));
  863. c := c.next
  864. UNTIL c = app.body;
  865. DrawCursor;
  866. G.Flip
  867. END DrawAll;
  868. PROCEDURE HandleMouseMove(VAR e: G.Event);
  869. VAR c: Widget;
  870. BEGIN
  871. mouseX := e.x; mouseY := e.y;
  872. c := FindHoveredInRing(app.body, e.x, e.y, FALSE);
  873. IF c # NIL THEN
  874. WidgetHandleMouseMove(c, e.x - c.x, e.y - c.y, e.buttons)
  875. END
  876. END HandleMouseMove;
  877. PROCEDURE HandleMouseDown(VAR e: G.Event);
  878. VAR c: Widget;
  879. BEGIN
  880. pressedX := 0; pressedY := 0;
  881. c := FindHoveredInRing(app.body, e.x, e.y, TRUE);
  882. IF c # NIL THEN
  883. WidgetHandleMouseDown(c, e.x - c.x, e.y - c.y, e.button)
  884. END
  885. END HandleMouseDown;
  886. PROCEDURE HandleMouseUp(VAR e: G.Event);
  887. VAR c: Widget;
  888. BEGIN
  889. IF pressedWidget # NIL THEN
  890. c := pressedWidget;
  891. IF ~c.hovered THEN c := NIL END;
  892. WidgetOnMouseUp(pressedWidget, e.x - pressedX, e.y - pressedY, e.button);
  893. IF (c # NIL) & (e.button = 1) THEN
  894. WidgetOnClick(c)
  895. END
  896. END
  897. END HandleMouseUp;
  898. PROCEDURE HandleKeyDown(VAR e: G.Event);
  899. VAR msg: KeyDownMsg;
  900. BEGIN
  901. IF focusedWidget # NIL THEN
  902. msg.key := e.key;
  903. focusedWidget.handle(focusedWidget, msg)
  904. END
  905. END HandleKeyDown;
  906. PROCEDURE HandleKeyUp(VAR e: G.Event);
  907. VAR msg: KeyUpMsg;
  908. BEGIN
  909. IF focusedWidget # NIL THEN
  910. msg.key := e.key;
  911. focusedWidget.handle(focusedWidget, msg)
  912. END
  913. END HandleKeyUp;
  914. PROCEDURE HandleChar(VAR e: G.Event);
  915. VAR msg: CharMsg;
  916. BEGIN
  917. IF focusedWidget # NIL THEN
  918. msg.key := e.key; msg.ch := e.ch;
  919. msg.mod := e.mod; msg.repeat := e.repeat;
  920. focusedWidget.handle(focusedWidget, msg)
  921. END
  922. END HandleChar;
  923. PROCEDURE HandleEvent(VAR e: G.Event);
  924. BEGIN
  925. IF e.type = G.quit THEN quit := TRUE
  926. ELSIF e.type = G.mouseMove THEN HandleMouseMove(e)
  927. ELSIF e.type = G.mouseDown THEN HandleMouseDown(e)
  928. ELSIF e.type = G.mouseUp THEN HandleMouseUp(e)
  929. ELSIF e.type = G.keyDown THEN HandleKeyDown(e)
  930. ELSIF e.type = G.keyUp THEN HandleKeyUp(e)
  931. ELSIF e.type = G.char THEN HandleChar(e)
  932. END
  933. END HandleEvent;
  934. PROCEDURE Quit*;
  935. BEGIN quit := TRUE
  936. END Quit;
  937. PROCEDURE Run*;
  938. VAR e: G.Event;
  939. BEGIN
  940. quit := FALSE;
  941. REPEAT
  942. WHILE ~quit & G.HasEvents() DO
  943. G.WaitEvent(e);
  944. HandleEvent(e)
  945. END;
  946. DrawAll
  947. UNTIL quit
  948. END Run;
  949. PROCEDURE CreateArrowCursor(): G.Bitmap;
  950. VAR m: G.Bitmap;
  951. bl, wh: G.Color;
  952. i: INTEGER;
  953. BEGIN
  954. m := G.NewBitmap(10, 16);
  955. G.ClearBitmap(m);
  956. G.Target(m);
  957. G.MakeCol(bl, 0, 0, 0);
  958. G.MakeCol(wh, 255, 255, 255);
  959. G.PutPixel(1, 1, wh);
  960. FOR i := 2 TO 8 DO G.HLine(1, i, i, wh) END;
  961. G.HLine(1, 9, 5, wh);
  962. G.HLine(1, 10, 5, wh);
  963. G.PutPixel(1, 11, wh);
  964. G.HLine(5, 11, 6, wh);
  965. G.HLine(5, 12, 6, wh);
  966. G.HLine(6, 13, 7, wh);
  967. G.HLine(6, 14, 7, wh);
  968. G.Line(1, 0, 9, 8, bl);
  969. G.VLine(0, 1, 12, bl);
  970. G.Line(1, 12, 3, 10, bl);
  971. G.Line(4, 11, 5, 14, bl);
  972. G.HLine(6, 15, 7, bl);
  973. G.Line(6, 9, 8, 14, bl);
  974. G.HLine(7, 9, 9, bl);
  975. RETURN m END CreateArrowCursor;
  976. PROCEDURE InitCursor;
  977. BEGIN
  978. mouseCursor := CreateArrowCursor();
  979. mouseX := -1; mouseY := 0;
  980. G.ShowMouse(FALSE)
  981. END InitCursor;
  982. PROCEDURE Init*;
  983. BEGIN
  984. font := G.LoadFont('Data/Fonts/Main');
  985. IF font = NIL THEN Out.String('SimpleGui: could not load font.'); Out.Ln END;
  986. InitCursor;
  987. Done := font # NIL;
  988. app := NewApp();
  989. hoveredWidget := NIL; pressedWidget := NIL;
  990. pressedX := 0; pressedY := 0
  991. END Init;
  992. END SimpleGui.