wxruby3 0.9.2 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (347) hide show
  1. checksums.yaml +4 -4
  2. data/INSTALL.md +35 -29
  3. data/README.md +24 -15
  4. data/ext/wxruby3/include/wxruby-ClientData.h +48 -0
  5. data/ext/wxruby3/include/wxruby-Validator.h +39 -0
  6. data/ext/wxruby3/include/wxruby-ValidatorBinding.h +64 -0
  7. data/ext/wxruby3/swig/custom/director.swg +8 -0
  8. data/ext/wxruby3/swig/mark_free_impl.i +0 -33
  9. data/lib/wx/core/bitmap_combobox.rb +17 -21
  10. data/lib/wx/core/combobox.rb +13 -3
  11. data/lib/wx/core/controlwithitems.rb +40 -130
  12. data/lib/wx/core/enum.rb +17 -1
  13. data/lib/wx/core/event.rb +6 -1
  14. data/lib/wx/core/evthandler.rb +8 -2
  15. data/lib/wx/core/generic_validator.rb +312 -0
  16. data/lib/wx/core/listbox.rb +2 -2
  17. data/lib/wx/core/log.rb +1 -1
  18. data/lib/wx/core/owner_drawn_combobox.rb +45 -0
  19. data/lib/wx/core/size.rb +4 -0
  20. data/lib/wx/core/sizer.rb +1 -2
  21. data/lib/wx/core/standard_paths.rb +22 -0
  22. data/lib/wx/core/text_entry.rb +4 -1
  23. data/lib/wx/core/validator.rb +70 -5
  24. data/lib/wx/core/window.rb +3 -7
  25. data/lib/wx/doc/art_locator.rb +1 -0
  26. data/lib/wx/doc/comboctrl.rb +18 -0
  27. data/lib/wx/doc/controlwithitems.rb +16 -0
  28. data/lib/wx/doc/date_picker_ctrl.rb +23 -0
  29. data/lib/wx/doc/editable_listbox.rb +44 -0
  30. data/lib/wx/doc/enum.rb +15 -0
  31. data/lib/wx/doc/event.rb +12 -0
  32. data/lib/wx/doc/extra/12_client_data.md +89 -0
  33. data/lib/wx/doc/extra/13_validators.md +139 -0
  34. data/lib/wx/doc/gdi_common.rb +4 -0
  35. data/lib/wx/doc/generic_validator.rb +95 -0
  36. data/lib/wx/doc/html/simple_html_listbox.rb +22 -0
  37. data/lib/wx/doc/num_validator.rb +387 -0
  38. data/lib/wx/doc/owner_drawn_combobox.rb +96 -0
  39. data/lib/wx/doc/pg/numeric_property_validator.rb +33 -0
  40. data/lib/wx/doc/static_bitmap.rb +25 -0
  41. data/lib/wx/doc/text_validator.rb +10 -0
  42. data/lib/wx/doc/validator.rb +113 -0
  43. data/lib/wx/doc/window.rb +30 -0
  44. data/lib/wx/html/simple_html_listbox.rb +24 -91
  45. data/lib/wx/keyword_defs.rb +34 -2
  46. data/lib/wx/rbn/ribbon_gallery.rb +0 -21
  47. data/lib/wx/version.rb +1 -1
  48. data/rakelib/lib/config/mingw.rb +12 -0
  49. data/rakelib/lib/core/include/client_data.inc +65 -0
  50. data/rakelib/lib/core/include/funcall.inc +1 -1
  51. data/rakelib/lib/core/package.rb +17 -2
  52. data/rakelib/lib/core/parameter.rb +1 -1
  53. data/rakelib/lib/core/spec_helper.rb +7 -1
  54. data/rakelib/lib/director/app.rb +18 -0
  55. data/rakelib/lib/director/app_traits.rb +3 -1
  56. data/rakelib/lib/director/bitmap_combobox.rb +2 -2
  57. data/rakelib/lib/director/combobox.rb +22 -0
  58. data/rakelib/lib/director/comboctrl.rb +1 -55
  59. data/rakelib/lib/director/ctrl_with_items.rb +88 -21
  60. data/rakelib/lib/director/date_picker_ctrl.rb +8 -6
  61. data/rakelib/lib/director/editable_listbox.rb +30 -0
  62. data/rakelib/lib/director/event.rb +102 -12
  63. data/rakelib/lib/director/event_handler.rb +3 -2
  64. data/rakelib/lib/director/functions.rb +4 -2
  65. data/rakelib/lib/director/generic_dirctrl.rb +17 -0
  66. data/rakelib/lib/director/grid_cell_attr.rb +0 -2
  67. data/rakelib/lib/director/grid_cell_editor.rb +0 -2
  68. data/rakelib/lib/director/grid_cell_renderer.rb +0 -2
  69. data/rakelib/lib/director/header_ctrl.rb +6 -1
  70. data/rakelib/lib/director/html_listbox.rb +96 -22
  71. data/rakelib/lib/director/num_validator.rb +495 -0
  72. data/rakelib/lib/director/numeric_property_validator.rb +131 -6
  73. data/rakelib/lib/director/owner_drawn_combobox.rb +192 -0
  74. data/rakelib/lib/director/pgproperty.rb +3 -29
  75. data/rakelib/lib/director/ribbon_button_bar.rb +3 -3
  76. data/rakelib/lib/director/ribbon_gallery.rb +3 -5
  77. data/rakelib/lib/director/sizer.rb +15 -4
  78. data/rakelib/lib/director/sizer_item.rb +13 -0
  79. data/rakelib/lib/director/static_bitmap.rb +28 -0
  80. data/rakelib/lib/director/text_entry.rb +4 -0
  81. data/rakelib/lib/director/text_validator.rb +124 -13
  82. data/rakelib/lib/director/toggle_button.rb +1 -0
  83. data/rakelib/lib/director/top_level_window.rb +0 -2
  84. data/rakelib/lib/director/tree_ctrl.rb +35 -0
  85. data/rakelib/lib/director/utils.rb +16 -2
  86. data/rakelib/lib/director/validator.rb +196 -21
  87. data/rakelib/lib/director/window.rb +16 -2
  88. data/rakelib/lib/extractor/function.rb +2 -1
  89. data/rakelib/lib/generate/analyzer.rb +1 -1
  90. data/rakelib/lib/generate/doc.rb +10 -4
  91. data/rakelib/lib/specs/interfaces.rb +2 -0
  92. data/rakelib/lib/swig_runner.rb +55 -9
  93. data/rakelib/lib/typemap/client_data.rb +67 -0
  94. data/rakelib/lib/typemap/common.rb +3 -3
  95. data/rakelib/lib/typemap/datetime.rb +1 -1
  96. data/rakelib/lib/typemap/tree_itemid.rb +2 -2
  97. data/samples/aui/aui.rb +1237 -1233
  98. data/samples/calendar/calendar.rb +293 -289
  99. data/samples/controls/get_item_sample.rb +83 -79
  100. data/samples/controls/htlbox.rb +331 -327
  101. data/samples/dialogs/dialogs.rb +682 -694
  102. data/samples/dialogs/wizard.rb +52 -55
  103. data/samples/dragdrop/dragdrop.rb +146 -142
  104. data/samples/drawing/bitmap.rb +29 -26
  105. data/samples/drawing/bitmap_image.rb +84 -80
  106. data/samples/drawing/graphics_drawing.rb +209 -205
  107. data/samples/drawing/image_prt.rb +344 -340
  108. data/samples/drawing/maths_images.rb +18 -1
  109. data/samples/drawing/rmagic_bitmap_image.rb +88 -84
  110. data/samples/etc/caret.rb +306 -0
  111. data/samples/etc/miniframe.rb +65 -60
  112. data/samples/etc/sash.rb +109 -105
  113. data/samples/etc/scrollwin.rb +86 -80
  114. data/samples/etc/system_settings.rb +216 -209
  115. data/samples/event/activation.rb +72 -67
  116. data/samples/event/event.rb +160 -153
  117. data/samples/event/threaded.rb +8 -1
  118. data/samples/event/update_ui_event.rb +67 -60
  119. data/samples/grid/grid.rb +188 -181
  120. data/samples/grid/gridtablebase.rb +136 -129
  121. data/samples/html/html.rb +208 -204
  122. data/samples/mdi/mdi.rb +59 -54
  123. data/samples/minimal/minimal.rb +54 -51
  124. data/samples/minimal/nothing.rb +1 -0
  125. data/samples/printing/printing.rb +367 -377
  126. data/samples/printing/printing2.rb +203 -198
  127. data/samples/propgrid/propgrid.rb +2312 -2308
  128. data/samples/propgrid/propgrid_minimal.rb +57 -50
  129. data/samples/ribbon/ribbon.rb +780 -774
  130. data/samples/sampler/ext.rb +0 -33
  131. data/samples/sampler/sample.rb +4 -9
  132. data/samples/sampler.rb +1 -1
  133. data/samples/splash/splash.rb +118 -116
  134. data/samples/text/rich_textctrl.rb +207 -201
  135. data/samples/text/richtext.rb +1409 -1405
  136. data/samples/text/scintilla.rb +137 -128
  137. data/samples/text/textctrl.rb +89 -83
  138. data/samples/text/unicode.rb +175 -168
  139. data/samples/treectrl/treectrl.rb +1430 -1426
  140. data/samples/widgets/activityindicator.rb +95 -0
  141. data/samples/widgets/art/widgets/activityindicator.xpm +278 -0
  142. data/samples/widgets/art/widgets/bmpbtn.xpm +37 -0
  143. data/samples/widgets/art/widgets/bmpcombobox.xpm +54 -0
  144. data/samples/widgets/art/widgets/button.xpm +54 -0
  145. data/samples/widgets/art/widgets/checkbox.xpm +54 -0
  146. data/samples/{bigdemo/icons → widgets/art/widgets}/choice.xpm +1 -1
  147. data/samples/widgets/art/widgets/choicebk.xpm +54 -0
  148. data/samples/widgets/art/widgets/clrpicker.xpm +193 -0
  149. data/samples/widgets/art/widgets/combobox.xpm +54 -0
  150. data/samples/widgets/art/widgets/datepick.xpm +200 -0
  151. data/samples/widgets/art/widgets/dirctrl.xpm +54 -0
  152. data/samples/widgets/art/widgets/dirpicker.xpm +213 -0
  153. data/samples/widgets/art/widgets/filepicker.xpm +214 -0
  154. data/samples/widgets/art/widgets/fontpicker.xpm +185 -0
  155. data/samples/widgets/art/widgets/gauge.xpm +54 -0
  156. data/samples/widgets/art/widgets/header.xpm +54 -0
  157. data/samples/widgets/art/widgets/hyperlnk.xpm +54 -0
  158. data/samples/widgets/art/widgets/listbook.xpm +54 -0
  159. data/samples/widgets/art/widgets/listbox.xpm +54 -0
  160. data/samples/widgets/art/widgets/native.xpm +81 -0
  161. data/samples/widgets/art/widgets/notebook.xpm +54 -0
  162. data/samples/widgets/art/widgets/odcombobox.xpm +54 -0
  163. data/samples/widgets/art/widgets/radiobox.xpm +54 -0
  164. data/samples/widgets/art/widgets/scrolbar.xpm +54 -0
  165. data/samples/widgets/art/widgets/slider.xpm +54 -0
  166. data/samples/widgets/art/widgets/spinbtn.xpm +40 -0
  167. data/samples/widgets/art/widgets/statbmp.xpm +40 -0
  168. data/samples/widgets/art/widgets/statbox.xpm +54 -0
  169. data/samples/widgets/art/widgets/stattext.xpm +54 -0
  170. data/samples/widgets/art/widgets/text.xpm +54 -0
  171. data/samples/widgets/art/widgets/timepick.xpm +207 -0
  172. data/samples/widgets/art/widgets/toggle.xpm +54 -0
  173. data/samples/widgets/art/widgets/toucan.png +0 -0
  174. data/samples/widgets/bmpcombobox.rb +651 -0
  175. data/samples/widgets/button.rb +462 -0
  176. data/samples/widgets/checkbox.rb +211 -0
  177. data/samples/widgets/choice.rb +287 -0
  178. data/samples/widgets/clrpicker.rb +156 -0
  179. data/samples/widgets/combobox.rb +516 -0
  180. data/samples/widgets/datepick.rb +215 -0
  181. data/samples/widgets/dirctrl.rb +265 -0
  182. data/samples/widgets/dirpicker.rb +158 -0
  183. data/samples/widgets/editlbox.rb +122 -0
  184. data/samples/widgets/filectrl.rb +216 -0
  185. data/samples/widgets/filepicker.rb +214 -0
  186. data/samples/widgets/fontpicker.rb +135 -0
  187. data/samples/widgets/gauge.rb +311 -0
  188. data/samples/widgets/headerctrl.rb +236 -0
  189. data/samples/widgets/hyperlink.rb +203 -0
  190. data/samples/widgets/itemcontainer.rb +185 -0
  191. data/samples/widgets/listbox.rb +473 -0
  192. data/samples/widgets/notebook.rb +488 -0
  193. data/samples/widgets/odcombobox.rb +608 -0
  194. data/samples/widgets/radiobox.rb +328 -0
  195. data/samples/widgets/searchctrl.rb +159 -0
  196. data/samples/widgets/slider.rb +594 -0
  197. data/samples/widgets/spinbtn.rb +443 -0
  198. data/samples/widgets/statbmp.rb +130 -0
  199. data/samples/widgets/static.rb +436 -0
  200. data/samples/widgets/textctrl.rb +700 -0
  201. data/samples/widgets/timepick.rb +148 -0
  202. data/samples/widgets/tn_widgets.png +0 -0
  203. data/samples/widgets/toggle.rb +385 -0
  204. data/samples/widgets/widgets.rb +1233 -0
  205. data/samples/xrc/custom_xrc_sample.rb +47 -41
  206. data/samples/xrc/xrc_sample.rb +53 -47
  207. data/tests/test_event_handling.rb +52 -34
  208. data/tests/test_events.rb +7 -0
  209. data/tests/test_item_data.rb +24 -0
  210. data/tests/test_sizer.rb +44 -1
  211. data/tests/test_validators.rb +805 -0
  212. metadata +92 -138
  213. data/lib/wx/core/choice.rb +0 -14
  214. data/lib/wx/core/combo_ctrl.rb +0 -110
  215. data/rakelib/lib/typemap/grid_client_data.rb +0 -58
  216. data/samples/bigdemo/About.rbw +0 -39
  217. data/samples/bigdemo/ColorPanel.rbw +0 -23
  218. data/samples/bigdemo/GridSimple.rbw +0 -78
  219. data/samples/bigdemo/MDIDemo.rbw +0 -57
  220. data/samples/bigdemo/PopupMenu.rbw +0 -146
  221. data/samples/bigdemo/ShapedWindow.rbw +0 -128
  222. data/samples/bigdemo/Sizers.rbw +0 -541
  223. data/samples/bigdemo/bigdemo.rb +0 -817
  224. data/samples/bigdemo/demoTemplate.rbw +0 -33
  225. data/samples/bigdemo/helpfile.htb +0 -0
  226. data/samples/bigdemo/icons/Test 015.jpg +0 -0
  227. data/samples/bigdemo/icons/Test 015.png +0 -0
  228. data/samples/bigdemo/icons/choice.bmp +0 -0
  229. data/samples/bigdemo/icons/combo.bmp +0 -0
  230. data/samples/bigdemo/icons/combo.xpm +0 -27
  231. data/samples/bigdemo/icons/copy.xpm +0 -25
  232. data/samples/bigdemo/icons/cut.xpm +0 -24
  233. data/samples/bigdemo/icons/gauge.bmp +0 -0
  234. data/samples/bigdemo/icons/gauge.xpm +0 -27
  235. data/samples/bigdemo/icons/help.xpm +0 -25
  236. data/samples/bigdemo/icons/list.bmp +0 -0
  237. data/samples/bigdemo/icons/list.xpm +0 -27
  238. data/samples/bigdemo/icons/mondrian.ico +0 -0
  239. data/samples/bigdemo/icons/mondrian.xpm +0 -44
  240. data/samples/bigdemo/icons/new.xpm +0 -24
  241. data/samples/bigdemo/icons/ogl.ico +0 -0
  242. data/samples/bigdemo/icons/ogl.xpm +0 -45
  243. data/samples/bigdemo/icons/open.xpm +0 -26
  244. data/samples/bigdemo/icons/paste.bmp +0 -0
  245. data/samples/bigdemo/icons/paste.xpm +0 -38
  246. data/samples/bigdemo/icons/pointy.png +0 -0
  247. data/samples/bigdemo/icons/preview.xpm +0 -26
  248. data/samples/bigdemo/icons/print.xpm +0 -26
  249. data/samples/bigdemo/icons/radio.bmp +0 -0
  250. data/samples/bigdemo/icons/radio.xpm +0 -27
  251. data/samples/bigdemo/icons/robert.xpm +0 -415
  252. data/samples/bigdemo/icons/sashtest.ico +0 -0
  253. data/samples/bigdemo/icons/save.xpm +0 -25
  254. data/samples/bigdemo/icons/smiles.bmp +0 -0
  255. data/samples/bigdemo/icons/smiles.xpm +0 -39
  256. data/samples/bigdemo/icons/smiley.ico +0 -0
  257. data/samples/bigdemo/icons/smiley.xpm +0 -42
  258. data/samples/bigdemo/icons/stattext.xpm +0 -24
  259. data/samples/bigdemo/icons/test2.bmp +0 -0
  260. data/samples/bigdemo/icons/test2.png +0 -0
  261. data/samples/bigdemo/icons/test2.xpm +0 -79
  262. data/samples/bigdemo/icons/text.bmp +0 -0
  263. data/samples/bigdemo/icons/text.xpm +0 -27
  264. data/samples/bigdemo/icons/tog1.bmp +0 -0
  265. data/samples/bigdemo/icons/tog1.xpm +0 -38
  266. data/samples/bigdemo/icons/tog2.bmp +0 -0
  267. data/samples/bigdemo/icons/tog2.xpm +0 -38
  268. data/samples/bigdemo/icons/wxruby-128x128.png +0 -0
  269. data/samples/bigdemo/icons/wxwin.ico +0 -0
  270. data/samples/bigdemo/icons/wxwin16x16.png +0 -0
  271. data/samples/bigdemo/icons/wxwin16x16.xpm +0 -25
  272. data/samples/bigdemo/icons/wxwin32x32.png +0 -0
  273. data/samples/bigdemo/icons/wxwin48x48.png +0 -0
  274. data/samples/bigdemo/run.rb +0 -98
  275. data/samples/bigdemo/tips.txt +0 -7
  276. data/samples/bigdemo/utils.rb +0 -20
  277. data/samples/bigdemo/wxArtProvider.rbw +0 -282
  278. data/samples/bigdemo/wxBitmapButton.rbw +0 -65
  279. data/samples/bigdemo/wxButton.rbw +0 -64
  280. data/samples/bigdemo/wxCalendarCtrl.rbw +0 -59
  281. data/samples/bigdemo/wxCheckBox.rbw +0 -50
  282. data/samples/bigdemo/wxCheckListBox.rbw +0 -65
  283. data/samples/bigdemo/wxChoice.rbw +0 -47
  284. data/samples/bigdemo/wxChoicebook.rbw +0 -78
  285. data/samples/bigdemo/wxColourDialog.rbw +0 -33
  286. data/samples/bigdemo/wxComboBox.rbw +0 -77
  287. data/samples/bigdemo/wxCursor.rbw +0 -136
  288. data/samples/bigdemo/wxDialog.rbw +0 -74
  289. data/samples/bigdemo/wxDirDialog.rbw +0 -29
  290. data/samples/bigdemo/wxDragImage.rbw +0 -70
  291. data/samples/bigdemo/wxFileDialog.rbw +0 -37
  292. data/samples/bigdemo/wxFileDialog_Save.rbw +0 -35
  293. data/samples/bigdemo/wxFindReplaceDialog.rbw +0 -82
  294. data/samples/bigdemo/wxFontDialog.rbw +0 -200
  295. data/samples/bigdemo/wxFrame.rbw +0 -53
  296. data/samples/bigdemo/wxGauge.rbw +0 -65
  297. data/samples/bigdemo/wxGenericDirCtrl.rbw +0 -74
  298. data/samples/bigdemo/wxGrid.rbw +0 -66
  299. data/samples/bigdemo/wxHtmlHelpController.rbw +0 -52
  300. data/samples/bigdemo/wxListBox.rbw +0 -140
  301. data/samples/bigdemo/wxListCtrl_virtual.rbw +0 -112
  302. data/samples/bigdemo/wxMDIWindows.rbw +0 -50
  303. data/samples/bigdemo/wxMenu.rbw +0 -247
  304. data/samples/bigdemo/wxMessageDialog.rbw +0 -27
  305. data/samples/bigdemo/wxMiniFrame.rbw +0 -70
  306. data/samples/bigdemo/wxMultipleChoiceDialog.rbw +0 -29
  307. data/samples/bigdemo/wxNotebook.rbw +0 -136
  308. data/samples/bigdemo/wxProgressDialog.rbw +0 -43
  309. data/samples/bigdemo/wxRadioBox.rbw +0 -72
  310. data/samples/bigdemo/wxRadioButton.rbw +0 -125
  311. data/samples/bigdemo/wxSashWindow.rbw +0 -141
  312. data/samples/bigdemo/wxScrolledMessageDialog.rbw +0 -57
  313. data/samples/bigdemo/wxScrolledWindow.rbw +0 -199
  314. data/samples/bigdemo/wxSingleChoiceDialog.rbw +0 -33
  315. data/samples/bigdemo/wxSlider.rbw +0 -42
  316. data/samples/bigdemo/wxSpinButton.rbw +0 -50
  317. data/samples/bigdemo/wxSpinCtrl.rbw +0 -51
  318. data/samples/bigdemo/wxSplitterWindow.rbw +0 -63
  319. data/samples/bigdemo/wxStaticBitmap.rbw +0 -47
  320. data/samples/bigdemo/wxStaticText.rbw +0 -55
  321. data/samples/bigdemo/wxStatusBar.rbw +0 -126
  322. data/samples/bigdemo/wxTextCtrl.rbw +0 -149
  323. data/samples/bigdemo/wxTextEntryDialog.rbw +0 -31
  324. data/samples/bigdemo/wxToggleButton.rbw +0 -49
  325. data/samples/bigdemo/wxToolBar.rbw +0 -131
  326. data/samples/bigdemo/wxTreeCtrl.rbw +0 -191
  327. data/samples/caret/caret.rb +0 -298
  328. data/samples/caret/mondrian.xpm +0 -44
  329. data/samples/controls/books.rb +0 -189
  330. data/samples/controls/choice.xpm +0 -27
  331. data/samples/controls/combo.xpm +0 -27
  332. data/samples/controls/controls.rb +0 -1099
  333. data/samples/controls/gauge.xpm +0 -27
  334. data/samples/controls/list.xpm +0 -27
  335. data/samples/controls/mondrian.ico +0 -0
  336. data/samples/controls/mondrian.xpm +0 -44
  337. data/samples/controls/radio.xpm +0 -27
  338. data/samples/controls/stattext.xpm +0 -24
  339. data/samples/controls/test2.bmp +0 -0
  340. data/samples/controls/text.xpm +0 -27
  341. data/samples/controls/tn_books.png +0 -0
  342. data/samples/controls/tn_controls.png +0 -0
  343. data/samples/etc/choice.rb +0 -87
  344. data/samples/etc/tn_choice.png +0 -0
  345. data/samples/text/mondrian.ico +0 -0
  346. data/samples/text/mondrian.xpm +0 -44
  347. /data/samples/{caret → etc}/tn_caret.png +0 -0
