2
0

SimpleGui.Mod 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131
  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. range := c.max - c.min;
  624. pos := c.value;
  625. IF pos < c.min THEN pos := c.min ELSIF pos > c.max THEN pos := c.max END;
  626. c.handlePos := bs - 2 + ((maxHs - hs) * pos + range DIV 2) DIV range;
  627. DrawButtonBox(x, y, bs, c.h, c.bgColor,
  628. c.parent.bgColor, c.btnPressed = 1, FALSE);
  629. DrawButtonBox(x + c.w - bs, y, bs, c.h, c.bgColor,
  630. c.parent.bgColor, c.btnPressed = 2, TRUE);
  631. X := x + (bs - 1) DIV 2; Y := y + (bs - 1) DIV 2;
  632. IF c.btnPressed = 1 THEN INC(X); INC(Y) END;
  633. G.HLine(X - 4, Y, X + 4, c.fgColor);
  634. G.Line(X - 4, Y, X - 1, Y + 3, c.fgColor);
  635. G.Line(X - 4, Y, X - 1, Y - 3, c.fgColor);
  636. X := x + c.w - bs DIV 2 - 1;
  637. IF c.btnPressed = 1 THEN DEC(Y) END;
  638. IF c.btnPressed = 2 THEN INC(X); INC(Y) END;
  639. G.HLine(X - 4, Y, X + 4, c.fgColor);
  640. G.Line(X + 4, Y, X + 1, Y + 3, c.fgColor);
  641. G.Line(X + 4, Y, X + 1, Y - 3, c.fgColor);
  642. DrawButtonBox(x + c.handlePos, y, hs, c.h, c.bgColor,
  643. c.parent.bgColor, c.btnPressed = 3, FALSE)
  644. END DrawHorizScrollBar;
  645. PROCEDURE DrawVertScrollBar(c: ScrollBar; x, y, w, h: INTEGER);
  646. VAR fw, fh, X, Y, hs, maxHs, pos, range: INTEGER;
  647. bs: INTEGER; (** Button size *)
  648. grey: G.Color;
  649. BEGIN
  650. G.MakeCol(grey, 80, 80, 80);
  651. DrawButtonBox(x, y, c.w, c.h, c.bgColor, c.parent.bgColor, TRUE, TRUE);
  652. hs := c.handleSize;
  653. bs := c.w;
  654. IF bs > 20 THEN bs := 20 END;
  655. c.btnSize := bs;
  656. maxHs := c.h - bs * 2 + 4;
  657. IF hs > maxHs THEN hs := maxHs END;
  658. range := c.max - c.min;
  659. pos := c.value;
  660. IF pos < c.min THEN pos := c.min ELSIF pos > c.max THEN pos := c.max END;
  661. c.handlePos := bs - 2 + ((maxHs - hs) * pos + range DIV 2) DIV range;
  662. DrawButtonBox(x, y, c.w, bs, c.bgColor,
  663. c.parent.bgColor, c.btnPressed = 1, FALSE);
  664. DrawButtonBox(x, y + c.h - bs, c.w, bs, c.bgColor,
  665. c.parent.bgColor, c.btnPressed = 2, TRUE);
  666. X := x + (bs - 1) DIV 2; Y := y + (bs - 1) DIV 2;
  667. IF c.btnPressed = 1 THEN INC(X); INC(Y) END;
  668. G.VLine(X, Y - 4, Y + 4, c.fgColor);
  669. G.Line(X, Y - 4, X + 3, Y - 1, c.fgColor);
  670. G.Line(X, Y - 4, X - 3, Y - 1, c.fgColor);
  671. Y := y + c.h - bs DIV 2 - 1;
  672. IF c.btnPressed = 1 THEN DEC(X) END;
  673. IF c.btnPressed = 2 THEN INC(X); INC(Y) END;
  674. G.VLine(X, Y - 4, Y + 4, c.fgColor);
  675. G.Line(X, Y + 4, X + 3, Y + 1, c.fgColor);
  676. G.Line(X, Y + 4, X - 3, Y + 1, c.fgColor);
  677. DrawButtonBox(x, y + c.handlePos, c.w, hs, c.bgColor,
  678. c.parent.bgColor, c.btnPressed = 3, FALSE)
  679. END DrawVertScrollBar;
  680. PROCEDURE DrawScrollBar*(c: ScrollBar; x, y, w, h: INTEGER);
  681. BEGIN
  682. IF c.vertical THEN
  683. DrawVertScrollBar(c, x, y, w, h)
  684. ELSE
  685. DrawHorizScrollBar(c, x, y, w, h)
  686. END
  687. END DrawScrollBar;
  688. PROCEDURE ScrollBarSetVertical*(c: ScrollBar; vertical: BOOLEAN);
  689. BEGIN c.vertical := vertical
  690. END ScrollBarSetVertical;
  691. PROCEDURE ScrollBarSetValue*(c: ScrollBar; value: INTEGER);
  692. BEGIN
  693. IF value < c.min THEN value := c.min
  694. ELSIF value > c.max THEN value := c.max
  695. END;
  696. IF c.value # value THEN
  697. c.value := value;
  698. IF c.onScroll # NIL THEN c.onScroll(c, value) END
  699. END
  700. END ScrollBarSetValue;
  701. PROCEDURE HandleScrollBarMouseMove(c: ScrollBar; VAR msg: MouseMoveMsg);
  702. VAR n, x, size, w: INTEGER;
  703. BEGIN
  704. IF c.handlePressed THEN
  705. IF c.vertical THEN x := msg.y; size := c.h
  706. ELSE x := msg.x; size := c.w
  707. END;
  708. w := size - c.btnSize * 2 - c.handleSize;
  709. n := x - c.handlePressPos - c.btnSize;
  710. n := (n * (c.max - c.min) + w DIV 2) DIV w + c.min;
  711. ScrollBarSetValue(c, n)
  712. END
  713. END HandleScrollBarMouseMove;
  714. PROCEDURE HandleScrollBarMouseDown(c: ScrollBar; VAR msg: MouseDownMsg);
  715. VAR x, d, size: INTEGER;
  716. BEGIN
  717. IF c.vertical THEN x := msg.y; size := c.h
  718. ELSE x := msg.x; size := c.w
  719. END;
  720. IF msg.btn = 2 THEN d := 1 ELSE d := c.inc END;
  721. IF x < c.btnSize THEN
  722. c.btnPressed := 1(*Less btn*);
  723. ScrollBarSetValue(c, c.value - d);
  724. ELSIF x >= size - c.btnSize THEN
  725. c.btnPressed := 2(*More btn*);
  726. ScrollBarSetValue(c, c.value + d)
  727. ELSIF msg.btn = 1 THEN
  728. IF (c.handlePos <= x) & (x < c.handlePos + c.handleSize) THEN
  729. c.btnPressed := 3(*Handle*);
  730. c.handlePressed := TRUE;
  731. c.handlePressPos := x - c.handlePos
  732. ELSIF x < c.handlePos THEN
  733. ScrollBarSetValue(c, c.value - c.bigInc)
  734. ELSE
  735. ScrollBarSetValue(c, c.value + c.bigInc)
  736. END
  737. ELSE c.btnPressed := 0(*Nothing*);
  738. END;
  739. WidgetHandler(c, msg)
  740. END HandleScrollBarMouseDown;
  741. PROCEDURE ScrollBarHandler*(c: Widget; VAR msg: Message);
  742. VAR s: ScrollBar;
  743. BEGIN s := c(ScrollBar);
  744. IF msg IS DrawMsg THEN
  745. DrawScrollBar(s, msg(DrawMsg).x, msg(DrawMsg).y,
  746. msg(DrawMsg).w, msg(DrawMsg).h)
  747. ELSIF msg IS MouseMoveMsg THEN HandleScrollBarMouseMove(s, msg(MouseMoveMsg))
  748. ELSIF msg IS MouseDownMsg THEN HandleScrollBarMouseDown(s, msg(MouseDownMsg))
  749. ELSIF msg IS MouseUpMsg THEN s.handlePressed := FALSE; s.btnPressed := 0(*Nothing*)
  750. ELSE WidgetHandler(c, msg)
  751. END
  752. END ScrollBarHandler;
  753. PROCEDURE InitScrollBar*(c: ScrollBar; where: Widget;
  754. x, y, w, h: INTEGER);
  755. BEGIN InitWidget(c, w, h);
  756. c.handle := ScrollBarHandler;
  757. c.value := 0; c.min := 0; c.max := 100; c.inc := 5; c.bigInc := 20;
  758. c.handlePos := 0; c.handleSize := 24; c.btnSize := 0;
  759. c.btnPressed := 0(*Nothing*);
  760. Put(c, where, x, y)
  761. END InitScrollBar;
  762. PROCEDURE NewScrollBar*(where: Widget; x, y, w, h: INTEGER): ScrollBar;
  763. VAR c: ScrollBar;
  764. BEGIN NEW(c); InitScrollBar(c, where, x, y, w, h)
  765. RETURN c END NewScrollBar;
  766. PROCEDURE ScrollBarSetOnScroll*(c: ScrollBar; proc: PROCEDURE (c: ScrollBar; value: INTEGER));
  767. BEGIN c.onScroll := proc
  768. END ScrollBarSetOnScroll;
  769. (** ScrollBox **)
  770. PROCEDURE ScrollBoxSetNoBg*(c: ScrollBox; noBg: BOOLEAN);
  771. BEGIN PanelSetNoBg(c.inner, noBg)
  772. END ScrollBoxSetNoBg;
  773. PROCEDURE ScrollBoxHandler*(c: Widget; VAR msg: Message);
  774. VAR x, y: INTEGER;
  775. BEGIN
  776. IF msg IS DrawMsg THEN
  777. x := msg(DrawMsg).x; y := msg(DrawMsg).y;
  778. DrawBody(c, x, y, c.w, c.h)
  779. ELSIF msg IS PutMsg THEN
  780. DirectPut(msg(PutMsg).what, c(ScrollBox).inner,
  781. msg(PutMsg).x, msg(PutMsg).y)
  782. ELSE WidgetHandler(c, msg)
  783. END
  784. END ScrollBoxHandler;
  785. PROCEDURE ScrollBoxSetInnerSize*(c: ScrollBox; w, h: INTEGER);
  786. BEGIN
  787. Resize(c.inner, w, h);
  788. c.scbHoriz.max := w - c.outer.w;
  789. c.scbVert.max := h - c.outer.h
  790. END ScrollBoxSetInnerSize;
  791. PROCEDURE ScrollBoxOnHorizScroll*(c: ScrollBar; value: INTEGER);
  792. VAR sbx: ScrollBox;
  793. BEGIN
  794. sbx := c.parent(ScrollBox);
  795. sbx.inner.x := -value
  796. END ScrollBoxOnHorizScroll;
  797. PROCEDURE ScrollBoxOnVertScroll*(c: ScrollBar; value: INTEGER);
  798. VAR sbx: ScrollBox;
  799. BEGIN
  800. sbx := c.parent(ScrollBox);
  801. sbx.inner.y := -value
  802. END ScrollBoxOnVertScroll;
  803. PROCEDURE InitScrollBox*(c: ScrollBox; where: Widget; x, y, w, h: INTEGER);
  804. BEGIN InitWidget(c, w, h);
  805. c.handle := ScrollBoxHandler;
  806. c.scbHoriz := NewScrollBar(NIL, 0, 0, w - 16, 16);
  807. DirectPut(c.scbHoriz, c, 0, h - 16);
  808. ScrollBarSetOnScroll(c.scbHoriz, ScrollBoxOnHorizScroll);
  809. c.scbVert := NewScrollBar(NIL, 0, 0, 16, h - 16);
  810. ScrollBarSetVertical(c.scbVert, TRUE);
  811. DirectPut(c.scbVert, c, w - 16, 0);
  812. ScrollBarSetOnScroll(c.scbVert, ScrollBoxOnVertScroll);
  813. c.outer := NewPanel(NIL, 0, 0, w - 16, h - 16);
  814. DirectPut(c.outer, c, 0, 0);
  815. PanelSetNoBg(c.outer, TRUE);
  816. c.inner := NewPanel(c.outer, 0, 0, 1, 1);
  817. ScrollBoxSetInnerSize(c, w * 2, h * 3);
  818. Put(c, where, x, y)
  819. END InitScrollBox;
  820. PROCEDURE NewScrollBox*(where: Widget; x, y, w, h: INTEGER): ScrollBox;
  821. VAR c: ScrollBox;
  822. BEGIN NEW(c); InitScrollBox(c, where, x, y, w, h)
  823. RETURN c END NewScrollBox;
  824. (** Canvas **)
  825. PROCEDURE CanvasHandler*(c: Widget; VAR msg: Message);
  826. VAR x, y: INTEGER;
  827. BEGIN
  828. IF msg IS DrawMsg THEN
  829. x := msg(DrawMsg).x; y := msg(DrawMsg).y;
  830. G.Draw(c(Canvas).bmp, x, y);
  831. DrawBody(c, x, y, c.w, c.h)
  832. ELSE WidgetHandler(c, msg)
  833. END
  834. END CanvasHandler;
  835. PROCEDURE InitCanvas*(c: Canvas; where: Widget; x, y, w, h: INTEGER);
  836. VAR wh: G.Color;
  837. BEGIN InitWidget(c, w, h);
  838. c.bmp := G.NewBitmap(w, h);
  839. G.MakeCol(wh, 255, 255, 255);
  840. G.ClearBitmapToColor(c.bmp, wh);
  841. c.handle := CanvasHandler;
  842. Put(c, where, x, y)
  843. END InitCanvas;
  844. PROCEDURE NewCanvas*(where: Widget; x, y, w, h: INTEGER): Canvas;
  845. VAR c: Canvas;
  846. BEGIN NEW(c); InitCanvas(c, where, x, y, w, h)
  847. RETURN c END NewCanvas;
  848. (** General **)
  849. PROCEDURE DrawCursor;
  850. BEGIN
  851. IF mouseX >= 0 THEN
  852. G.Draw(mouseCursor, mouseX, mouseY)
  853. END
  854. END DrawCursor;
  855. PROCEDURE DrawAll;
  856. VAR c: Widget;
  857. BEGIN
  858. G.TargetScreen;
  859. c := app.body;
  860. REPEAT
  861. DrawForm(c(Form));
  862. c := c.next
  863. UNTIL c = app.body;
  864. DrawCursor;
  865. G.Flip
  866. END DrawAll;
  867. PROCEDURE HandleMouseMove(VAR e: G.Event);
  868. VAR c: Widget;
  869. BEGIN
  870. mouseX := e.x; mouseY := e.y;
  871. c := FindHoveredInRing(app.body, e.x, e.y, FALSE);
  872. IF c # NIL THEN
  873. WidgetHandleMouseMove(c, e.x - c.x, e.y - c.y, e.buttons)
  874. END
  875. END HandleMouseMove;
  876. PROCEDURE HandleMouseDown(VAR e: G.Event);
  877. VAR c: Widget;
  878. BEGIN
  879. pressedX := 0; pressedY := 0;
  880. c := FindHoveredInRing(app.body, e.x, e.y, TRUE);
  881. IF c # NIL THEN
  882. WidgetHandleMouseDown(c, e.x - c.x, e.y - c.y, e.button)
  883. END
  884. END HandleMouseDown;
  885. PROCEDURE HandleMouseUp(VAR e: G.Event);
  886. VAR c: Widget;
  887. BEGIN
  888. IF pressedWidget # NIL THEN
  889. c := pressedWidget;
  890. IF ~c.hovered THEN c := NIL END;
  891. WidgetOnMouseUp(pressedWidget, e.x - pressedX, e.y - pressedY, e.button);
  892. IF (c # NIL) & (e.button = 1) THEN
  893. WidgetOnClick(c)
  894. END
  895. END
  896. END HandleMouseUp;
  897. PROCEDURE HandleKeyDown(VAR e: G.Event);
  898. VAR msg: KeyDownMsg;
  899. BEGIN
  900. IF focusedWidget # NIL THEN
  901. msg.key := e.key;
  902. focusedWidget.handle(focusedWidget, msg)
  903. END
  904. END HandleKeyDown;
  905. PROCEDURE HandleKeyUp(VAR e: G.Event);
  906. VAR msg: KeyUpMsg;
  907. BEGIN
  908. IF focusedWidget # NIL THEN
  909. msg.key := e.key;
  910. focusedWidget.handle(focusedWidget, msg)
  911. END
  912. END HandleKeyUp;
  913. PROCEDURE HandleChar(VAR e: G.Event);
  914. VAR msg: CharMsg;
  915. BEGIN
  916. IF focusedWidget # NIL THEN
  917. msg.key := e.key; msg.ch := e.ch;
  918. msg.mod := e.mod; msg.repeat := e.repeat;
  919. focusedWidget.handle(focusedWidget, msg)
  920. END
  921. END HandleChar;
  922. PROCEDURE HandleEvent(VAR e: G.Event);
  923. BEGIN
  924. IF e.type = G.quit THEN quit := TRUE
  925. ELSIF e.type = G.mouseMove THEN HandleMouseMove(e)
  926. ELSIF e.type = G.mouseDown THEN HandleMouseDown(e)
  927. ELSIF e.type = G.mouseUp THEN HandleMouseUp(e)
  928. ELSIF e.type = G.keyDown THEN HandleKeyDown(e)
  929. ELSIF e.type = G.keyUp THEN HandleKeyUp(e)
  930. ELSIF e.type = G.char THEN HandleChar(e)
  931. END
  932. END HandleEvent;
  933. PROCEDURE Quit*;
  934. BEGIN quit := TRUE
  935. END Quit;
  936. PROCEDURE Run*;
  937. VAR e: G.Event;
  938. BEGIN
  939. quit := FALSE;
  940. REPEAT
  941. WHILE ~quit & G.HasEvents() DO
  942. G.WaitEvent(e);
  943. HandleEvent(e)
  944. END;
  945. DrawAll
  946. UNTIL quit
  947. END Run;
  948. PROCEDURE CreateArrowCursor(): G.Bitmap;
  949. VAR m: G.Bitmap;
  950. bl, wh: G.Color;
  951. i: INTEGER;
  952. BEGIN
  953. m := G.NewBitmap(10, 16);
  954. G.ClearBitmap(m);
  955. G.Target(m);
  956. G.MakeCol(bl, 0, 0, 0);
  957. G.MakeCol(wh, 255, 255, 255);
  958. G.PutPixel(1, 1, wh);
  959. FOR i := 2 TO 8 DO G.HLine(1, i, i, wh) END;
  960. G.HLine(1, 9, 5, wh);
  961. G.HLine(1, 10, 5, wh);
  962. G.PutPixel(1, 11, wh);
  963. G.HLine(5, 11, 6, wh);
  964. G.HLine(5, 12, 6, wh);
  965. G.HLine(6, 13, 7, wh);
  966. G.HLine(6, 14, 7, wh);
  967. G.Line(1, 0, 9, 8, bl);
  968. G.VLine(0, 1, 12, bl);
  969. G.Line(1, 12, 3, 10, bl);
  970. G.Line(4, 11, 5, 14, bl);
  971. G.HLine(6, 15, 7, bl);
  972. G.Line(6, 9, 8, 14, bl);
  973. G.HLine(7, 9, 9, bl);
  974. RETURN m END CreateArrowCursor;
  975. PROCEDURE InitCursor;
  976. BEGIN
  977. mouseCursor := CreateArrowCursor();
  978. mouseX := -1; mouseY := 0;
  979. G.ShowMouse(FALSE)
  980. END InitCursor;
  981. PROCEDURE Init*;
  982. BEGIN
  983. font := G.LoadFont('Data/Fonts/Main');
  984. IF font = NIL THEN Out.String('SimpleGui: could not load font.'); Out.Ln END;
  985. InitCursor;
  986. Done := font # NIL;
  987. app := NewApp();
  988. hoveredWidget := NIL; pressedWidget := NIL;
  989. pressedX := 0; pressedY := 0
  990. END Init;
  991. END SimpleGui.