SimpleGui.Mod 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839
  1. MODULE SimpleGui;
  2. IMPORT G := Graph, Strings, Out;
  3. TYPE
  4. Widget* = POINTER TO WidgetDesc;
  5. Message* = RECORD END;
  6. DrawMsg* = RECORD(Message) x*, y*, w*, h*: INTEGER END;
  7. MouseMoveMsg* = RECORD(Message) x*, y*, btn*: INTEGER END;
  8. MouseDownMsg* = RECORD(Message) x*, y*, btn*: INTEGER END;
  9. MouseUpMsg* = RECORD(Message) x*, y*, btn*: INTEGER END;
  10. MouseEnterMsg* = RECORD(Message) END;
  11. MouseLeaveMsg* = RECORD(Message) END;
  12. ClickMsg* = RECORD(Message) END;
  13. GetFocusMsg* = RECORD(Message) END;
  14. LostFocusMsg* = RECORD(Message) END;
  15. KeyDownMsg* = RECORD(Message) key*: INTEGER END;
  16. KeyUpMsg* = RECORD(Message) key*: INTEGER END;
  17. CharMsg* = RECORD(Message)
  18. key*: INTEGER;
  19. ch*: CHAR;
  20. mod*: SET;
  21. repeat*: BOOLEAN
  22. END;
  23. Handler* = PROCEDURE (c: Widget; VAR msg: Message);
  24. WidgetDesc* = RECORD
  25. x*, y*, w*, h*: INTEGER;
  26. bgColor*, fgColor*: G.Color;
  27. focusable*: BOOLEAN; (** TRUE if widget can get focus *)
  28. focused*: BOOLEAN; (** TRUE if this widget is globally in focus *)
  29. hovered*: BOOLEAN; (** TRUE if mouse pointer is over the widget *)
  30. pressed*: BOOLEAN; (** TRUE if widget is held down with LMB *)
  31. body*: Widget; (** A ring of widgets that this widget contains *)
  32. parent*: Widget; (** A widget that this widget is contained in *)
  33. prev*, next*: Widget;
  34. handle*: Handler;
  35. onMouseDown*: PROCEDURE (c: Widget; x, y, btn: INTEGER);
  36. onMouseUp*: PROCEDURE (c: Widget; x, y, btn: INTEGER);
  37. onMouseMove*: PROCEDURE (c: Widget; x, y, btn: INTEGER);
  38. onMouseEnter*: PROCEDURE (c: Widget);
  39. onMouseLeave*: PROCEDURE (c: Widget);
  40. onClick*: PROCEDURE (c: Widget);
  41. onKeyDown*: PROCEDURE (c: Widget; key: INTEGER);
  42. onKeyUp*: PROCEDURE (c: Widget; key: INTEGER);
  43. onChar*: PROCEDURE (c: Widget; key: INTEGER; ch: CHAR; mod: SET; repeat: BOOLEAN);
  44. END;
  45. Panel* = POINTER TO PanelDesc;
  46. PanelDesc* = RECORD(WidgetDesc) END;
  47. App* = POINTER TO AppDesc;
  48. AppDesc* = RECORD(WidgetDesc) END;
  49. Form* = POINTER TO FormDesc;
  50. FormDesc* = RECORD(WidgetDesc) END;
  51. Button* = POINTER TO ButtonDesc;
  52. ButtonDesc* = RECORD(WidgetDesc)
  53. caption*: ARRAY 64 OF CHAR
  54. END;
  55. Edit* = POINTER TO EditDesc;
  56. EditDesc* = RECORD(WidgetDesc)
  57. text*: ARRAY 256 OF CHAR;
  58. len*: INTEGER; (** Length of text in characters *)
  59. pos*: INTEGER; (** Position of text carret, in range [0; len] *)
  60. off*: INTEGER (** Used to slide text that does not fit, normal is 0 *)
  61. END;
  62. ScrollBar* = POINTER TO ScrollBarDesc;
  63. ScrollBarDesc* = RECORD(WidgetDesc)
  64. vertical*: BOOLEAN; (** TRUE for vertical scroll, FALSE for horizontal *)
  65. min*, max*: INTEGER;
  66. value*: INTEGER; (** The position of the scroll, in range [min; max] *)
  67. inc*, bigInc*: INTEGER; (** A single increment of value, and a big one *)
  68. handlePos*, handleSize*: INTEGER; (** Size and position of handle, px *)
  69. handlePressed*: BOOLEAN;
  70. handlePressPos*: INTEGER; (** Where handle was pressed, offset in px *)
  71. btnPressed*: INTEGER; (** 0-nothing, 1-less btn, 2-more btn, 3-handle *)
  72. onScroll*: PROCEDURE (c: Widget; value: INTEGER);
  73. END;
  74. VAR
  75. Done*: BOOLEAN; (** FALSE after a failed opration and before the next Init *)
  76. app*: App;
  77. focusedWidget*: Widget; (** The widget with focus = TRUE *)
  78. font*: G.Font;
  79. quit: BOOLEAN; (** Main loop in procedure Run ends when TRUE *)
  80. hoveredWidget: Widget;
  81. pressedWidget: Widget;
  82. pressedX, pressedY: INTEGER;
  83. (** Widget **)
  84. PROCEDURE FindHoveredInRing(list: Widget; x, y: INTEGER;
  85. forMouseDown: BOOLEAN): Widget;
  86. VAR c: Widget;
  87. BEGIN
  88. IF list # NIL THEN
  89. c := list.prev;
  90. WHILE (c # NIL) &
  91. ~((c.x <= x) & (x < c.x + c.w) &
  92. (c.y <= y) & (y < c.y + c.h))
  93. DO
  94. IF c = list THEN c := NIL ELSE c := c.prev END
  95. END;
  96. IF forMouseDown & (c # NIL) THEN
  97. INC(pressedX, c.x); INC(pressedY, c.y)
  98. END
  99. ELSE c := NIL
  100. END
  101. RETURN c END FindHoveredInRing;
  102. PROCEDURE WidgetOnMouseEnter*(c: Widget);
  103. VAR msg: MouseEnterMsg;
  104. BEGIN
  105. IF pressedWidget = c THEN c.pressed := TRUE END;
  106. c.hovered := TRUE;
  107. c.handle(c, msg)
  108. END WidgetOnMouseEnter;
  109. PROCEDURE WidgetOnMouseLeave*(c: Widget);
  110. VAR msg: MouseLeaveMsg;
  111. BEGIN
  112. c.hovered := FALSE;
  113. c.pressed := FALSE;
  114. c.handle(c, msg)
  115. END WidgetOnMouseLeave;
  116. PROCEDURE WidgetOnMouseMove*(c: Widget; x, y, btn: INTEGER);
  117. VAR msg: MouseMoveMsg;
  118. BEGIN
  119. IF (0 <= x) & (x < c.w) & (0 <= y) & (y < c.h) THEN
  120. IF c # hoveredWidget THEN
  121. IF hoveredWidget # NIL THEN WidgetOnMouseLeave(hoveredWidget) END;
  122. hoveredWidget := c;
  123. WidgetOnMouseEnter(hoveredWidget)
  124. END
  125. ELSIF c = hoveredWidget THEN
  126. WidgetOnMouseLeave(c);
  127. hoveredWidget := NIL
  128. END;
  129. msg.x := x; msg.y := y; msg.btn := btn;
  130. c.handle(c, msg);
  131. IF c.onMouseMove # NIL THEN c.onMouseMove(c, x, y, btn) END
  132. END WidgetOnMouseMove;
  133. PROCEDURE WidgetHandleMouseMove*(c: Widget; x, y, btn: INTEGER);
  134. VAR p: Widget;
  135. BEGIN
  136. IF pressedWidget # NIL THEN
  137. WidgetOnMouseMove(pressedWidget, x - pressedX, y - pressedY, btn)
  138. ELSE
  139. p := FindHoveredInRing(c.body, x, y, FALSE);
  140. IF p # NIL THEN
  141. WidgetHandleMouseMove(p, x - p.x, y - p.y, btn)
  142. ELSE
  143. WidgetOnMouseMove(c, x, y, btn)
  144. END
  145. END
  146. END WidgetHandleMouseMove;
  147. PROCEDURE Focus*(c: Widget);
  148. VAR get: GetFocusMsg;
  149. lost: LostFocusMsg;
  150. BEGIN
  151. IF c.focusable THEN
  152. IF focusedWidget # NIL THEN
  153. focusedWidget.focused := FALSE;
  154. focusedWidget.handle(focusedWidget, lost)
  155. END;
  156. c.focused := TRUE;
  157. focusedWidget := c;
  158. focusedWidget.handle(focusedWidget, get)
  159. END
  160. END Focus;
  161. PROCEDURE WidgetOnMouseDown*(c: Widget; x, y, btn: INTEGER);
  162. VAR msg: MouseDownMsg;
  163. BEGIN
  164. pressedWidget := c;
  165. Focus(c);
  166. msg.x := x; msg.y := y; msg.btn := btn;
  167. c.handle(c, msg);
  168. IF c.onMouseDown # NIL THEN c.onMouseDown(c, x, y, btn) END
  169. END WidgetOnMouseDown;
  170. PROCEDURE WidgetHandleMouseDown*(c: Widget; x, y, btn: INTEGER);
  171. VAR p: Widget;
  172. BEGIN
  173. p := FindHoveredInRing(c.body, x, y, TRUE);
  174. IF p # NIL THEN
  175. WidgetHandleMouseDown(p, x - p.x, y - p.y, btn)
  176. ELSE
  177. WidgetOnMouseDown(c, x, y, btn)
  178. END
  179. END WidgetHandleMouseDown;
  180. PROCEDURE WidgetOnMouseUp*(c: Widget; x, y, btn: INTEGER);
  181. VAR msg: MouseUpMsg;
  182. BEGIN
  183. pressedWidget := NIL;
  184. msg.x := x; msg.y := y; msg.btn := btn;
  185. c.handle(c, msg);
  186. IF c.onMouseUp # NIL THEN c.onMouseUp(c, x, y, btn) END
  187. END WidgetOnMouseUp;
  188. PROCEDURE WidgetOnClick*(c: Widget);
  189. VAR msg: ClickMsg;
  190. BEGIN c.handle(c, msg);
  191. IF c.onClick # NIL THEN c.onClick(c) END
  192. END WidgetOnClick;
  193. PROCEDURE WidgetHandler*(c: Widget; VAR msg: Message);
  194. VAR x, y: INTEGER;
  195. BEGIN
  196. IF msg IS DrawMsg THEN
  197. x := msg(DrawMsg).x; y := msg(DrawMsg).y;
  198. G.FillRect(x, y, x + c.w - 1, y + c.h - 1, c.bgColor);
  199. G.Rect(x, y, x + c.w - 1, y + c.h - 1, c.fgColor);
  200. G.Rect(x + 2, y + 2, x + c.w - 3, y + c.h - 3, c.fgColor)
  201. ELSIF msg IS MouseDownMsg THEN
  202. IF msg(MouseDownMsg).btn = 1 THEN c.pressed := TRUE END
  203. ELSIF msg IS MouseUpMsg THEN c.pressed := FALSE
  204. END
  205. END WidgetHandler;
  206. PROCEDURE DrawWidget*(c: Widget; x, y, w, h: INTEGER);
  207. VAR M: DrawMsg;
  208. BEGIN
  209. M.x := x; M.y := y; M.w := w; M.h := h;
  210. c.handle(c, M)
  211. END DrawWidget;
  212. PROCEDURE DrawBody*(c: Widget; x, y, w, h: INTEGER);
  213. VAR p: Widget;
  214. x2, y2, w2, h2: INTEGER;
  215. cx, cy, cw, ch: INTEGER;
  216. BEGIN
  217. p := c.body;
  218. IF p # NIL THEN
  219. REPEAT
  220. x2 := x + p.x; y2 := y + p.y;
  221. w2 := w - p.x; h2 := h - p.y;
  222. cx := x2; cy := y2; cw := p.w; ch := p.h;
  223. IF cx + cw > x + w THEN cw := x + w - cx END;
  224. IF cy + ch > y + h THEN ch := y + h - cy END;
  225. IF cx < x THEN DEC(cw, x - cx); cx := x END;
  226. IF cy < y THEN DEC(ch, y - cy); cy := y END;
  227. G.SetClip(cx, cy, cw, ch);
  228. DrawWidget(p, x2, y2, p.w, p.h);
  229. p := p.next
  230. UNTIL p = c.body;
  231. G.UnsetClip
  232. END
  233. END DrawBody;
  234. PROCEDURE SetBgColor*(c: Widget; color: G.Color);
  235. BEGIN c.bgColor := color
  236. END SetBgColor;
  237. PROCEDURE SetFgColor*(c: Widget; color: G.Color);
  238. BEGIN c.fgColor := color
  239. END SetFgColor;
  240. PROCEDURE SetOnMouseMove*(c: Widget; proc: PROCEDURE (c: Widget; x, y, btn: INTEGER));
  241. BEGIN c.onMouseMove := proc
  242. END SetOnMouseMove;
  243. PROCEDURE SetOnMouseDown*(c: Widget; proc: PROCEDURE (c: Widget; x, y, btn: INTEGER));
  244. BEGIN c.onMouseDown := proc
  245. END SetOnMouseDown;
  246. PROCEDURE SetOnMouseUp*(c: Widget; proc: PROCEDURE (c: Widget; x, y, btn: INTEGER));
  247. BEGIN c.onMouseUp := proc
  248. END SetOnMouseUp;
  249. PROCEDURE SetOnClick*(c: Widget; proc: PROCEDURE (c: Widget));
  250. BEGIN c.onClick := proc
  251. END SetOnClick;
  252. PROCEDURE InitWidget*(c: Widget; w, h: INTEGER);
  253. BEGIN c.x := 0; c.y := 0; c.w := w; c.h := h;
  254. c.focusable := FALSE; c.focused := FALSE;
  255. c.hovered := FALSE; c.pressed := FALSE;
  256. G.MakeCol(c.bgColor, 180, 180, 180);
  257. G.MakeCol(c.fgColor, 0, 0, 0);
  258. c.handle := WidgetHandler
  259. END InitWidget;
  260. PROCEDURE Detach*(c: Widget);
  261. VAR p: Widget;
  262. BEGIN
  263. IF c.parent # NIL THEN
  264. IF c.prev = c THEN
  265. c.parent.body := NIL
  266. ELSE
  267. c.prev.next := c.next;
  268. c.next.prev := c.prev
  269. END;
  270. c.parent := NIL
  271. END;
  272. c.prev := NIL; c.next := NIL
  273. END Detach;
  274. PROCEDURE AppendTo*(c: Widget; container: Widget);
  275. VAR r: Widget;
  276. BEGIN
  277. Detach(c);
  278. c.parent := container;
  279. r := container.body;
  280. IF r = NIL THEN
  281. container.body := c;
  282. c.prev := c; c.next := c
  283. ELSE
  284. c.next := r; c.prev := r.prev;
  285. r.prev.next := c; r.prev := c
  286. END
  287. END AppendTo;
  288. PROCEDURE Put*(c, where: Widget; x, y: INTEGER);
  289. VAR p: Widget;
  290. BEGIN
  291. IF (c # NIL) & (where # NIL) THEN
  292. c.x := x; c.y := y;
  293. AppendTo(c, where)
  294. END
  295. END Put;
  296. (** Panel **)
  297. PROCEDURE PanelHandler*(c: Widget; VAR msg: Message);
  298. VAR x, y: INTEGER;
  299. BEGIN
  300. IF msg IS DrawMsg THEN
  301. x := msg(DrawMsg).x; y := msg(DrawMsg).y;
  302. G.FillRect(x, y, x + c.w - 1, y + c.h - 1, c.bgColor);
  303. DrawBody(c, x, y, c.w, c.h)
  304. ELSE WidgetHandler(c, msg)
  305. END
  306. END PanelHandler;
  307. PROCEDURE InitPanel*(c: Panel; where: Widget; x, y, w, h: INTEGER);
  308. BEGIN InitWidget(c, w, h);
  309. c.handle := PanelHandler;
  310. Put(c, where, x, y)
  311. END InitPanel;
  312. PROCEDURE NewPanel*(where: Widget; x, y, w, h: INTEGER): Panel;
  313. VAR c: Panel;
  314. BEGIN NEW(c); InitPanel(c, where, x, y, w, h)
  315. RETURN c END NewPanel;
  316. (** App **)
  317. PROCEDURE InitApp*(c: App);
  318. VAR W, H: INTEGER;
  319. BEGIN
  320. G.GetScreenSize(W, H);
  321. InitWidget(c, W, H)
  322. END InitApp;
  323. PROCEDURE NewApp*(): App;
  324. VAR c: App;
  325. BEGIN NEW(c); InitApp(c)
  326. RETURN c END NewApp;
  327. (** Form **)
  328. PROCEDURE DrawForm*(c: Form);
  329. BEGIN
  330. G.FillRect(c.x, c.y, c.x + c.w - 1, c.y + c.h - 1, c.bgColor);
  331. DrawBody(c, c.x, c.y, c.w, c.h)
  332. END DrawForm;
  333. PROCEDURE FormHandler*(c: Widget; VAR msg: Message);
  334. BEGIN WidgetHandler(c, msg)
  335. END FormHandler;
  336. PROCEDURE InitForm*(c: Form; x, y, w, h: INTEGER);
  337. BEGIN InitWidget(c, w, h);
  338. c.x := x; c.y := y;
  339. c.handle := FormHandler;
  340. AppendTo(c, app)
  341. END InitForm;
  342. PROCEDURE NewForm*(x, y, w, h: INTEGER): Form;
  343. VAR c: Form;
  344. BEGIN NEW(c); InitForm(c, x, y, w, h)
  345. RETURN c END NewForm;
  346. (** Button **)
  347. PROCEDURE MakeOrAndYw(bg: G.Color; VAR or, yw: G.Color);
  348. VAR r, g, b: INTEGER;
  349. BEGIN
  350. G.ColorToRGB(bg, r, g, b);
  351. G.MakeCol(or, (r * 2 + 255 * 3) DIV 5, g + (255 - g) DIV 4, b);
  352. G.MakeCol(yw, (r + 255 * 2) DIV 3, (g + 255 * 3) DIV 4, (b * 3 + 255) DIV 4)
  353. END MakeOrAndYw;
  354. PROCEDURE DrawButtonBox(x, y, w, h: INTEGER; bg, parentBg: G.Color;
  355. down, glow: BOOLEAN);
  356. VAR wh, bl, g1, g2, or, yw: G.Color;
  357. X, Y: INTEGER;
  358. BEGIN
  359. G.MakeCol(bl, 0, 0, 0);
  360. G.MakeCol(wh, 255, 255, 255);
  361. G.MakeCol(g1, 140, 140, 140);
  362. G.MakeCol(g2, 80, 80, 80);
  363. MakeOrAndYw(parentBg, or, yw);
  364. X := x + w - 1; Y := y + h - 1;
  365. G.FillRect(x + 1, y + 1, X - 2, Y - 2, bg);
  366. G.HLine(x + 2, y, X - 1, bl);
  367. G.HLine(x, Y - 1, X - 4, bl);
  368. G.VLine(x, y + 2, Y - 1, bl);
  369. G.VLine(X - 1, y + 1, Y - 4, bl);
  370. IF ~down THEN
  371. G.HLine(x + 3, y + 1, X - 2, wh);
  372. G.HLine(x + 2, Y - 2, X - 4, g1);
  373. G.VLine(x + 1, y + 3, Y - 2, wh);
  374. G.VLine(X - 2, y + 2, Y - 4, g1);
  375. G.PutPixel(X - 3, Y - 3, g1);
  376. G.Line(X - 4, Y - 3, X - 3, Y - 4, g1);
  377. G.PutPixel(x + 2, y + 2, wh)
  378. END;
  379. G.Line(X - 3, Y - 2, X - 2, Y - 3, bl);
  380. G.Line(X - 3, Y - 1, X - 1, Y - 3, g2);
  381. G.Line(x + 1, y + 2, x + 2, y + 1, g1);
  382. G.PutPixel(x + 1, y + 1, bl);
  383. IF glow THEN
  384. G.Line(X - 2, Y - 1, X - 1, Y - 2, yw);
  385. G.HLine(x + 1, Y, X - 2, or);
  386. G.VLine(X, y + 1, Y - 2, or);
  387. G.PutPixel(X - 1, Y - 1, or)
  388. END
  389. END DrawButtonBox;
  390. PROCEDURE DrawButton*(c: Button; x, y, w, h: INTEGER);
  391. VAR fw, fh, tw, tx, ty: INTEGER;
  392. BEGIN
  393. DrawButtonBox(x, y, c.w, c.h, c.bgColor, c.parent.bgColor,
  394. c.pressed & c.hovered, TRUE);
  395. G.GetMonoFontSize(font, fw, fh);
  396. tw := Strings.Length(c.caption) * fw;
  397. tx := x + (c.w - tw) DIV 2;
  398. ty := y + (c.h - fh) DIV 2;
  399. IF c.pressed & c.hovered THEN INC(tx); INC(ty) END;
  400. G.DrawString(c.caption, tx, ty, font, c.fgColor)
  401. END DrawButton;
  402. PROCEDURE ButtonHandler*(c: Widget; VAR msg: Message);
  403. VAR b: Button;
  404. BEGIN b := c(Button);
  405. IF msg IS DrawMsg THEN
  406. DrawButton(b, msg(DrawMsg).x, msg(DrawMsg).y,
  407. msg(DrawMsg).w, msg(DrawMsg).h)
  408. ELSE WidgetHandler(c, msg)
  409. END
  410. END ButtonHandler;
  411. PROCEDURE InitButton*(c: Button; where: Widget;
  412. x, y, w, h: INTEGER; caption: ARRAY OF CHAR);
  413. BEGIN InitWidget(c, w, h);
  414. Strings.Copy(caption, c.caption);
  415. c.focusable := TRUE;
  416. c.handle := ButtonHandler;
  417. Put(c, where, x, y)
  418. END InitButton;
  419. PROCEDURE NewButton*(where: Widget; x, y, w, h: INTEGER; caption: ARRAY OF CHAR): Button;
  420. VAR c: Button;
  421. BEGIN NEW(c); InitButton(c, where, x, y, w, h, caption)
  422. RETURN c END NewButton;
  423. (** Edit **)
  424. PROCEDURE DrawEdit*(c: Edit; x, y, w, h: INTEGER);
  425. VAR fw, fh, tw, tx, ty: INTEGER;
  426. or, yw: G.Color;
  427. BEGIN
  428. MakeOrAndYw(c.parent.bgColor, or, yw);
  429. G.FillRect(x, y, x + c.w - 1, y + c.h - 1, c.bgColor);
  430. G.GetMonoFontSize(font, fw, fh);
  431. tw := Strings.Length(c.text) * fw;
  432. tx := x + 2 - c.off;
  433. ty := y + (c.h - fh) DIV 2;
  434. G.DrawString(c.text, tx, ty, font, c.fgColor);
  435. IF c.focused THEN
  436. INC(tx, fw * c.pos - 1);
  437. G.VLine(tx, ty, ty + fh - 1, or);
  438. G.HLine(tx - 1, ty, tx + 1, or);
  439. G.HLine(tx - 1, ty + fh - 1, tx + 1, or)
  440. END;
  441. G.HLine(x, y, x + c.w - 2, c.fgColor);
  442. G.VLine(x, y, y + c.h - 1, c.fgColor);
  443. G.HLine(x + 1, y + c.h - 1, x + c.w - 1, or);
  444. G.VLine(x + c.w - 1, y, y + c.h - 1, or)
  445. END DrawEdit;
  446. PROCEDURE EditOnMouseDown*(c: Edit; VAR msg: MouseDownMsg);
  447. VAR n, fw, fh: INTEGER;
  448. BEGIN
  449. IF (msg.btn = 1) & (msg.x > 0) & (msg.x < c.w - 1) &
  450. (msg.y > 0) & (msg.y < c.h - 1)
  451. THEN
  452. G.GetMonoFontSize(font, fw, fh);
  453. n := (msg.x - 2 + fw DIV 2) DIV fw;
  454. IF n < 0 THEN n := 0 ELSIF n > c.len THEN n := c.len END;
  455. c.pos := n
  456. END
  457. END EditOnMouseDown;
  458. PROCEDURE EditCheckOffset(c: Edit);
  459. VAR n, fw, fh: INTEGER;
  460. BEGIN
  461. G.GetMonoFontSize(font, fw, fh);
  462. n := c.pos * fw - c.off;
  463. IF c.len * fw <= c.w - 4 THEN c.off := 0
  464. ELSIF n < 0 THEN c.off := c.pos * fw
  465. ELSIF n >= c.w - 4 THEN c.off := c.pos * fw - c.w + 4
  466. ELSIF c.len * fw - c.off <= c.w - 4 THEN c.off := c.len * fw - c.w + 4
  467. END
  468. END EditCheckOffset;
  469. PROCEDURE EditOnChar*(c: Edit; VAR msg: CharMsg);
  470. VAR i: INTEGER;
  471. BEGIN
  472. IF msg.key = G.kBackspace THEN
  473. IF c.pos > 0 THEN
  474. Strings.Delete(c.text, c.pos - 1, 1);
  475. DEC(c.len); DEC(c.pos)
  476. END
  477. ELSIF msg.key = G.kDel THEN
  478. IF c.pos < c.len THEN
  479. Strings.Delete(c.text, c.pos, 1);
  480. DEC(c.len)
  481. END
  482. ELSIF msg.ch < ' ' THEN
  483. IF msg.key = G.kLeft THEN DEC(c.pos)
  484. ELSIF msg.key = G.kRight THEN INC(c.pos)
  485. ELSIF msg.key = G.kHome THEN c.pos := 0
  486. ELSIF msg.key = G.kEnd THEN c.pos := c.len
  487. END;
  488. IF c.pos < 0 THEN c.pos := 0 ELSIF c.pos > c.len THEN c.pos := c.len END
  489. ELSIF c.len < LEN(c.text) - 1 THEN
  490. c.text[c.len + 1] := 0X;
  491. i := c.len;
  492. WHILE i > c.pos DO
  493. c.text[i] := c.text[i - 1];
  494. DEC(i)
  495. END;
  496. c.text[c.pos] := msg.ch;
  497. INC(c.len); INC(c.pos)
  498. END;
  499. EditCheckOffset(c)
  500. END EditOnChar;
  501. PROCEDURE EditHandler*(c: Widget; VAR msg: Message);
  502. VAR e: Edit;
  503. BEGIN e := c(Edit);
  504. IF msg IS DrawMsg THEN
  505. DrawEdit(e, msg(DrawMsg).x, msg(DrawMsg).y,
  506. msg(DrawMsg).w, msg(DrawMsg).h)
  507. ELSIF msg IS MouseDownMsg THEN EditOnMouseDown(e, msg(MouseDownMsg))
  508. ELSIF msg IS CharMsg THEN EditOnChar(e, msg(CharMsg))
  509. ELSE WidgetHandler(c, msg)
  510. END
  511. END EditHandler;
  512. PROCEDURE InitEdit*(c: Edit; where: Widget; x, y, w, h: INTEGER);
  513. BEGIN InitWidget(c, w, h);
  514. c.focusable := TRUE;
  515. G.MakeCol(c.bgColor, 255, 255, 255);
  516. c.text := 'Привет'; c.len := 6; c.pos := 2; c.off := 0;
  517. c.handle := EditHandler;
  518. Put(c, where, x, y)
  519. END InitEdit;
  520. PROCEDURE NewEdit*(where: Widget; x, y, w, h: INTEGER): Edit;
  521. VAR c: Edit;
  522. BEGIN NEW(c); InitEdit(c, where, x, y, w, h)
  523. RETURN c END NewEdit;
  524. PROCEDURE EditSetText*(c: Edit; text: ARRAY OF CHAR);
  525. BEGIN
  526. Strings.Copy(text, c.text);
  527. c.len := Strings.Length(text);
  528. c.pos := 0;
  529. c.off := 0
  530. END EditSetText;
  531. (** ScrollBar **)
  532. PROCEDURE DrawBox(x, y, w, h: INTEGER; bg, fg: G.Color);
  533. BEGIN
  534. G.FillRect(x, y, x + w - 1, y + h - 1, bg);
  535. G.Rect(x, y, x + w - 1, y + h - 1, fg)
  536. END DrawBox;
  537. PROCEDURE DrawScrollBar*(c: ScrollBar; x, y, w, h: INTEGER);
  538. VAR fw, fh, X, Y, hs, maxHs, pos, range: INTEGER;
  539. grey: G.Color;
  540. BEGIN
  541. G.MakeCol(grey, 80, 80, 80);
  542. DrawButtonBox(x, y, c.w, c.h, c.bgColor, c.parent.bgColor, TRUE, TRUE);
  543. hs := c.handleSize;
  544. maxHs := c.w - c.h * 2 + 4;
  545. IF hs > maxHs THEN hs := maxHs END;
  546. range := c.max - c.min;
  547. pos := c.value;
  548. IF pos < c.min THEN pos := c.min ELSIF pos > c.max THEN pos := c.max END;
  549. c.handlePos := c.h - 2 + ((maxHs - hs) * pos + range DIV 2) DIV range;
  550. DrawButtonBox(x, y, c.h, c.h, c.bgColor,
  551. c.parent.bgColor, c.btnPressed = 1, FALSE);
  552. DrawButtonBox(x + c.w - c.h, y, c.h, c.h, c.bgColor,
  553. c.parent.bgColor, c.btnPressed = 2, TRUE);
  554. X := x + (c.h - 1) DIV 2; Y := y + (c.h - 1) DIV 2;
  555. IF c.btnPressed = 1 THEN INC(X); INC(Y) END;
  556. G.HLine(X - 4, Y, X + 4, c.fgColor);
  557. G.Line(X - 4, Y, X - 1, Y + 3, c.fgColor);
  558. G.Line(X - 4, Y, X - 1, Y - 3, c.fgColor);
  559. X := x + c.w - c.h DIV 2 - 1;
  560. IF c.btnPressed = 1 THEN DEC(Y) END;
  561. IF c.btnPressed = 2 THEN INC(X); INC(Y) END;
  562. G.HLine(X - 4, Y, X + 4, c.fgColor);
  563. G.Line(X + 4, Y, X + 1, Y + 3, c.fgColor);
  564. G.Line(X + 4, Y, X + 1, Y - 3, c.fgColor);
  565. DrawButtonBox(x + c.handlePos, y, hs, c.h, c.bgColor,
  566. c.parent.bgColor, c.btnPressed = 3, FALSE)
  567. END DrawScrollBar;
  568. PROCEDURE SetScrollBarValue*(c: ScrollBar; value: INTEGER);
  569. BEGIN
  570. IF value < c.min THEN value := c.min
  571. ELSIF value > c.max THEN value := c.max
  572. END;
  573. c.value := value;
  574. IF c.onScroll # NIL THEN c.onScroll(c, value) END
  575. END SetScrollBarValue;
  576. PROCEDURE HandleScrollBarMouseMove(c: ScrollBar; VAR msg: MouseMoveMsg);
  577. VAR n, x, size, btnSize, w: INTEGER;
  578. BEGIN
  579. IF c.handlePressed THEN
  580. x := msg.x; size := c.w; btnSize := c.h;
  581. w := size - btnSize * 2 - c.handleSize;
  582. n := x - c.handlePressPos - btnSize;
  583. n := (n * (c.max - c.min) + w DIV 2) DIV w + c.min;
  584. SetScrollBarValue(c, n)
  585. END
  586. END HandleScrollBarMouseMove;
  587. PROCEDURE HandleScrollBarMouseDown(c: ScrollBar; VAR msg: MouseDownMsg);
  588. VAR x, d, size, btnSize: INTEGER;
  589. BEGIN
  590. x := msg.x; size := c.w; btnSize := c.h;
  591. IF msg.btn = 2 THEN d := 1 ELSE d := c.inc END;
  592. IF x < btnSize THEN
  593. c.btnPressed := 1(*Less btn*);
  594. SetScrollBarValue(c, c.value - d);
  595. ELSIF x >= size - btnSize THEN
  596. c.btnPressed := 2(*More btn*);
  597. SetScrollBarValue(c, c.value + d)
  598. ELSIF msg.btn = 1 THEN
  599. IF (c.handlePos <= x) & (x < c.handlePos + c.handleSize) THEN
  600. c.btnPressed := 3(*Handle*);
  601. c.handlePressed := TRUE;
  602. c.handlePressPos := x - c.handlePos
  603. ELSIF x < c.handlePos THEN
  604. SetScrollBarValue(c, c.value - c.bigInc)
  605. ELSE
  606. SetScrollBarValue(c, c.value + c.bigInc)
  607. END
  608. ELSE c.btnPressed := 0(*Nothing*);
  609. END;
  610. WidgetHandler(c, msg)
  611. END HandleScrollBarMouseDown;
  612. PROCEDURE ScrollBarHandler*(c: Widget; VAR msg: Message);
  613. VAR s: ScrollBar;
  614. BEGIN s := c(ScrollBar);
  615. IF msg IS DrawMsg THEN
  616. DrawScrollBar(s, msg(DrawMsg).x, msg(DrawMsg).y,
  617. msg(DrawMsg).w, msg(DrawMsg).h)
  618. ELSIF msg IS MouseMoveMsg THEN HandleScrollBarMouseMove(s, msg(MouseMoveMsg))
  619. ELSIF msg IS MouseDownMsg THEN HandleScrollBarMouseDown(s, msg(MouseDownMsg))
  620. ELSIF msg IS MouseUpMsg THEN s.handlePressed := FALSE; s.btnPressed := 0(*Nothing*)
  621. ELSE WidgetHandler(c, msg)
  622. END
  623. END ScrollBarHandler;
  624. PROCEDURE InitScrollBar*(c: ScrollBar; where: Widget;
  625. x, y, w, h: INTEGER);
  626. BEGIN InitWidget(c, w, h);
  627. c.handle := ScrollBarHandler;
  628. c.value := 0; c.min := 0; c.max := 100; c.inc := 5; c.bigInc := 20;
  629. c.handlePos := 0; c.handleSize := 24;
  630. c.btnPressed := 0(*Nothing*);
  631. Put(c, where, x, y)
  632. END InitScrollBar;
  633. PROCEDURE NewScrollBar*(where: Widget; x, y, w, h: INTEGER): ScrollBar;
  634. VAR c: ScrollBar;
  635. BEGIN NEW(c); InitScrollBar(c, where, x, y, w, h)
  636. RETURN c END NewScrollBar;
  637. PROCEDURE SetOnScroll*(c: ScrollBar; proc: PROCEDURE (c: Widget; value: INTEGER));
  638. BEGIN c.onScroll := proc
  639. END SetOnScroll;
  640. (** General **)
  641. PROCEDURE DrawAll*;
  642. VAR c: Widget;
  643. BEGIN
  644. c := app.body;
  645. REPEAT
  646. DrawForm(c(Form));
  647. c := c.next
  648. UNTIL c = app.body;
  649. G.Flip
  650. END DrawAll;
  651. PROCEDURE HandleMouseMove(VAR e: G.Event);
  652. VAR c: Widget;
  653. BEGIN
  654. c := FindHoveredInRing(app.body, e.x, e.y, FALSE);
  655. IF c # NIL THEN
  656. WidgetHandleMouseMove(c, e.x - c.x, e.y - c.y, e.button)
  657. END
  658. END HandleMouseMove;
  659. PROCEDURE HandleMouseDown(VAR e: G.Event);
  660. VAR c: Widget;
  661. BEGIN
  662. pressedX := 0; pressedY := 0;
  663. c := FindHoveredInRing(app.body, e.x, e.y, TRUE);
  664. IF c # NIL THEN
  665. WidgetHandleMouseDown(c, e.x - c.x, e.y - c.y, e.button)
  666. END
  667. END HandleMouseDown;
  668. PROCEDURE HandleMouseUp(VAR e: G.Event);
  669. VAR c: Widget;
  670. BEGIN
  671. IF pressedWidget # NIL THEN
  672. c := pressedWidget;
  673. IF ~c.hovered THEN c := NIL END;
  674. WidgetOnMouseUp(pressedWidget, e.x - pressedX, e.y - pressedY, e.button);
  675. IF (c # NIL) & (e.button = 1) THEN
  676. WidgetOnClick(c)
  677. END
  678. END
  679. END HandleMouseUp;
  680. PROCEDURE HandleKeyDown(VAR e: G.Event);
  681. VAR msg: KeyDownMsg;
  682. BEGIN
  683. IF focusedWidget # NIL THEN
  684. msg.key := e.key;
  685. focusedWidget.handle(focusedWidget, msg)
  686. END
  687. END HandleKeyDown;
  688. PROCEDURE HandleKeyUp(VAR e: G.Event);
  689. VAR msg: KeyUpMsg;
  690. BEGIN
  691. IF focusedWidget # NIL THEN
  692. msg.key := e.key;
  693. focusedWidget.handle(focusedWidget, msg)
  694. END
  695. END HandleKeyUp;
  696. PROCEDURE HandleChar(VAR e: G.Event);
  697. VAR msg: CharMsg;
  698. BEGIN
  699. IF focusedWidget # NIL THEN
  700. msg.key := e.key; msg.ch := e.ch;
  701. msg.mod := e.mod; msg.repeat := e.repeat;
  702. focusedWidget.handle(focusedWidget, msg)
  703. END
  704. END HandleChar;
  705. PROCEDURE HandleEvent(VAR e: G.Event);
  706. BEGIN
  707. IF e.type = G.quit THEN quit := TRUE
  708. ELSIF e.type = G.mouseMove THEN HandleMouseMove(e)
  709. ELSIF e.type = G.mouseDown THEN HandleMouseDown(e)
  710. ELSIF e.type = G.mouseUp THEN HandleMouseUp(e)
  711. ELSIF e.type = G.keyDown THEN HandleKeyDown(e)
  712. ELSIF e.type = G.keyUp THEN HandleKeyUp(e)
  713. ELSIF e.type = G.char THEN HandleChar(e)
  714. END
  715. END HandleEvent;
  716. PROCEDURE Run*;
  717. VAR e: G.Event;
  718. BEGIN
  719. quit := FALSE;
  720. REPEAT
  721. WHILE G.HasEvents() DO
  722. G.WaitEvent(e);
  723. HandleEvent(e)
  724. END;
  725. DrawAll
  726. UNTIL quit
  727. END Run;
  728. PROCEDURE Init*;
  729. BEGIN
  730. font := G.LoadFont('Data/Fonts/Main');
  731. IF font = NIL THEN Out.String('SimpleGui: could not load font.'); Out.Ln END;
  732. Done := font # NIL;
  733. app := NewApp();
  734. hoveredWidget := NIL; pressedWidget := NIL;
  735. pressedX := 0; pressedY := 0
  736. END Init;
  737. END SimpleGui.