cross platform gui lib for lua
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

930 lines
25 KiB

  1. /* lui.c
  2. *
  3. * lua binding to libui (https://github.com/andlabs/libui)
  4. *
  5. * Gunnar Zötl <gz@tset.de>, 2016
  6. * Released under MIT/X11 license. See file LICENSE for details.
  7. *
  8. * This file is included by lui.c
  9. */
  10. /* window control *********************************************************/
  11. /*** Object
  12. * Name: window
  13. */
  14. #define LUI_WINDOW "lui_window"
  15. #define lui_pushWindow(L) lui_pushObject(L, LUI_WINDOW, 1)
  16. #define lui_checkWindow(L, pos) ((lui_object*)luaL_checkudata(L, pos, LUI_WINDOW))
  17. /*** Property
  18. * Object: window
  19. * Name: title
  20. * get / set the title of a window.
  21. * Object: window
  22. * Name: margined
  23. * true if the window has / should have a margin around its contents, false if
  24. * not. Default is false.
  25. *** Property
  26. * Object: window
  27. * Name: fullscreen
  28. * true if the window is / should be a fullscreen, false if not. Default is
  29. * false.
  30. *** Property
  31. * Name: borderless
  32. * true if the window is / should be borderless, false if not. Default is
  33. * false.
  34. *** Property
  35. * Object: window
  36. * Name: onclosing
  37. * a function <code>bool onclosing(window)</code> to be called when the user
  38. * requested to close the window. Must return true if the window can be
  39. * closed, false or nil if not.
  40. *** Property
  41. * Object: window
  42. * Name: oncontentsizechanged
  43. * a function <code>oncontentsizechanged(window)</code> to be called when the user
  44. * changes the window size.
  45. */
  46. static int lui_window__index(lua_State *L)
  47. {
  48. lui_object *lobj = lui_checkWindow(L, 1);
  49. const char *what = luaL_checkstring(L, 2);
  50. if (strcmp(what, "title") == 0) {
  51. char *title = uiWindowTitle(uiWindow(lobj->object));
  52. lua_pushstring(L, title);
  53. uiFreeText(title);
  54. } else if (strcmp(what, "margined") == 0) {
  55. lua_pushboolean(L, uiWindowMargined(uiWindow(lobj->object)) != 0);
  56. } else if (strcmp(what, "fullscreen") == 0) {
  57. lua_pushboolean(L, uiWindowFullscreen(uiWindow(lobj->object)) != 0);
  58. } else if (strcmp(what, "borderless") == 0) {
  59. lua_pushboolean(L, uiWindowBorderless(uiWindow(lobj->object)) != 0);
  60. } else if (strcmp(what, "oncontentsizechanged") == 0) {
  61. lui_objectGetHandler(L, "oncontentsizechanged");
  62. } else if (strcmp(what, "onclosing") == 0) {
  63. lui_objectGetHandler(L, "onclosing");
  64. } else {
  65. return lui_control__index(L);
  66. }
  67. return 1;
  68. }
  69. static int lui_window__newindex(lua_State *L)
  70. {
  71. lui_object *lobj = lui_checkWindow(L, 1);
  72. const char *what = luaL_checkstring(L, 2);
  73. if (strcmp(what, "title") == 0) {
  74. const char *title = luaL_checkstring(L, 3);
  75. uiWindowSetTitle(uiWindow(lobj->object), title);
  76. } else if (strcmp(what, "margined") == 0) {
  77. int margined = lua_toboolean(L, 3);
  78. uiWindowSetMargined(uiWindow(lobj->object), margined);
  79. } else if (strcmp(what, "fullscreen") == 0) {
  80. int fullscreen = lua_toboolean(L, 3);
  81. uiWindowSetFullscreen(uiWindow(lobj->object), fullscreen);
  82. } else if (strcmp(what, "borderless") == 0) {
  83. int borderless = lua_toboolean(L, 3);
  84. uiWindowSetBorderless(uiWindow(lobj->object), borderless);
  85. } else if (strcmp(what, "oncontentsizechanged") == 0) {
  86. lui_objectSetHandler(L, "oncontentsizechanged", 3);
  87. } else if (strcmp(what, "onclosing") == 0) {
  88. lui_objectSetHandler(L, "onclosing", 3);
  89. } else {
  90. return lui_control__newindex(L);
  91. }
  92. return 1;
  93. }
  94. static void lui_windowOnContentSizeChangedCallback(uiWindow *win, void *data)
  95. {
  96. lua_State *L = (lua_State*) data;
  97. int top = lua_gettop(L);
  98. lui_objectHandlerCallback(L, uiControl(win), "oncontentsizechanged", top, 0, 0);
  99. lua_settop(L, top);
  100. }
  101. static int lui_windowOnClosingCallback(uiWindow *win, void *data)
  102. {
  103. lua_State *L = (lua_State*) data;
  104. int top = lua_gettop(L);
  105. int kill = 1;
  106. if (lui_objectHandlerCallback(L, uiControl(win), "onclosing", top, 0, 1) != 0) {
  107. kill = lua_toboolean(L, -1);
  108. }
  109. lua_settop(L, top);
  110. /* don't close, just hide! */
  111. if (kill && lui_findObject(L, uiControl(win)) != LUA_TNIL) {
  112. uiControlHide(uiControl(win));
  113. }
  114. lua_pop(L, 1);
  115. return 0;
  116. }
  117. /*** Method
  118. * Object: window
  119. * Name: contentsize
  120. * Signature: w, h = window:contentsize(h = nil, h = nil)
  121. * set and get the content size for a window. Sets the size if the arguments
  122. * w and h are not nil, returns the content size of the window.
  123. */
  124. static int lui_windowContentsize(lua_State *L)
  125. {
  126. lui_object *lobj = lui_checkWindow(L, 1);
  127. int w, h;
  128. if (!lua_isnoneornil(L, 2) && !lua_isnoneornil(L, 3)) {
  129. w = luaL_checkinteger(L, 2);
  130. h = luaL_checkinteger(L, 3);
  131. uiWindowSetContentSize(uiWindow(lobj->object), w, h);
  132. }
  133. uiWindowContentSize(uiWindow(lobj->object), &w, &h);
  134. lua_pushinteger(L, w);
  135. lua_pushinteger(L, h);
  136. return 2;
  137. }
  138. /*** Method
  139. * Object: window
  140. * Name: setchild
  141. * Signature: control = window:setchild(control)
  142. * set the child control for a window. Returns the child control.
  143. */
  144. static int lui_windowSetChild(lua_State *L)
  145. {
  146. lui_object *lobj = lui_checkWindow(L, 1);
  147. lui_object *child = lui_checkObject(L, 2);
  148. uiWindowSetChild(uiWindow(lobj->object), child->object);
  149. lui_controlSetChild(L, 1, 2);
  150. lua_pushvalue(L, 2);
  151. return 1;
  152. }
  153. /*** Method
  154. * Object: window
  155. * Name: hide
  156. * Signature: window:hide()
  157. * hides the window. This is the same as setting window.visible = false
  158. */
  159. static int lui_windowHide(lua_State *L)
  160. {
  161. lui_object *lobj = lui_checkWindow(L, 1);
  162. uiControlHide(lobj->object);
  163. return 0;
  164. }
  165. /*** Method
  166. * Object: window
  167. * Name: show
  168. * Signature: window:show()
  169. * shows the window. This is the same as setting window.visible = true
  170. */
  171. static int lui_windowShow(lua_State *L)
  172. {
  173. lui_object *lobj = lui_checkWindow(L, 1);
  174. uiControlShow(lobj->object);
  175. return 0;
  176. }
  177. /*** Constructor
  178. * Object: window
  179. * Name: window
  180. * Signature: win = lui.window(title = "(lui window"), [width = 600, height = 600], hasmenubar = false, properties = nil)
  181. * create a new window. Every parameter is optional, with the exception
  182. * that when you specify a width, you must also specify a height. If
  183. * hasmenubar is true, the menu must have been created before the window
  184. * is created.
  185. */
  186. static int lui_newWindow(lua_State *L)
  187. {
  188. int arg = 1;
  189. const char *title = LUI_DEFAULT_WINDOW_TITLE;
  190. if (lua_isstring(L, arg)) {
  191. title = lua_tostring(L, arg++);
  192. }
  193. int width = LUI_DEFAULT_WINDOW_WIDTH;
  194. int height = LUI_DEFAULT_WINDOW_HEIGHT;
  195. if (lua_isnumber(L, arg)) {
  196. width = luaL_checkinteger(L, arg++);
  197. height = luaL_checkinteger(L, arg++);
  198. }
  199. int hasmenubar = 0;
  200. if (lua_isboolean(L, arg)) {
  201. hasmenubar = lua_toboolean(L, arg++);
  202. }
  203. int hastable = lui_aux_istable(L, arg);
  204. lui_object *lobj = lui_pushWindow(L);
  205. lobj->object = uiNewWindow(title, width, height, hasmenubar);
  206. uiWindowOnContentSizeChanged(uiWindow(lobj->object), lui_windowOnContentSizeChangedCallback, L);
  207. uiWindowOnClosing(uiWindow(lobj->object), lui_windowOnClosingCallback, L);
  208. uiControlShow(uiControl(lobj->object));
  209. if (hastable) { lui_aux_setFieldsFromTable(L, lua_gettop(L), arg); }
  210. lui_registerObject(L, lua_gettop(L));
  211. return 1;
  212. }
  213. /* metamethods for uiwindows */
  214. static const luaL_Reg lui_window_meta[] = {
  215. {"__index", lui_window__index},
  216. {"__newindex", lui_window__newindex},
  217. {0, 0}
  218. };
  219. /* methods for uiWindows */
  220. static const luaL_Reg lui_window_methods[] = {
  221. {"setchild", lui_windowSetChild},
  222. {"contentsize", lui_windowContentsize},
  223. {"hide", lui_windowHide},
  224. {"show", lui_windowShow},
  225. {0, 0}
  226. };
  227. /* box control ************************************************************/
  228. /*** Object
  229. * Name: box
  230. * a container for multiple controls. Automatically arranges contents
  231. * vertically (for a vbox) or horizontally (for a hbox).
  232. */
  233. #define LUI_BOX "lui_box"
  234. #define lui_pushBox(L) lui_pushObject(L, LUI_BOX, 1)
  235. #define lui_checkBox(L, pos) ((lui_object*)luaL_checkudata(L, pos, LUI_BOX))
  236. /*** Property
  237. * Object: box
  238. * Name: padded
  239. * true if there is / should be padding between the boxes contained controls,
  240. * false if not. Default is false.
  241. */
  242. static int lui_box__index(lua_State *L)
  243. {
  244. lui_object *lobj = lui_checkBox(L, 1);
  245. const char *what = luaL_checkstring(L, 2);
  246. if (strcmp(what, "padded") == 0) {
  247. lua_pushinteger(L, uiBoxPadded(uiBox(lobj->object)));
  248. } else {
  249. return lui_control__index(L);
  250. }
  251. return 1;
  252. }
  253. static int lui_box__newindex(lua_State *L)
  254. {
  255. lui_object *lobj = lui_checkBox(L, 1);
  256. const char *what = luaL_checkstring(L, 2);
  257. if (strcmp(what, "padded") == 0) {
  258. int padded = lua_toboolean(L, 3);
  259. uiBoxSetPadded(uiBox(lobj->object), padded);
  260. } else {
  261. return lui_control__newindex(L);
  262. }
  263. return 1;
  264. }
  265. /*** Method
  266. * Object: box
  267. * Name: append
  268. * Signature: control = box:append(control, stretchy = false)
  269. * append a control to a box. If stretchy is set to true, the control will
  270. * be expanded to fill all available space. Returns the appended control.
  271. */
  272. static int lui_boxAppend(lua_State *L)
  273. {
  274. lui_object *lobj = lui_checkBox(L, 1);
  275. lui_object *child = lui_checkObject(L, 2);
  276. int stretchy = lua_toboolean(L, 3);
  277. uiBoxAppend(uiBox(lobj->object), child->object, stretchy);
  278. lui_controlAppendChild(L, 1, 2);
  279. lua_pushvalue(L, 2);
  280. return 1;
  281. }
  282. /*** Method
  283. * Object: box
  284. * Name: delete
  285. * Signature: box:delete(index)
  286. * delete a control by index from a box. index starts at 1
  287. */
  288. static int lui_boxDelete(lua_State *L)
  289. {
  290. lui_object *lobj = lui_checkBox(L, 1);
  291. int pos = luaL_checkinteger(L, 2);
  292. uiBoxDelete(uiBox(lobj->object), pos - 1);
  293. lui_controlDelChild(L, 1, pos);
  294. return 0;
  295. }
  296. /*** Constructor
  297. * Object: box
  298. * Name: hbox
  299. * Signature: hbox = lui.hbox(properties = nil)
  300. * creates a new box whose children are arranged horizontally.
  301. */
  302. static int lui_newHBox(lua_State *L)
  303. {
  304. int hastable = lui_aux_istable(L, 1);
  305. lui_object *lobj = lui_pushBox(L);
  306. lobj->object = uiNewHorizontalBox();
  307. if (hastable) { lui_aux_setFieldsFromTable(L, lua_gettop(L), 1); }
  308. lui_registerObject(L, lua_gettop(L));
  309. return 1;
  310. }
  311. /*** Constructor
  312. * Object: box
  313. * Name: vbox
  314. * Signature: vbox = lui.vbox(properties = nil)
  315. * creates a new box whose children are arranged vertically.
  316. */
  317. static int lui_newVBox(lua_State *L)
  318. {
  319. int hastable = lui_aux_istable(L, 1);
  320. lui_object *lobj = lui_pushBox(L);
  321. lobj->object = uiNewVerticalBox();
  322. if (hastable) { lui_aux_setFieldsFromTable(L, lua_gettop(L), 1); }
  323. lui_registerObject(L, lua_gettop(L));
  324. return 1;
  325. }
  326. /* metamethods for boxes */
  327. static const luaL_Reg lui_box_meta[] = {
  328. {"__index", lui_box__index},
  329. {"__newindex", lui_box__newindex},
  330. {0, 0}
  331. };
  332. /* methods for boxes */
  333. static const luaL_Reg lui_box_methods[] = {
  334. {"append", lui_boxAppend},
  335. {"delete", lui_boxDelete},
  336. {0, 0}
  337. };
  338. /* tab controls ***********************************************************/
  339. // TODO tab margined objects, to allow for tab.margined[page]
  340. /*** Object
  341. * Name: tab
  342. * a container whose children are arranged in individual tabs.
  343. */
  344. #define LUI_TAB "lui_tab"
  345. #define lui_pushTab(L) lui_pushObject(L, LUI_TAB, 1)
  346. #define lui_checkTab(L, pos) ((lui_object*)luaL_checkudata(L, pos, LUI_TAB))
  347. /*** Property
  348. * Object: tab
  349. * Name: margined
  350. * (TODO) not implemented
  351. *** Property
  352. * Object: tab
  353. * Name: numpages
  354. * returns the number of pages in a tabbed container. This is a read-only
  355. * property.
  356. */
  357. static int lui_tab__index(lua_State *L)
  358. {
  359. lui_object *lobj = lui_checkTab(L, 1);
  360. const char *what = luaL_checkstring(L, 2);
  361. if (strcmp(what, "margined") == 0) {
  362. //lua_pushboolean(L, uiTabMargined(uiTab(lobj->object), page) != 0); TODO
  363. NOT_IMPLEMENTED;
  364. } else if (strcmp(what, "numpages") == 0) {
  365. lua_pushinteger(L, uiTabNumPages(uiTab(lobj->object)));
  366. } else {
  367. return lui_control__index(L);
  368. }
  369. return 1;
  370. }
  371. static int lui_tab__newindex(lua_State *L)
  372. {
  373. lui_object *lobj = lui_checkTab(L, 1);
  374. const char *what = luaL_checkstring(L, 2);
  375. if (strcmp(what, "margined") == 0) {
  376. //int margined = lua_toboolean(L, 3);
  377. //uiLabelSetTabMargined(uiTab(lobj->object), page, margined); TODO
  378. (void)lobj;
  379. NOT_IMPLEMENTED;
  380. } else {
  381. return lui_control__newindex(L);
  382. }
  383. return 0;
  384. }
  385. /*** Method
  386. * Object: tab
  387. * Name: append
  388. * Signature: control = tab:append(title, control)
  389. * append a control to a tabbed container. This should be a container control
  390. * like a box. Returns the appended control.
  391. */
  392. static int lui_tabAppend(lua_State *L)
  393. {
  394. lui_object *lobj = lui_checkTab(L, 1);
  395. const char *title = luaL_checkstring(L, 2);
  396. lui_object *child = lui_checkObject(L, 3);
  397. uiTabAppend(uiTab(lobj->object), title, child->object);
  398. lui_controlAppendChild(L, 1, 3);
  399. lua_pushvalue(L, 3);
  400. return 1;
  401. }
  402. /*** Method
  403. * Object: tab
  404. * Name: insertat
  405. * Signature: control = tab:insertat(title, before, control)
  406. * insert a control before a position into a tabbed container. before is an
  407. * index starting at 1. Returns the inserted control.
  408. */
  409. static int lui_tabInsertAt(lua_State *L)
  410. {
  411. lui_object *lobj = lui_checkTab(L, 1);
  412. const char *title = luaL_checkstring(L, 2);
  413. int before = luaL_checkinteger(L, 3) - 1;
  414. lui_object *child = lui_checkObject(L, 4);
  415. uiTabInsertAt(uiTab(lobj->object), title, before, child->object);
  416. lui_controlInsertChild(L, 1, before, 4);
  417. lua_pushvalue(L, 4);
  418. return 1;
  419. }
  420. /*** Method
  421. * Object: tab
  422. * Name: delete
  423. * Signature: tab:delete(index)
  424. * delete a control and its tab by index from a tabbed container. index starts
  425. * at 1
  426. */
  427. static int lui_tabDelete(lua_State *L)
  428. {
  429. lui_object *lobj = lui_checkTab(L, 1);
  430. int pos = luaL_checkinteger(L, 2) - 1;
  431. uiTabDelete(uiTab(lobj->object), pos);
  432. lui_controlDelChild(L, 1, pos);
  433. return 0;
  434. }
  435. /*** Constructor
  436. * Object: tab
  437. * Name: tab
  438. * Signature: tab = lui.tab(properties = nil)
  439. * create a new tabbed container.
  440. */
  441. static int lui_newTab(lua_State *L)
  442. {
  443. int hastable = lui_aux_istable(L, 1);
  444. lui_object *lobj = lui_pushTab(L);
  445. lobj->object = uiNewTab();
  446. if (hastable) { lui_aux_setFieldsFromTable(L, lua_gettop(L), 1); }
  447. lui_registerObject(L, lua_gettop(L));
  448. return 1;
  449. }
  450. /* metamethods for tabs */
  451. static const luaL_Reg lui_tab_meta[] = {
  452. {"__index", lui_tab__index},
  453. {"__newindex", lui_tab__newindex},
  454. {0, 0}
  455. };
  456. /* methods for tabs */
  457. static const luaL_Reg lui_tab_methods[] = {
  458. {"append", lui_tabAppend},
  459. {"insertat", lui_tabInsertAt},
  460. {"delete", lui_tabDelete},
  461. {0, 0}
  462. };
  463. /* group control **********************************************************/
  464. /*** Object
  465. * Name: group
  466. * a labelled container control.
  467. */
  468. #define LUI_GROUP "lui_group"
  469. #define lui_pushGroup(L) lui_pushObject(L, LUI_GROUP, 1)
  470. #define lui_checkGroup(L, pos) ((lui_object*)luaL_checkudata(L, pos, LUI_GROUP))
  471. /*** Property
  472. * Object: group
  473. * Name: title
  474. * the label of the group
  475. *** Property
  476. * Object: group
  477. * Name: margined
  478. * true if the group has / should have a margin around its contents, false if
  479. * not. Default is false.
  480. */
  481. static int lui_group__index(lua_State *L)
  482. {
  483. lui_object *lobj = lui_checkGroup(L, 1);
  484. const char *what = luaL_checkstring(L, 2);
  485. if (strcmp(what, "title") == 0) {
  486. char *text = uiGroupTitle(uiGroup(lobj->object));
  487. lua_pushstring(L, text);
  488. uiFreeText(text);
  489. } else if (strcmp(what, "margined") == 0) {
  490. lua_pushboolean(L, uiGroupMargined(uiGroup(lobj->object)) != 0);
  491. } else {
  492. return lui_control__index(L);
  493. }
  494. return 1;
  495. }
  496. static int lui_group__newindex(lua_State *L)
  497. {
  498. lui_object *lobj = lui_checkGroup(L, 1);
  499. const char *what = luaL_checkstring(L, 2);
  500. if (strcmp(what, "title") == 0) {
  501. const char *title = lua_tostring(L, 3);
  502. uiGroupSetTitle(uiGroup(lobj->object), title);
  503. } else if (strcmp(what, "margined") == 0) {
  504. int margined = lua_toboolean(L, 3);
  505. uiGroupSetMargined(uiGroup(lobj->object), margined);
  506. } else {
  507. return lui_control__newindex(L);
  508. }
  509. return 0;
  510. }
  511. /*** Method
  512. * Object: group
  513. * Name: setchild
  514. * Signature: group:setchild(control)
  515. * set the child control for a group control. Returns the child control.
  516. */
  517. static int lui_groupSetChild(lua_State *L)
  518. {
  519. lui_object *lobj = lui_checkGroup(L, 1);
  520. lui_object *child = lui_checkObject(L, 2);
  521. uiGroupSetChild(uiGroup(lobj->object), child->object);
  522. lui_controlSetChild(L, 1, 2);
  523. lua_pushvalue(L, 2);
  524. return 1;
  525. }
  526. /*** Constructor
  527. * Object: group
  528. * Name: group
  529. * Signature: group = lui.group(title, properties = nil)
  530. * create a new group control.
  531. */
  532. static int lui_newGroup(lua_State *L)
  533. {
  534. const char *title = luaL_checkstring(L, 1);
  535. int hastable = lui_aux_istable(L, 2);
  536. lui_object *lobj = lui_pushGroup(L);
  537. lobj->object = uiNewGroup(title);
  538. if (hastable) { lui_aux_setFieldsFromTable(L, lua_gettop(L), 2); }
  539. lui_registerObject(L, lua_gettop(L));
  540. return 1;
  541. }
  542. /* metamethods for groups */
  543. static const luaL_Reg lui_group_meta[] = {
  544. {"__index", lui_group__index},
  545. {"__newindex", lui_group__newindex},
  546. {0, 0}
  547. };
  548. /* methods for groups */
  549. static const luaL_Reg lui_group_methods[] = {
  550. {"setchild", lui_groupSetChild},
  551. {0, 0}
  552. };
  553. /* form control ************************************************************/
  554. /*** Object
  555. * Name: form
  556. * a container, which arranges its children vertically and aligns the labels
  557. * and controls.
  558. */
  559. #define LUI_FORM "lui_form"
  560. #define lui_pushForm(L) lui_pushObject(L, LUI_FORM, 1)
  561. #define lui_checkForm(L, pos) ((lui_object*)luaL_checkudata(L, pos, LUI_FORM))
  562. /*** Property
  563. * Object: form
  564. * Name: padded
  565. * true if there is / should be padding between the forms children, false if
  566. * not. Default is false.
  567. */
  568. static int lui_form__index(lua_State *L)
  569. {
  570. lui_object *lobj = lui_checkForm(L, 1);
  571. const char *what = luaL_checkstring(L, 2);
  572. if (strcmp(what, "padded") == 0) {
  573. lua_pushinteger(L, uiFormPadded(uiForm(lobj->object)));
  574. } else {
  575. return lui_control__index(L);
  576. }
  577. return 1;
  578. }
  579. static int lui_form__newindex(lua_State *L)
  580. {
  581. lui_object *lobj = lui_checkForm(L, 1);
  582. const char *what = luaL_checkstring(L, 2);
  583. if (strcmp(what, "padded") == 0) {
  584. int padded = lua_toboolean(L, 3);
  585. uiFormSetPadded(uiForm(lobj->object), padded);
  586. } else {
  587. return lui_control__newindex(L);
  588. }
  589. return 1;
  590. }
  591. /*** Method
  592. * Object: form
  593. * Name: append
  594. * Signature: control = form:append(label, control, stretchy = false)
  595. * append a control to a form. If stretchy is set to true, the control will
  596. * be expanded to fill all available space. Returns the appended control.
  597. */
  598. static int lui_formAppend(lua_State *L)
  599. {
  600. lui_object *lobj = lui_checkForm(L, 1);
  601. const char *label = luaL_checkstring(L, 2);
  602. lui_object *child = lui_checkObject(L, 3);
  603. int stretchy = lua_toboolean(L, 4);
  604. uiFormAppend(uiForm(lobj->object), label, child->object, stretchy);
  605. lui_controlAppendChild(L, 1, 3);
  606. lua_pushvalue(L, 3);
  607. return 1;
  608. }
  609. /*** Method
  610. * Object: form
  611. * Name: delete
  612. * Signature: form:delete(control, index)
  613. * delete a control by index from a form. index starts at 1.
  614. */
  615. static int lui_formDelete(lua_State *L)
  616. {
  617. lui_object *lobj = lui_checkForm(L, 1);
  618. int pos = luaL_checkinteger(L, 2);
  619. uiFormDelete(uiForm(lobj->object), pos - 1);
  620. lui_controlDelChild(L, 1, pos);
  621. return 0;
  622. }
  623. /*** Constructor
  624. * Object: form
  625. * Name: form
  626. * Signature: form = lui.form(properties = nil)
  627. * create a new form control.
  628. */
  629. static int lui_newForm(lua_State *L)
  630. {
  631. int hastable = lui_aux_istable(L, 1);
  632. lui_object *lobj = lui_pushForm(L);
  633. lobj->object = uiNewForm();
  634. if (hastable) { lui_aux_setFieldsFromTable(L, lua_gettop(L), 1); }
  635. lui_registerObject(L, lua_gettop(L));
  636. return 1;
  637. }
  638. /* metamethods for forms */
  639. static const luaL_Reg lui_form_meta[] = {
  640. {"__index", lui_form__index},
  641. {"__newindex", lui_form__newindex},
  642. {0, 0}
  643. };
  644. /* methods for forms */
  645. static const luaL_Reg lui_form_methods[] = {
  646. {"append", lui_formAppend},
  647. {"delete", lui_formDelete},
  648. {0, 0}
  649. };
  650. /* grid control ************************************************************/
  651. /*** Object
  652. * Name: grid
  653. * a container, which arranges its children in a grid.
  654. */
  655. #define LUI_GRID "lui_grid"
  656. #define lui_pushGrid(L) lui_pushObject(L, LUI_GRID, 1)
  657. #define lui_checkGrid(L, pos) ((lui_object*)luaL_checkudata(L, pos, LUI_GRID))
  658. /*** Property
  659. * Object: grid
  660. * Name: padded
  661. * true if there is / should be padding between the grids children, false if
  662. * not. Default is false.
  663. */
  664. static int lui_grid__index(lua_State *L)
  665. {
  666. lui_object *lobj = lui_checkGrid(L, 1);
  667. const char *what = luaL_checkstring(L, 2);
  668. if (strcmp(what, "padded") == 0) {
  669. lua_pushinteger(L, uiGridPadded(uiGrid(lobj->object)));
  670. } else {
  671. return lui_control__index(L);
  672. }
  673. return 1;
  674. }
  675. static int lui_grid__newindex(lua_State *L)
  676. {
  677. lui_object *lobj = lui_checkGrid(L, 1);
  678. const char *what = luaL_checkstring(L, 2);
  679. if (strcmp(what, "padded") == 0) {
  680. int padded = lua_toboolean(L, 3);
  681. uiGridSetPadded(uiGrid(lobj->object), padded);
  682. } else {
  683. return lui_control__newindex(L);
  684. }
  685. return 1;
  686. }
  687. static int lui_makeAlignEnum(lua_State *L)
  688. {
  689. lua_newtable(L);
  690. lui_enumItem("fill", uiAlignFill);
  691. lui_enumItem("start", uiAlignStart);
  692. lui_enumItem("center", uiAlignCenter);
  693. lui_enumItem("end", uiAlignEnd);
  694. return 1;
  695. }
  696. static int lui_makeAtEnum(lua_State *L)
  697. {
  698. lua_newtable(L);
  699. lui_enumItem("leading", uiAtLeading);
  700. lui_enumItem("top", uiAtTop);
  701. lui_enumItem("trailing", uiAtTrailing);
  702. lui_enumItem("bottom", uiAtBottom);
  703. return 1;
  704. }
  705. /*** Method
  706. * Object: grid
  707. * Name: append
  708. * Signature: control = grid:append(control, left, top, xspan = 1, yspan = 1, hexpand = false, halign = "fill", vexpand = false, valign = "fill")
  709. * append a control to a grid. left and top are the coordinates of the cell
  710. * where the control should be placed, starting at 1. xspan and yspan are
  711. * how many cells the control spans in either direction, minimum is 1. Set
  712. * hexpand to true to horizontally expand the control to fill available
  713. * space, false to not do that. Set halign to "fill", "start", "center" or
  714. * "end", or lui.enum.align.fill, ... to set horizontal alignment. vexpand
  715. * and valign are the same for the vertical case.
  716. */
  717. static int lui_gridAppend(lua_State *L)
  718. {
  719. lui_object *lobj = lui_checkGrid(L, 1);
  720. lui_object *child = lui_checkObject(L, 2);
  721. int left = luaL_checkinteger(L, 3) - 1;
  722. int top = luaL_checkinteger(L, 4) - 1;
  723. int xspan = luaL_optinteger(L, 5, 1);
  724. int yspan = luaL_optinteger(L, 6, 1);
  725. int hexpand = lua_toboolean(L, 7);
  726. int halign = uiAlignFill;
  727. if (lua_gettop(L) >= 8) {
  728. halign = lui_aux_getNumberOrValue(L, 8, "lui_enumalign");
  729. }
  730. int vexpand = lua_toboolean(L, 9);
  731. int valign = uiAlignFill;
  732. if (lua_gettop(L) >= 10) {
  733. valign = lui_aux_getNumberOrValue(L, 10, "lui_enumalign");
  734. }
  735. uiGridAppend(uiGrid(lobj->object), uiControl(child->object), left, top, xspan, yspan, hexpand, halign, vexpand, valign);
  736. lui_controlAppendChild(L, 1, 2);
  737. lua_pushvalue(L, 2);
  738. return 1;
  739. }
  740. /*** Method
  741. * Object: grid
  742. * Name: insertat
  743. * Signature: control = grid:insertat(control, existing, at, xspan = 1, yspan = 1, hexpand = false, halign = "fill", vexpand = false, valign = "fill")
  744. * insert a control into a grid relative to another control that already
  745. * exists in the grid. at is any of "leading", "trailing", "top" or "bottom",
  746. * or lui.enum.at.leading, .... The other arguments have the same meaning as
  747. * with grid:append().
  748. */
  749. static int lui_gridInsertAt(lua_State *L)
  750. {
  751. lui_object *lobj = lui_checkGrid(L, 1);
  752. lui_object *child = lui_checkObject(L, 2);
  753. lui_object *existing = lui_checkObject(L, 3);
  754. int at = lui_aux_getNumberOrValue(L, 4, "lui_enumat");
  755. int xspan = luaL_optinteger(L, 5, 1);
  756. int yspan = luaL_optinteger(L, 6, 1);
  757. int hexpand = lua_toboolean(L, 7);
  758. int halign = uiAlignFill;
  759. if (lua_gettop(L) >= 8) {
  760. halign = lui_aux_getNumberOrValue(L, 8, "lui_enumalign");
  761. }
  762. int vexpand = lua_toboolean(L, 9);
  763. int valign = uiAlignFill;
  764. if (lua_gettop(L) >= 10) {
  765. valign = lui_aux_getNumberOrValue(L, 10, "lui_enumalign");
  766. }
  767. uiGridInsertAt(uiGrid(lobj->object), uiControl(child->object), uiControl(existing->object), at, xspan, yspan, hexpand, halign, vexpand, valign);
  768. lui_controlAppendChild(L, 1, 2);
  769. lua_pushvalue(L, 2);
  770. return 1;
  771. }
  772. /*** Constructor
  773. * Object: grid
  774. * Name: grid
  775. * Signature: grid = lui.grid(properties = nil)
  776. * create a new grid control.
  777. */
  778. static int lui_newGrid(lua_State *L)
  779. {
  780. int hastable = lui_aux_istable(L, 1);
  781. lui_object *lobj = lui_pushGrid(L);
  782. lobj->object = uiNewGrid();
  783. if (hastable) { lui_aux_setFieldsFromTable(L, lua_gettop(L), 1); }
  784. lui_registerObject(L, lua_gettop(L));
  785. return 1;
  786. }
  787. /* metamethods for grids */
  788. static const luaL_Reg lui_grid_meta[] = {
  789. {"__index", lui_grid__index},
  790. {"__newindex", lui_grid__newindex},
  791. {0, 0}
  792. };
  793. /* methods for grids */
  794. static const luaL_Reg lui_grid_methods[] = {
  795. {"append", lui_gridAppend},
  796. {"insertat", lui_gridInsertAt},
  797. {0, 0}
  798. };
  799. /* container function list table
  800. */
  801. static const struct luaL_Reg lui_container_funcs [] ={
  802. {"window", lui_newWindow},
  803. {"hbox", lui_newHBox},
  804. {"vbox", lui_newVBox},
  805. {"tab", lui_newTab},
  806. {"group", lui_newGroup},
  807. {"form", lui_newForm},
  808. {"grid", lui_newGrid},
  809. {0, 0}
  810. };
  811. static void lui_addContainerEnums(lua_State *L)
  812. {
  813. int top = lua_gettop(L);
  814. if (lua_getfield(L, top, "enum") == LUA_TNIL) {
  815. lua_pop(L, 1);
  816. lua_newtable(L);
  817. }
  818. lui_makeAlignEnum(L);
  819. lua_pushvalue(L, -1);
  820. lua_setfield(L, top + 1, "align");
  821. lua_setfield(L, LUA_REGISTRYINDEX, "lui_enumalign");
  822. lui_makeAtEnum(L);
  823. lua_pushvalue(L, -1);
  824. lua_setfield(L, top + 1, "at");
  825. lua_setfield(L, LUA_REGISTRYINDEX, "lui_enumat");
  826. lua_setfield(L, top, "enum");
  827. }
  828. static int lui_init_container(lua_State *L)
  829. {
  830. luaL_setfuncs(L, lui_container_funcs, 0);
  831. lui_add_control_type(L, LUI_WINDOW, lui_window_methods, lui_window_meta);
  832. lui_add_control_type(L, LUI_BOX, lui_box_methods, lui_box_meta);
  833. lui_add_control_type(L, LUI_TAB, lui_tab_methods, lui_tab_meta);
  834. lui_add_control_type(L, LUI_GROUP, lui_group_methods, lui_group_meta);
  835. lui_add_control_type(L, LUI_FORM, lui_form_methods, lui_form_meta);
  836. lui_add_control_type(L, LUI_GRID, lui_grid_methods, lui_grid_meta);
  837. lui_addContainerEnums(L);
  838. return 1;
  839. }