@@ -0,0 +1,1233 @@
1
+ # Copyright (c) 2023 M.J.N. Corino, The Netherlands
2
+ #
3
+ # This software is released under the MIT license.
4
+ #
5
+ # Adapted for wxRuby from wxWidgets widgets sample
6
+ # Copyright (c) 2001 Vadim Zeitlin
7
+
8
+ require 'wx'
9
+
10
+ module Widgets
11
+
12
+ if Wx.has_feature?(:USE_TREEBOOK)
13
+ BookCtrl = Wx::Treebook
14
+ elsif Wx.has_feature?(:USE_NOTEBOOK)
15
+ BookCtrl = Wx::Notebook
16
+ else
17
+ BookCtrl = Wx::Choicebook
18
+ end
19
+
20
+ ICON_SIZE = 16
21
+
22
+ NATIVE_PAGE = 0
23
+ GENERIC_PAGE = 1
24
+ PICKER_PAGE = 2
25
+ COMBO_PAGE = 3
26
+ WITH_ITEMS_PAGE = 4
27
+ EDITABLE_PAGE = 5
28
+ BOOK_PAGE = 6
29
+ ALL_PAGE = 7
30
+ MAX_PAGES = 8
31
+
32
+ NATIVE_CTRLS = 1 << NATIVE_PAGE
33
+ GENERIC_CTRLS = 1 << GENERIC_PAGE
34
+ PICKER_CTRLS = 1 << PICKER_PAGE
35
+ COMBO_CTRLS = 1 << COMBO_PAGE
36
+ WITH_ITEMS_CTRLS = 1 << WITH_ITEMS_PAGE
37
+ EDITABLE_CTRLS = 1 << EDITABLE_PAGE
38
+ BOOK_CTRLS = 1 << BOOK_PAGE
39
+ ALL_CTRLS = 1 << ALL_PAGE
40
+
41
+ class Attributes
42
+
43
+ attr_accessor :tool_tip if Wx.has_feature?(:USE_TOOLTIPS)
44
+ attr_accessor :font if Wx.has_feature?(:USE_FONTDLG)
45
+ attr_accessor :col_fg, :col_bg, :col_page_bg
46
+ attr_accessor :enabled, :show, :dir, :variant, :cursor, :default_flags
47
+
48
+ def initialize
49
+ @tool_tip = 'This is a tooltip'
50
+ @enabled = true
51
+ @show = true
52
+ @dir = Wx::LayoutDirection::Layout_Default
53
+ @variant = Wx::WindowVariant::WINDOW_VARIANT_NORMAL
54
+ @cursor = Wx::NULL_CURSOR
55
+ @default_flags = Wx::Border::BORDER_DEFAULT
56
+ end
57
+
58
+ end
59
+
60
+ class Page < Wx::Panel
61
+
62
+ ATTRS = Attributes.new
63
+
64
+ def initialize(book, images, icon)
65
+ super(book, Wx::ID_ANY, style: Wx::CLIP_CHILDREN | Wx::TAB_TRAVERSAL)
66
+ images << Wx.Image(icon).scale(ICON_SIZE, ICON_SIZE).to_bitmap
67
+ end
68
+
69
+ # return the control shown by this page
70
+ def get_widget
71
+ nil
72
+ end
73
+
74
+ # return the control shown by this page, if it supports text entry interface
75
+ def get_text_entry
76
+ nil
77
+ end
78
+
79
+ # lazy creation of the content
80
+ def create_content
81
+ ::Kernel.raise NotImplementedError
82
+ end
83
+
84
+ # some pages show additional controls, in this case override this one to
85
+ # return all of them (including the one returned by GetWidget())
86
+ def get_widgets
87
+ [get_widget]
88
+ end
89
+
90
+ # recreate the control shown by this page
91
+ #
92
+ # this is currently used only to take into account the border flags
93
+ def recreate_widget
94
+ ::Kernel.raise NotImplementedError
95
+ end
96
+
97
+ # apply current attributes to the widget(s)
98
+ def set_up_widget
99
+ widgets = get_widgets
100
+
101
+ widgets.each do |widget|
102
+ ::Kernel.raise 'nil widget' if widget.nil?
103
+
104
+ if Wx.has_feature?(:USE_TOOLTIPS)
105
+ widget.set_tool_tip(get_attrs.tool_tip)
106
+ end # wxUSE_TOOLTIPS
107
+ if Wx.has_feature?(:USE_FONTDLG)
108
+ widget.set_font(get_attrs.font) if get_attrs.font&.ok?
109
+ end # wxUSE_FONTDLG
110
+
111
+ widget.set_foreground_colour(get_attrs.col_fg) if get_attrs.col_fg&.ok?
112
+ widget.set_background_colour(get_attrs.col_bg) if get_attrs.col_bg&.ok?
113
+
114
+ widget.set_layout_direction(get_attrs.dir) if get_attrs.dir != Wx::LayoutDirection::Layout_Default
115
+
116
+ widget.enable(get_attrs.enabled)
117
+ widget.show(get_attrs.show)
118
+
119
+ widget.set_cursor(get_attrs.cursor)
120
+
121
+ widget.set_window_variant(get_attrs.variant)
122
+
123
+ widget.refresh
124
+ end
125
+
126
+ if get_attrs.col_page_bg&.ok?
127
+ set_background_colour(get_attrs.col_page_bg)
128
+ refresh
129
+ end
130
+ end
131
+
132
+ # the default attributes for the widget
133
+ def get_attrs
134
+ Page::ATTRS
135
+ end
136
+
137
+ # return true if we're showing logs in the log window (always the case
138
+ # except during startup and shutdown)
139
+ def self.is_using_log_window
140
+ Wx.get_app.is_using_log_window
141
+ end
142
+
143
+ protected
144
+
145
+ # several helper functions for page creation
146
+
147
+ # create a horz sizer containing the given control and the text ctrl
148
+ # (pointer to which will be returned next to sizer)
149
+ # with the specified id
150
+ def create_sizer_with_text(control, id = Wx::ID_ANY)
151
+ sizerRow = Wx::HBoxSizer.new
152
+ text = Wx::TextCtrl.new(control.parent, id, style: Wx::TE_PROCESS_ENTER)
153
+
154
+ sizerRow.add(control, Wx::SizerFlags.new(0).border(Wx::RIGHT).centre_vertical)
155
+ sizerRow.add(text, Wx::SizerFlags.new(1).border(Wx::LEFT).centre_vertical)
156
+
157
+ [sizerRow, text]
158
+ end
159
+
160
+ # create a sizer containing a label and a text ctrl
161
+ def create_sizer_with_text_and_label(label, id = Wx::ID_ANY, statBoxParent = nil)
162
+ create_sizer_with_text(Wx::StaticText.new(statBoxParent ? statBoxParent: self, Wx::ID_ANY, label), id)
163
+ end
164
+
165
+ # create a sizer containing a button and a text ctrl
166
+ def create_sizer_with_text_and_button(idBtn, labelBtn, id = Wx::ID_ANY, statBoxParent = nil)
167
+ create_sizer_with_text(Wx::Button.new(statBoxParent ? statBoxParent: self, idBtn, labelBtn), id)
168
+ end
169
+
170
+ # create a checkbox and add it to the sizer
171
+ def create_check_box_and_add_to_sizer(sizer, label, id = Wx::ID_ANY, statBoxParent = nil)
172
+ checkbox = Wx::CheckBox.new(statBoxParent ? statBoxParent: self, id, label)
173
+ sizer.add(checkbox, Wx::SizerFlags.new.horz_border)
174
+ sizer.add_spacer(2)
175
+
176
+ checkbox
177
+ end
178
+
179
+ class << self
180
+ # the list containing info about all pages
181
+ def widget_pages
182
+ @widget_pages ||= []
183
+ end
184
+ end
185
+ end
186
+
187
+ class PageInfo
188
+
189
+ # our ctor
190
+ def initialize(klass, label, categories = 0)
191
+ @klass = klass
192
+ @label = label
193
+ @categories = Widgets::ALL_CTRLS | categories
194
+ # dummy sorting: add and immediately sort in the list according to label
195
+ if Page.widget_pages.empty? || (pg_gt = Page.widget_pages.bsearch_index { |p| p.label > @label }).nil?
196
+ # add when first (or 'largest' label)
197
+ Page.widget_pages << self
198
+ else
199
+ Page.widget_pages.insert(pg_gt, self)
200
+ end
201
+ end
202
+
203
+ def create(book, images)
204
+ @klass.new(book, images)
205
+ end
206
+
207
+ # the label of the page
208
+ attr_reader :label
209
+
210
+ # the list (flags) for sharing page between categories
211
+ attr_reader :categories
212
+
213
+ # the class of this page
214
+ attr_reader :klass
215
+ end
216
+
217
+ if Wx.has_feature?(:USE_LOG)
218
+ # A log target which just redirects the messages to a listbox
219
+ class LboxLogger < Wx::Log
220
+
221
+ def initialize(lbox, logOld)
222
+ super()
223
+ @lbox = lbox
224
+ #@lbox->Disable() -- looks ugly under MSW
225
+ @logOld = logOld
226
+ end
227
+
228
+ def reset
229
+ Wx::Log.set_active_target(@logOld)
230
+ end
231
+
232
+ protected
233
+
234
+ # implement sink functions
235
+
236
+ def do_log_record(level, msg, info)
237
+ if level == Wx::LOG_Trace
238
+ @logOld.log_record(level, msg, info) if @logOld
239
+ else
240
+ super
241
+ end
242
+ end
243
+
244
+ def do_log_text_at_level(level, msg)
245
+ # if Wx.has_feature?(:WXUNIVERSAL)
246
+ # @lbox.append_and_ensure_visible(msg)
247
+ # else # other ports don't have this method yet
248
+ @lbox.append(msg)
249
+ @lbox.set_first_item(@lbox.count - 1)
250
+ # end
251
+ end
252
+
253
+ end
254
+ end # USE_LOG
255
+
256
+ Categories = [
257
+ # if Wx.has_feature?(:WXUNIVERSAL)
258
+ # 'Universal'
259
+ # else
260
+ 'Native',
261
+ # end,
262
+ 'Generic',
263
+ 'Pickers',
264
+ 'Comboboxes',
265
+ 'With items',
266
+ 'Editable',
267
+ 'Books',
268
+ 'All controls'
269
+ ]
270
+
271
+ class Frame < Wx::Frame
272
+
273
+ module ID
274
+ include Wx::IDHelper
275
+
276
+ Widgets_ClearLog = self.next_id
277
+ Widgets_Quit = self.next_id
278
+
279
+ Widgets_BookCtrl = self.next_id
280
+
281
+ if Wx.has_feature?(:USE_TOOLTIPS)
282
+ Widgets_SetTooltip = self.next_id
283
+ end # wxUSE_TOOLTIPS
284
+ Widgets_SetFgColour = self.next_id
285
+ Widgets_SetBgColour = self.next_id
286
+ Widgets_SetPageBg = self.next_id
287
+ Widgets_SetFont = self.next_id
288
+ Widgets_Enable = self.next_id
289
+ Widgets_Show = self.next_id
290
+
291
+ Widgets_BorderNone = self.next_id
292
+ Widgets_BorderStatic = self.next_id
293
+ Widgets_BorderSimple = self.next_id
294
+ Widgets_BorderRaised = self.next_id
295
+ Widgets_BorderSunken = self.next_id
296
+ Widgets_BorderDouble = self.next_id
297
+ Widgets_BorderDefault = self.next_id
298
+
299
+ Widgets_VariantNormal = self.next_id
300
+ Widgets_VariantSmall = self.next_id
301
+ Widgets_VariantMini = self.next_id
302
+ Widgets_VariantLarge = self.next_id
303
+
304
+ Widgets_LayoutDirection = self.next_id
305
+
306
+ Widgets_GlobalBusyCursor = self.next_id
307
+ Widgets_BusyCursor = self.next_id
308
+
309
+ Widgets_GoToPage = self.next_id
310
+ Widgets_GoToPageLast = self.next_id(Widgets_GoToPage + 100)
311
+
312
+
313
+ TextEntry_Begin = self.next_id
314
+ TextEntry_DisableAutoComplete = TextEntry_Begin
315
+ TextEntry_AutoCompleteFixed = self.next_id
316
+ TextEntry_AutoCompleteFilenames = self.next_id
317
+ TextEntry_AutoCompleteDirectories = self.next_id
318
+ TextEntry_AutoCompleteCustom = self.next_id
319
+ TextEntry_AutoCompleteKeyLength = self.next_id
320
+
321
+ TextEntry_SetHint = self.next_id
322
+ TextEntry_End = self.next_id
323
+
324
+ Last = self.next_id
325
+ end
326
+
327
+ def initialize(title)
328
+ super(nil, Wx::ID_ANY, title)
329
+
330
+ set_icon(Wx.Icon(:sample, art_path: File.dirname(__dir__)))
331
+
332
+ # init everything
333
+ if Wx.has_feature?(:USE_LOG)
334
+ @lboxLog = nil
335
+ @logTarget = nil
336
+ end # USE_LOG
337
+ @book = nil
338
+
339
+ if Wx.has_feature?(:USE_MENUS)
340
+ # create the menubar
341
+ mbar = Wx::MenuBar.new
342
+ menuWidget = Wx::Menu.new
343
+ if Wx.has_feature?(:USE_TOOLTIPS)
344
+ menuWidget.append(ID::Widgets_SetTooltip, "Set &tooltip...\tCtrl-T")
345
+ menuWidget.append_separator
346
+ end # wxUSE_TOOLTIPS
347
+ menuWidget.append(ID::Widgets_SetFgColour, "Set &foreground...\tCtrl-F")
348
+ menuWidget.append(ID::Widgets_SetBgColour, "Set &background...\tCtrl-B")
349
+ menuWidget.append(ID::Widgets_SetPageBg, "Set &page background...\tShift-Ctrl-B")
350
+ menuWidget.append(ID::Widgets_SetFont, "Set f&ont...\tCtrl-O")
351
+ menuWidget.append_check_item(ID::Widgets_Enable, "&Enable/disable\tCtrl-E")
352
+ menuWidget.append_check_item(ID::Widgets_Show, "Show/Hide")
353
+
354
+ menuBorders = Wx::Menu.new
355
+ menuBorders.append_radio_item(ID::Widgets_BorderDefault, "De&fault\tCtrl-Shift-9")
356
+ menuBorders.append_radio_item(ID::Widgets_BorderNone, "&None\tCtrl-Shift-0")
357
+ menuBorders.append_radio_item(ID::Widgets_BorderSimple, "&Simple\tCtrl-Shift-1")
358
+ menuBorders.append_radio_item(ID::Widgets_BorderDouble, "&Double\tCtrl-Shift-2")
359
+ menuBorders.append_radio_item(ID::Widgets_BorderStatic, "Stati&c\tCtrl-Shift-3")
360
+ menuBorders.append_radio_item(ID::Widgets_BorderRaised, "&Raised\tCtrl-Shift-4")
361
+ menuBorders.append_radio_item(ID::Widgets_BorderSunken, "S&unken\tCtrl-Shift-5")
362
+ menuWidget.append_sub_menu(menuBorders, "Set &border")
363
+
364
+ menuVariants = Wx::Menu.new
365
+ menuVariants.append_radio_item(ID::Widgets_VariantMini, "&Mini\tCtrl-Shift-6")
366
+ menuVariants.append_radio_item(ID::Widgets_VariantSmall, "&Small\tCtrl-Shift-7")
367
+ menuVariants.append_radio_item(ID::Widgets_VariantNormal, "&Normal\tCtrl-Shift-8")
368
+ menuVariants.append_radio_item(ID::Widgets_VariantLarge, "&Large\tCtrl-Shift-9")
369
+ menuWidget.append_sub_menu(menuVariants, "Set &variant")
370
+
371
+ menuWidget.append_separator
372
+ menuWidget.append_check_item(ID::Widgets_LayoutDirection,
373
+ "Toggle &layout direction\tCtrl-L")
374
+ menuWidget.check(ID::Widgets_LayoutDirection,
375
+ get_layout_direction == Wx::LayoutDirection::Layout_RightToLeft)
376
+
377
+ menuWidget.append_separator
378
+ menuWidget.append_check_item(ID::Widgets_GlobalBusyCursor,
379
+ "Toggle &global busy cursor\tCtrl-Shift-U")
380
+ menuWidget.append_check_item(ID::Widgets_BusyCursor,
381
+ "Toggle b&usy cursor\tCtrl-U")
382
+
383
+ menuWidget.append_separator
384
+ menuWidget.append(Wx::ID_EXIT, "&Quit\tCtrl-Q")
385
+ mbar.append(menuWidget, "&Widget")
386
+
387
+ menuTextEntry = Wx::Menu.new
388
+ menuTextEntry.append_radio_item(ID::TextEntry_DisableAutoComplete,
389
+ "&Disable auto-completion")
390
+ menuTextEntry.append_radio_item(ID::TextEntry_AutoCompleteFixed,
391
+ "Fixed-&list auto-completion")
392
+ menuTextEntry.append_radio_item(ID::TextEntry_AutoCompleteFilenames,
393
+ "&Files names auto-completion")
394
+ menuTextEntry.append_radio_item(ID::TextEntry_AutoCompleteDirectories,
395
+ "&Directories names auto-completion")
396
+ menuTextEntry.append_radio_item(ID::TextEntry_AutoCompleteCustom,
397
+ "&Custom auto-completion")
398
+ menuTextEntry.append_radio_item(ID::TextEntry_AutoCompleteKeyLength,
399
+ "Custom with &min length")
400
+ menuTextEntry.append_separator
401
+ menuTextEntry.append(ID::TextEntry_SetHint, "Set help &hint")
402
+
403
+ mbar.append(menuTextEntry, "&Text")
404
+
405
+ set_menu_bar(mbar)
406
+
407
+ mbar.check(ID::Widgets_Enable, true)
408
+ mbar.check(ID::Widgets_Show, true)
409
+
410
+ mbar.check(ID::Widgets_VariantNormal, true)
411
+ end # wxUSE_MENUS
412
+
413
+ # create controls
414
+ @panel = Wx::Panel.new(self)
415
+
416
+ sizerTop = Wx::VBoxSizer.new
417
+
418
+ # we have 2 panes: book with pages demonstrating the controls in the
419
+ # upper one and the log window with some buttons in the lower
420
+
421
+ style = Wx::BK_DEFAULT
422
+ # Uncomment to suppress page theme (draw in solid colour)
423
+ #style |= Wx::NB_NOPAGETHEME
424
+
425
+ @book = Widgets::BookCtrl.new(@panel, ID::Widgets_BookCtrl,
426
+ style: style, name: 'Widgets')
427
+
428
+ init_book
429
+
430
+ # the lower one only has the log listbox and a button to clear it
431
+ if Wx.has_feature?(:USE_LOG)
432
+ sizerDown = Wx::StaticBoxSizer.new(Wx::VERTICAL, @panel, '&Log window')
433
+ sizerDownBox = sizerDown.get_static_box
434
+
435
+ @lboxLog = Wx::ListBox.new(sizerDownBox)
436
+ sizerDown.add(@lboxLog, Wx::SizerFlags.new(1).expand.border)
437
+ sizerDown.set_min_size(100, 150)
438
+ else
439
+ sizerDown = Wx::VBoxSizer.new
440
+ end # USE_LOG
441
+
442
+ sizerBtns = Wx::HBoxSizer.new
443
+ if Wx.has_feature?(:USE_LOG)
444
+ btn = Wx::Button.new(sizerDownBox, ID::Widgets_ClearLog, 'Clear &log')
445
+ sizerBtns.add(btn)
446
+ sizerBtns.add_spacer(10)
447
+ end # USE_LOG
448
+ btn = Wx::Button.new(sizerDownBox, ID::Widgets_Quit, 'E&xit')
449
+ sizerBtns.add(btn)
450
+ sizerDown.add(sizerBtns, Wx::SizerFlags.new.border.right)
451
+
452
+ # put everything together
453
+ sizerTop.add(@book, Wx::SizerFlags.new(1).expand.double_border(Wx::ALL & ~(Wx::TOP | Wx::BOTTOM)))
454
+ sizerTop.add_spacer(5)
455
+ sizerTop.add(sizerDown, Wx::SizerFlags.new(0).expand.double_border(Wx::ALL & ~Wx::TOP))
456
+
457
+ @panel.set_sizer(sizerTop)
458
+
459
+ # TODO - review wxPersistenceManager
460
+ # sizeSet = wxPersistentRegisterAndRestore(this, "Main")
461
+
462
+ sizeMin = @panel.get_best_size
463
+ # if ( !sizeSet )
464
+ set_client_size(sizeMin)
465
+ set_min_client_size(sizeMin)
466
+
467
+ # connect the event handlers
468
+ if Wx.has_feature?(:USE_LOG)
469
+ evt_button(ID::Widgets_ClearLog, :on_button_clear_log)
470
+ end # USE_LOG
471
+ evt_button(ID::Widgets_Quit, :on_exit)
472
+
473
+ if Wx.has_feature?(:USE_TOOLTIPS)
474
+ evt_menu(ID::Widgets_SetTooltip, :on_set_tooltip)
475
+ end # wxUSE_TOOLTIPS
476
+
477
+ if Wx.has_feature?(:USE_TREEBOOK)
478
+ evt_treebook_page_changing(Wx::ID_ANY, :on_page_changing)
479
+ elsif Wx.has_feature?(:USE_NOTEBOOK)
480
+ evt_notebook_page_changing(Wx::ID_ANY, :on_page_changing)
481
+ else
482
+ evt_choicebook_page_changing(Wx::ID_ANY, :on_page_changing)
483
+ end
484
+ if Wx.has_feature?(:USE_MENUS)
485
+ evt_menu_range(ID::Widgets_GoToPage, ID::Widgets_GoToPageLast,
486
+ :on_go_to_page)
487
+
488
+ evt_menu(ID::Widgets_SetFgColour, :on_set_fg_col)
489
+ evt_menu(ID::Widgets_SetBgColour, :on_set_bg_col)
490
+ evt_menu(ID::Widgets_SetPageBg, :on_set_page_bg)
491
+ evt_menu(ID::Widgets_SetFont, :on_set_font)
492
+ evt_menu(ID::Widgets_Enable, :on_enable)
493
+ evt_menu(ID::Widgets_Show, :on_show)
494
+
495
+ evt_menu_range(ID::Widgets_BorderNone, ID::Widgets_BorderDefault,
496
+ :on_set_border)
497
+
498
+ evt_menu_range(ID::Widgets_VariantNormal, ID::Widgets_VariantLarge,
499
+ :on_set_variant)
500
+
501
+ evt_menu(ID::Widgets_LayoutDirection, :on_toggle_layout_direction)
502
+
503
+ evt_menu(ID::Widgets_GlobalBusyCursor, :on_toggle_global_busy_cursor)
504
+ evt_menu(ID::Widgets_BusyCursor, :on_toggle_busy_cursor)
505
+
506
+ evt_menu(ID::TextEntry_DisableAutoComplete, :on_disable_auto_complete)
507
+ evt_menu(ID::TextEntry_AutoCompleteFixed, :on_auto_complete_fixed)
508
+ evt_menu(ID::TextEntry_AutoCompleteFilenames, :on_auto_complete_filenames)
509
+ evt_menu(ID::TextEntry_AutoCompleteDirectories, :on_auto_complete_directories)
510
+ evt_menu(ID::TextEntry_AutoCompleteCustom, :on_auto_complete_custom)
511
+ evt_menu(ID::TextEntry_AutoCompleteKeyLength, :on_auto_complete_key_length)
512
+
513
+ evt_menu(ID::TextEntry_SetHint, :on_set_hint)
514
+
515
+ evt_update_ui_range(ID::TextEntry_Begin, ID::TextEntry_End - 1,
516
+ :on_update_text_ui)
517
+
518
+ evt_menu(Wx::ID_EXIT, :on_exit)
519
+ end # wxUSE_MENUS
520
+
521
+ if Wx.has_feature?(:USE_LOG)
522
+ # now that everything is created we can redirect the log messages to the
523
+ # listbox
524
+ @logTarget = LboxLogger.new(@lboxLog, Wx::Log.get_active_target)
525
+ Wx::Log.set_active_target(@logTarget)
526
+ end
527
+ end
528
+
529
+ protected
530
+
531
+ if Wx.has_feature?(:USE_LOG)
532
+ def on_button_clear_log(_event)
533
+ @lboxLog.clear
534
+ end
535
+ end # USE_LOG
536
+
537
+ def on_exit(_event)
538
+ @logTarget.reset if @logTarget
539
+ @logTarget = nil
540
+ close
541
+ end
542
+
543
+ if Wx.has_feature?(:USE_MENUS)
544
+
545
+ def on_page_changing(event)
546
+ if Wx.has_feature?(:USE_TREEBOOK)
547
+ # don't allow selection of entries without pages (categories)
548
+ event.veto unless @book.get_page(event.selection)
549
+ end
550
+ end
551
+
552
+ def on_page_changed(event)
553
+ sel = event.selection
554
+
555
+ # adjust "Page" menu selection
556
+ item, _ = get_menu_bar.find_item(ID::Widgets_GoToPage + sel)
557
+ item.check if item
558
+
559
+ get_menu_bar.check(ID::Widgets_BusyCursor, false)
560
+
561
+ # create the pages on demand, otherwise the sample startup is too slow as
562
+ # it creates hundreds of controls
563
+ curPage = current_page
564
+ if curPage.get_children.empty?
565
+ Wx::WindowUpdateLocker.update(curPage) do
566
+ curPage.create_content
567
+ curPage.layout
568
+
569
+ connect_to_widget_events
570
+ end
571
+ end
572
+
573
+ # re-apply the attributes to the widget(s)
574
+ curPage.set_up_widget
575
+
576
+ event.skip
577
+ end
578
+
579
+ def on_go_to_page(event)
580
+ if Wx.has_feature?(:USE_TREEBOOK)
581
+ @book.set_selection(event.id - ID::Widgets_GoToPage)
582
+ else
583
+ @book.set_selection(@book.get_page_count-1)
584
+ book = @book.get_current_page
585
+ book.set_selection(event.id - ID::Widgets_GoToPage)
586
+ end
587
+ end
588
+
589
+ if Wx.has_feature?(:USE_TOOLTIPS)
590
+ def on_set_tooltip(_event)
591
+ Wx.TextEntryDialog(self,
592
+ 'Tooltip text (may use \\n, leave empty to remove): ',
593
+ 'Widgets sample',
594
+ Page::ATTRS.tool_tip) do |dialog|
595
+ return if dialog.show_modal != Wx::ID_OK
596
+
597
+ Page::ATTRS.tool_tip = dialog.value
598
+ Page::ATTRS.tool_tip.gsub!("\\n", "\n")
599
+ end
600
+
601
+ current_page.set_up_widget
602
+ end
603
+ end # wxUSE_TOOLTIPS
604
+
605
+ # Trivial wrapper for wxGetColourFromUser() which also does something even if
606
+ # the colour dialog is not available in the current build (which may happen
607
+ # for the ports in development and it is still useful to see how colours work)
608
+ def get_colour_from_user(colDefault)
609
+ if Wx.has_feature?(:USE_COLOURDLG)
610
+ Wx.get_colour_from_user(self, colDefault)
611
+ else # !wxUSE_COLOURDLG
612
+ if colDefault == Wx::BLACK
613
+ Wx::WHITE
614
+ else
615
+ Wx::BLACK
616
+ end
617
+ end # wxUSE_COLOURDLG/!wxUSE_COLOURDLG
618
+ end
619
+
620
+ def on_set_fg_col(_event)
621
+ # allow for debugging the default colour the first time this is called
622
+ page = current_page
623
+
624
+ unless Page::ATTRS.col_fg&.ok?
625
+ Page::ATTRS.col_fg = page.get_foreground_colour
626
+ end
627
+
628
+ col = get_colour_from_user(Page::ATTRS.col_fg)
629
+ return unless col&.ok?
630
+
631
+ Page::ATTRS.col_fg = col
632
+
633
+ page.set_up_widget
634
+ end
635
+
636
+ def on_set_bg_col(_event)
637
+ # allow for debugging the default colour the first time this is called
638
+ page = current_page
639
+
640
+ unless Page::ATTRS.col_bg&.ok?
641
+ Page::ATTRS.col_bg = page.get_background_colour
642
+ end
643
+
644
+ col = get_colour_from_user(Page::ATTRS.col_bg)
645
+ return unless col&.ok?
646
+
647
+ Page::ATTRS.col_bg = col
648
+
649
+ page.set_up_widget
650
+ end
651
+
652
+ def on_set_page_bg(_event)
653
+ col = get_colour_from_user(get_background_colour)
654
+ return unless col&.ok?
655
+
656
+ Page::ATTRS.col_page_bg = col
657
+
658
+ current_page.set_up_widget
659
+ end
660
+
661
+ def on_set_font(_event)
662
+ if Wx.has_feature?(:USE_FONTDLG)
663
+ page = current_page
664
+
665
+ unless Page::ATTRS.font&.ok?
666
+ Page::ATTRS.font = page.get_font
667
+ end
668
+
669
+ font = Wx.get_font_from_user(self, Page::ATTRS.font)
670
+ return unless font&.ok?
671
+
672
+ Page::ATTRS.font = font
673
+
674
+ page.set_up_widget
675
+ # The best size of the widget could have changed after changing its font,
676
+ # so re-layout to show it correctly.
677
+ page.layout
678
+ else
679
+ Wx.log_message('Font selection dialog not available in current build.')
680
+ end
681
+ end
682
+
683
+ def on_enable(event)
684
+ Page::ATTRS.enabled = event.checked?
685
+
686
+ current_page.set_up_widget
687
+ end
688
+
689
+ def on_show(event)
690
+ Page::ATTRS.show = event.checked?
691
+
692
+ current_page.set_up_widget
693
+ end
694
+
695
+ def on_set_border(event)
696
+ case event.id
697
+ when ID::Widgets_BorderNone
698
+ border = Wx::BORDER_NONE
699
+ when ID::Widgets_BorderStatic
700
+ border = Wx::BORDER_STATIC
701
+ when ID::Widgets_BorderSimple
702
+ border = Wx::BORDER_SIMPLE
703
+ when ID::Widgets_BorderRaised
704
+ border = Wx::BORDER_RAISED
705
+ when ID::Widgets_BorderSunken
706
+ border = Wx::BORDER_SUNKEN
707
+ when ID::Widgets_BorderDouble
708
+ border = Wx::BORDER_DOUBLE
709
+ when ID::Widgets_BorderDefault
710
+ border = Wx::BORDER_DEFAULT
711
+ else
712
+ ::Kernel.raise RuntimeError, 'unknown border style'
713
+ end
714
+
715
+ Page::ATTRS.default_flags &= ~Wx::BORDER_MASK
716
+ Page::ATTRS.default_flags |= border
717
+
718
+ page = current_page
719
+
720
+ page.recreate_widget
721
+
722
+ connect_to_widget_events
723
+
724
+ # re-apply the attributes to the widget(s)
725
+ page.set_up_widget
726
+ end
727
+
728
+ def on_set_variant(event)
729
+ case event.id
730
+ when ID::Widgets_VariantSmall
731
+ v = Wx::WINDOW_VARIANT_SMALL
732
+ when ID::Widgets_VariantMini
733
+ v = Wx::WINDOW_VARIANT_MINI
734
+ when ID::Widgets_VariantLarge
735
+ v = Wx::WINDOW_VARIANT_LARGE
736
+ when ID::Widgets_VariantNormal
737
+ v = Wx::WINDOW_VARIANT_NORMAL
738
+ else
739
+ ::Kernel.raise RuntimeError, 'unknown window variant'
740
+ end
741
+
742
+ Page::ATTRS.variant = v
743
+
744
+ current_page.set_up_widget
745
+ current_page.layout
746
+ end
747
+
748
+ def on_toggle_layout_direction(_event)
749
+ dir = Page::ATTRS.dir
750
+ dir = get_layout_direction if dir == Wx::LayoutDirection::Layout_Default
751
+ Page::ATTRS.dir =
752
+ (dir == Wx::LayoutDirection::Layout_LeftToRight) ?
753
+ Wx::LayoutDirection::Layout_RightToLeft :
754
+ Wx::LayoutDirection::Layout_LeftToRight
755
+
756
+ current_page.set_up_widget
757
+ end
758
+
759
+ def on_toggle_global_busy_cursor(event)
760
+ if event.checked?
761
+ Wx.begin_busy_cursor
762
+ else
763
+ Wx.end_busy_cursor
764
+ end
765
+ end
766
+
767
+ def on_toggle_busy_cursor(event)
768
+ Page::ATTRS.cursor = event.checked? ? Wx::HOURGLASS_CURSOR : Wx::NULL_CURSOR
769
+
770
+ current_page.set_up_widget
771
+ end
772
+
773
+ # wxTextEntry-specific tests
774
+ def on_disable_auto_complete(_event)
775
+ unless (entry = current_page.get_text_entry)
776
+ Wx.log_error('menu item should be disabled')
777
+ return
778
+ end
779
+
780
+ if entry.auto_complete(nil)
781
+ Wx.log_message('Disabled auto completion.')
782
+ else
783
+ Wx.log_message('auto_complete() failed.')
784
+ end
785
+ end
786
+
787
+ def on_auto_complete_fixed(_event)
788
+ unless (entry = current_page.get_text_entry)
789
+ Wx.log_error('menu item should be disabled')
790
+ return
791
+ end
792
+
793
+ completion_choices = (?a..?z).collect { |c| c*2 }
794
+ completion_choices <<
795
+ 'is this string for test?' <<
796
+ 'this is a test string' <<
797
+ 'this is another test string' <<
798
+ 'this string is for test'
799
+
800
+ if entry.auto_complete(completion_choices)
801
+ Wx.log_message('Enabled auto completion of a set of fixed strings.')
802
+ else
803
+ Wx.log_message('auto_complete() failed.')
804
+ end
805
+ end
806
+
807
+ def on_auto_complete_filenames(_event)
808
+ unless (entry = current_page.get_text_entry)
809
+ Wx.log_error('menu item should be disabled')
810
+ return
811
+ end
812
+
813
+ if entry.auto_complete_file_names
814
+ Wx.log_message('Enabled auto completion of file names.')
815
+ else
816
+ Wx.log_message('auto_complete_file_names failed.')
817
+ end
818
+ end
819
+
820
+ def on_auto_complete_directories(_event)
821
+ unless (entry = current_page.get_text_entry)
822
+ Wx.log_error('menu item should be disabled')
823
+ return
824
+ end
825
+
826
+ if entry.auto_complete_directories
827
+ Wx.log_message('Enabled auto completion of directories.')
828
+ else
829
+ Wx.log_message('AutoCompleteDirectories() failed.')
830
+ end
831
+ end
832
+
833
+ def on_auto_complete_custom(_event)
834
+ do_use_custom_auto_complete
835
+ end
836
+
837
+ def on_auto_complete_key_length(_event)
838
+ message = "The auto-completion is triggered if and only if\n" +
839
+ "the length of the search key (prefix) is at least [LENGTH].\n" +
840
+ "Hint: 0 disables the length check completely."
841
+ prompt = 'Enter the minimum key length:'
842
+ caption = 'Minimum key length'
843
+
844
+ res = Wx.get_number_from_user(message, prompt, caption, 1, 0, 100, self)
845
+ return if res == -1
846
+
847
+ Wx.log_message("The minimum key length for autocomplete is #{res}.")
848
+
849
+ do_use_custom_auto_complete(res)
850
+ end
851
+
852
+ # This is a simple (and hence rather useless) example of a custom
853
+ # completer class that completes the first word (only) initially and only
854
+ # build the list of the possible second words once the first word is
855
+ # known. This allows to avoid building the full 676000 item list of
856
+ # possible strings all at once as the we have 1000 possibilities for the
857
+ # first word (000..999) and 676 (aa..zz) for the second one.
858
+ class CustomTextCompleter < Wx::TextCompleterSimple
859
+
860
+ def initialize(min_length)
861
+ super()
862
+ @min_length = min_length
863
+ end
864
+
865
+ def get_completions(prefix)
866
+ res = []
867
+ 1.times do
868
+ # Wait for enough text to be entered before proposing completions:
869
+ # this is done to avoid proposing too many of them when the
870
+ # control is empty, for example.
871
+ break if prefix.size < @min_length
872
+
873
+ # The only valid strings start with 3 digits so check for their
874
+ # presence proposing to complete the remaining ones.
875
+ break unless prefix.start_with?(/\d/)
876
+
877
+ if prefix.size == 1
878
+ 10.times { |i| 10.times { |j| res << "#{prefix}#{i}#{j}"} }
879
+ break
880
+ else
881
+ break unless (?0..?9).include?(prefix[1])
882
+ end
883
+
884
+ if prefix.size == 2
885
+ 10.times { |i| res << "#{prefix}#{i}" }
886
+ break
887
+ else
888
+ break unless (?0..?9).include?(prefix[2])
889
+ end
890
+
891
+ # Next we must have a space and two letters.
892
+ prefix2 = prefix.dup
893
+ if prefix.size == 3
894
+ prefix2 += ' '
895
+ elsif prefix[3] != ' '
896
+ break
897
+ end
898
+
899
+ if prefix2.size == 4
900
+ (?a..?z).each { |c| (?a..?z).each { |d| res << "#{prefix2}#{c}#{d}"} }
901
+ break
902
+ else
903
+ break unless (?a..?z).include?(prefix[4])
904
+ end
905
+
906
+ if prefix.size == 5
907
+ (?a..?z).each { |c| res << "#{prefix}#{c}" }
908
+ end
909
+ ensure
910
+ # This is used for illustrative purposes only and shows how many
911
+ # completions we return every time when we're called.
912
+ Wx.log_message("Returning #{res.size} possible completions for prefix \"#{prefix}\"")
913
+ end
914
+ res
915
+ end
916
+
917
+ end
918
+
919
+ def do_use_custom_auto_complete(min_length = 1)
920
+ unless (entry = current_page.get_text_entry)
921
+ Wx.log_error('menu item should be disabled')
922
+ return
923
+ end
924
+
925
+ if entry.auto_complete(CustomTextCompleter.new(min_length))
926
+ Wx.log_message('Enabled custom auto completer for "NNN XX" items ' +
927
+ '(where N is a digit and X is a letter).')
928
+ else
929
+ Wx.log_message('auto_complete() failed.')
930
+ end
931
+ end
932
+
933
+ class << self
934
+
935
+ def s_hint
936
+ @s_hint ||= 'Type here'
937
+ end
938
+
939
+ def s_hint=(v)
940
+ @s_hint = v
941
+ end
942
+
943
+ end
944
+
945
+ def on_set_hint(_event)
946
+ unless (entry = current_page.get_text_entry)
947
+ Wx.log_error('menu item should be disabled')
948
+ return
949
+ end
950
+
951
+ hint = Wx.get_text_from_user('Text hint:', 'Widgets sample', self.class.s_hint, self)
952
+ return if hint.empty?
953
+
954
+ self.class.s_hint = hint
955
+
956
+ if entry.set_hint(hint)
957
+ Wx.log_message("Set hint to \"#{hint}\".")
958
+ else
959
+ Wx.log_message('Text hints not supported.')
960
+ end
961
+ end
962
+
963
+ def on_update_text_ui(event)
964
+ event.enable( !current_page.get_text_entry.nil? )
965
+ end
966
+ end # wxUSE_MENUS
967
+
968
+ # initialize the book: add all pages to it
969
+ def init_book
970
+ images = []
971
+
972
+ img = Wx.Image(:sample, art_path: File.dirname(__dir__))
973
+ images << img.scale(ICON_SIZE, ICON_SIZE).to_bitmap
974
+
975
+ unless Wx.has_feature?(:USE_TREEBOOK)
976
+ books = []
977
+ end
978
+
979
+ pages = []
980
+ labels = []
981
+
982
+ menuPages = Wx::Menu.new
983
+ nPage = 0
984
+ imageId = 1
985
+
986
+ # we need to first create all pages and only then add them to the book
987
+ # as we need the image list first
988
+ #
989
+ # we also construct the pages menu during this first iteration
990
+ MAX_PAGES.times do |cat|
991
+ if Wx.has_feature?(:USE_TREEBOOK)
992
+ nPage += 1 # increase for parent page
993
+ else
994
+ books << BookCtrl.new(@book, style: Wx::BK_DEFAULT)
995
+ end
996
+
997
+ pages << []
998
+ labels << []
999
+ Page.widget_pages.each do |info|
1000
+ next if info.categories.nobits?(1 << cat)
1001
+
1002
+ page = info.create(
1003
+ if Wx.has_feature?(:USE_TREEBOOK)
1004
+ @book
1005
+ else
1006
+ books.last
1007
+ end,
1008
+ images)
1009
+ pages.last << page
1010
+
1011
+ labels.last << info.label
1012
+ if cat == ALL_PAGE
1013
+ menuPages.append_radio_item(
1014
+ ID::Widgets_GoToPage + nPage,
1015
+ info.label)
1016
+ unless Wx.has_feature?(:USE_TREEBOOK)
1017
+ # consider only for book in book architecture
1018
+ nPage += 1
1019
+ end
1020
+ end
1021
+
1022
+ if Wx.has_feature?(:USE_TREEBOOK)
1023
+ # consider only for treebook architecture (with subpages)
1024
+ nPage += 1
1025
+ end
1026
+ end
1027
+ end
1028
+
1029
+ self.menu_bar.append(menuPages, '&Page')
1030
+
1031
+ images.collect! { |img_| img_.is_a?(Wx::BitmapBundle) ? img_ : Wx::BitmapBundle.new(img_) }
1032
+ @book.set_images(images)
1033
+
1034
+ MAX_PAGES.times do |cat|
1035
+ if Wx.has_feature?(:USE_TREEBOOK)
1036
+ @book.add_page(nil, Categories[cat],false,0)
1037
+ else
1038
+ @book.add_page(books[cat], Categories[cat],false,0)
1039
+ books[cat].set_images(images)
1040
+ end
1041
+
1042
+ # now do add them
1043
+ pages[cat].size.times do |n|
1044
+ if Wx.has_feature?(:USE_TREEBOOK)
1045
+ @book.add_sub_page(
1046
+ pages[cat][n],
1047
+ labels[cat][n],
1048
+ false, # don't select
1049
+ imageId
1050
+ )
1051
+ else
1052
+ books[cat].add_page(
1053
+ pages[cat][n],
1054
+ labels[cat][n],
1055
+ false, # don't select
1056
+ imageId
1057
+ )
1058
+ end
1059
+ imageId += 1
1060
+ end
1061
+ end
1062
+
1063
+ if Wx.has_feature?(:USE_TREEBOOK)
1064
+ evt_treebook_page_changed(ID::Widgets_BookCtrl, :on_page_changed)
1065
+ elsif Wx.has_feature?(:USE_NOTEBOOK)
1066
+ evt_notebook_page_changed(ID::Widgets_BookCtrl, :on_page_changed)
1067
+ else
1068
+ evt_choicebook_page_changed(ID::Widgets_BookCtrl, :on_page_changed)
1069
+ end
1070
+
1071
+ # TODO - review wxPersistenceManager
1072
+ # const bool pageSet = wxPersistentRegisterAndRestore(m_book)
1073
+ pageSet = false
1074
+
1075
+ if Wx.has_feature?(:USE_TREEBOOK)
1076
+ # for treebook page #0 is empty parent page only so select the first page
1077
+ # with some contents
1078
+ @book.set_selection(1) if !pageSet || !@book.get_current_page
1079
+
1080
+ # but ensure that the top of the tree is shown nevertheless
1081
+ tree = @book.get_tree_ctrl
1082
+
1083
+ first_child, _ = tree.get_first_child(tree.root_item)
1084
+ tree.ensure_visible(first_child)
1085
+ else
1086
+ if !pageSet || !@book.get_current_page
1087
+ # for other books set selection twice to force connected event handler
1088
+ # to force lazy creation of initial visible content
1089
+ @book.set_selection(1)
1090
+ @book.set_selection(0)
1091
+ end
1092
+ end # USE_TREEBOOK
1093
+ end
1094
+
1095
+ # return the currently selected page (never null)
1096
+ def current_page
1097
+ page = @book.get_current_page
1098
+
1099
+ unless Wx.has_feature?(:USE_TREEBOOK)
1100
+ ::Kernel.raise 'no WidgetsBookCtrl?' unless page.is_a?(Wx::Treebook)
1101
+
1102
+ page = page.get_current_page
1103
+ end # !USE_TREEBOOK
1104
+
1105
+ page
1106
+ end
1107
+
1108
+ private
1109
+
1110
+ def on_widget_focus(event)
1111
+ # Don't show annoying message boxes when starting or closing the sample,
1112
+ # only log these events in our own logger.
1113
+ if Wx.get_app.is_using_log_window
1114
+ win = event.get_event_object
1115
+ Wx.log_message("Widget '#{win.class.name}' #{event.event_type == Wx::EVT_SET_FOCUS ? "got" : "lost"} focus")
1116
+ end
1117
+
1118
+ event.skip
1119
+ end
1120
+ def on_widget_context_menu(event)
1121
+ win = event.get_event_object
1122
+ Wx.log_message("Context menu event for #{win.class.name} at #{event.position.x}x#{event.position.y}")
1123
+
1124
+ event.skip
1125
+ end
1126
+
1127
+ def connect_to_widget_events
1128
+ widgets = current_page.get_widgets
1129
+
1130
+ widgets.each do |w|
1131
+ ::Kernel.raise RuntimeError, 'nil widget' unless w
1132
+
1133
+ w.evt_set_focus self.method(:on_widget_focus)
1134
+ w.evt_kill_focus self.method(:on_widget_focus)
1135
+
1136
+ w.evt_context_menu self.method(:on_widget_context_menu)
1137
+ end
1138
+ end
1139
+
1140
+ end
1141
+
1142
+ class App < Wx::App
1143
+ def initialize
1144
+ super
1145
+ if Wx.has_feature?(:USE_LOG)
1146
+ @logTarget = nil
1147
+ end
1148
+ end
1149
+
1150
+ # this one is called on application startup and is a good place for the app
1151
+ # initialization (doing it here and not in the ctor allows to have an error
1152
+ # return: if OnInit() returns false, the application terminates)
1153
+ def on_init
1154
+ set_vendor_name('wxWidgets_Samples')
1155
+
1156
+ # when running multiple copies of this sample side by side this is useful to see which one is which
1157
+ title = ''
1158
+ # if Wx.has_feature?(:WXUNIVERSAL)
1159
+ # title << "wxUniv/"
1160
+ # end
1161
+ title << Wx::PLATFORM
1162
+
1163
+ frame = Frame.new(title + ' widgets demo')
1164
+ frame.show
1165
+
1166
+ if Wx.has_feature?(:USE_LOG)
1167
+ @logTarget = Wx::Log.get_active_target
1168
+ end # USE_LOG
1169
+
1170
+ true
1171
+ end
1172
+
1173
+ # real implementation of WidgetsPage method with the same name
1174
+ def is_using_log_window
1175
+ Wx.has_feature?(:USE_LOG) && (Wx::Log.get_active_target == @logTarget)
1176
+ end
1177
+ end
1178
+
1179
+ end # module Widgets
1180
+
1181
+ require_relative './activityindicator' if Wx.has_feature?(:USE_ACTIVITYINDICATOR)
1182
+ require_relative './bmpcombobox' if Wx.has_feature?(:USE_BITMAPCOMBOBOX)
1183
+ require_relative './button' if Wx.has_feature?(:USE_BUTTON)
1184
+ require_relative './checkbox' if Wx.has_feature?(:USE_CHECKBOX)
1185
+ require_relative './choice' if Wx.has_feature?(:USE_CHOICE)
1186
+ require_relative './clrpicker' if Wx.has_feature?(:USE_COLOURPICKERCTRL) && Wx.has_feature?(:USE_COLOURDLG)
1187
+ require_relative './combobox' if Wx.has_feature?(:USE_COMBOBOX)
1188
+ require_relative './datepick' if Wx.has_feature?(:USE_DATEPICKCTRL)
1189
+ require_relative './dirctrl' if Wx.has_feature?(:USE_FILEDLG) || Wx.has_feature?(:USE_DIRDLG)
1190
+ require_relative './dirpicker' if Wx.has_feature?(:USE_DIRPICKERCTRL) && Wx.has_feature?(:USE_DIRDLG)
1191
+ require_relative './editlbox' if Wx.has_feature?(:USE_EDITABLELISTBOX)
1192
+ require_relative './filectrl' if Wx.has_feature?(:USE_FILECTRL)
1193
+ require_relative './filepicker' if Wx.has_feature?(:USE_FILEPICKERCTRL) && Wx.has_feature?(:USE_FILEDLG)
1194
+ require_relative './fontpicker' if Wx.has_feature?(:USE_FONTPICKERCTRL) && Wx.has_feature?(:USE_FONTDLG)
1195
+ require_relative './gauge' if Wx.has_feature?(:USE_GAUGE)
1196
+ require_relative './headerctrl'
1197
+ require_relative './hyperlink' if Wx.has_feature?(:USE_HYPERLINKCTRL)
1198
+ require_relative './listbox' if Wx.has_feature?(:USE_LISTBOX)
1199
+ require_relative './notebook' if Wx.has_feature?(:USE_NOTEBOOK) || Wx.has_feature?(:USE_LISTBOOK) || Wx.has_feature?(:USE_CHOICEBOOK)
1200
+ require_relative './odcombobox' if Wx.has_feature?(:USE_ODCOMBOBOX)
1201
+ require_relative './radiobox' if Wx.has_feature?(:USE_RADIOBOX)
1202
+ require_relative './searchctrl' if Wx.has_feature?(:USE_SEARCHCTRL)
1203
+ require_relative './slider' if Wx.has_feature?(:USE_SLIDER)
1204
+ require_relative './spinbtn' if Wx.has_feature?(:USE_SPINBTN)
1205
+ require_relative './statbmp' if Wx.has_feature?(:USE_STATBMP)
1206
+ require_relative './static'
1207
+ require_relative './timepick' if Wx.has_feature?(:USE_TIMEPICKCTRL)
1208
+ require_relative './toggle' if Wx.has_feature?(:USE_TOGGLEBTN)
1209
+ require_relative './textctrl'
1210
+
1211
+ module WidgetsSample
1212
+
1213
+ include WxRuby::Sample if defined? WxRuby::Sample
1214
+
1215
+ def self.describe
1216
+ { file: __FILE__,
1217
+ summary: 'wxRuby Widgets example.',
1218
+ description: <<~__DESC
1219
+ A Ruby port of the extensive wxWidgets widgets sample which showcases
1220
+ a wide selection of widgets (controls).
1221
+ __DESC
1222
+ }
1223
+ end
1224
+
1225
+ def self.run
1226
+ execute(__FILE__)
1227
+ end
1228
+
1229
+ if $0 == __FILE__
1230
+ Widgets::App.run
1231
+ end
1232
+
1233
+ end