SimpleGui.Mod 32 KB

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