wxruby3 0.9.3 → 0.9.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/INSTALL.md +1 -1
  3. data/README.md +2 -2
  4. data/ext/wxruby3/include/wxruby-ComboPopup.h +777 -0
  5. data/ext/wxruby3/include/wxruby-Config.h +23 -5
  6. data/ext/wxruby3/include/wxruby-Persistence.h +79 -0
  7. data/ext/wxruby3/swig/memory_management.i +6 -0
  8. data/lib/wx/core/book_ctrl_base.rb +16 -0
  9. data/lib/wx/core/combo_ctrl.rb +171 -0
  10. data/lib/wx/core/config.rb +454 -83
  11. data/lib/wx/core/notebook.rb +10 -8
  12. data/lib/wx/core/peristent_object.rb +15 -0
  13. data/lib/wx/core/persistence_manager.rb +39 -0
  14. data/lib/wx/core/persistent_window.rb +16 -0
  15. data/lib/wx/core/top_level_window.rb +16 -0
  16. data/lib/wx/core/treebook.rb +18 -0
  17. data/lib/wx/core.rb +4 -0
  18. data/lib/wx/doc/book_ctrl_base.rb +19 -0
  19. data/lib/wx/doc/comboctrl.rb +128 -3
  20. data/lib/wx/doc/config.rb +101 -41
  21. data/lib/wx/doc/extra/14_config.md +101 -0
  22. data/lib/wx/doc/extra/15_persistence.md +148 -0
  23. data/lib/wx/doc/owner_drawn_combobox.rb +5 -1
  24. data/lib/wx/doc/persistence_manager.rb +36 -0
  25. data/lib/wx/doc/persistent_object.rb +27 -0
  26. data/lib/wx/doc/top_level_window.rb +19 -0
  27. data/lib/wx/doc/treebook.rb +6 -1
  28. data/lib/wx/version.rb +1 -1
  29. data/rakelib/build.rb +1 -1
  30. data/rakelib/lib/core/include/funcall.inc +2 -1
  31. data/rakelib/lib/core/package.rb +22 -1
  32. data/rakelib/lib/core/spec.rb +10 -0
  33. data/rakelib/lib/core/spec_helper.rb +1 -1
  34. data/rakelib/lib/director/comboctrl.rb +104 -3
  35. data/rakelib/lib/director/config_base.rb +490 -19
  36. data/rakelib/lib/director/defs.rb +1 -3
  37. data/rakelib/lib/director/event_filter.rb +1 -1
  38. data/rakelib/lib/director/event_loop.rb +1 -1
  39. data/rakelib/lib/director/file_dialog_customize_hook.rb +2 -2
  40. data/rakelib/lib/director/gdicommon.rb +6 -0
  41. data/rakelib/lib/director/grid_cell_attr.rb +1 -1
  42. data/rakelib/lib/director/grid_cell_editor.rb +1 -1
  43. data/rakelib/lib/director/grid_cell_renderer.rb +1 -1
  44. data/rakelib/lib/director/header_ctrl.rb +3 -0
  45. data/rakelib/lib/director/html_listbox.rb +2 -1
  46. data/rakelib/lib/director/menu_item.rb +1 -1
  47. data/rakelib/lib/director/num_validator.rb +5 -7
  48. data/rakelib/lib/director/owner_drawn_combobox.rb +1 -0
  49. data/rakelib/lib/director/persistence_manager.rb +410 -0
  50. data/rakelib/lib/director/persistent_object.rb +70 -0
  51. data/rakelib/lib/director/persistent_window.rb +73 -0
  52. data/rakelib/lib/director/pgeditor.rb +1 -1
  53. data/rakelib/lib/director/pgproperties.rb +3 -3
  54. data/rakelib/lib/director/pgproperty.rb +5 -1
  55. data/rakelib/lib/director/richtext_style_listbox.rb +5 -0
  56. data/rakelib/lib/director/sizer.rb +1 -1
  57. data/rakelib/lib/director/static_bitmap.rb +4 -0
  58. data/rakelib/lib/director/text_entry.rb +1 -1
  59. data/rakelib/lib/director/window.rb +4 -0
  60. data/rakelib/lib/extractor/module.rb +15 -0
  61. data/rakelib/lib/generate/analyzer.rb +43 -43
  62. data/rakelib/lib/generate/doc/combo_ctrl.yaml +135 -0
  63. data/rakelib/lib/generate/doc/file_dialog_customize_hook.yaml +62 -0
  64. data/rakelib/lib/generate/doc/file_system.yaml +28 -0
  65. data/rakelib/lib/generate/doc.rb +1 -1
  66. data/rakelib/lib/generate/interface.rb +12 -4
  67. data/rakelib/lib/specs/interfaces.rb +3 -0
  68. data/rakelib/lib/swig_runner.rb +7 -4
  69. data/rakelib/lib/typemap/combo_popup.rb +42 -0
  70. data/rakelib/lib/typemap/config.rb +8 -0
  71. data/samples/widgets/widgets.rb +5 -9
  72. data/tests/test_combo_ctrl.rb +196 -0
  73. data/tests/test_config.rb +207 -42
  74. data/tests/test_persistence.rb +142 -0
  75. metadata +26 -2
@@ -0,0 +1,196 @@
1
+ # Copyright (c) 2023 M.J.N. Corino, The Netherlands
2
+ #
3
+ # This software is released under the MIT license.
4
+
5
+ require_relative './lib/wxframe_runner'
6
+ require_relative './lib/text_entry_tests'
7
+
8
+ class ComboCtrlCtrlTests < WxRuby::Test::GUITests
9
+
10
+ include TextEntryTests
11
+
12
+ class LVComboPopup < Wx::ListView
13
+
14
+ include Wx::ComboPopup
15
+
16
+ def initialize
17
+ # call default control ctor; need to call Wx::ListView#create later
18
+ super
19
+ end
20
+
21
+ def init
22
+ @value = -1
23
+ end
24
+
25
+ def create(parent)
26
+ # need to finish creating the list view here
27
+ # as calling super here would just call Wx::ComboPopup#create and not Wx::ListView#create
28
+ # we need to use Ruby magic
29
+ wx_lv_create = (Wx::ListView.instance_method :create).bind(self)
30
+ wx_lv_create.call(parent, 1, [0,0], Wx::DEFAULT_SIZE)
31
+ evt_motion :on_mouse_move
32
+ evt_left_up :on_mouse_click
33
+ end
34
+
35
+ # Return pointer to the created control
36
+ def get_control
37
+ self
38
+ end
39
+
40
+ def lv_find_item(*args)
41
+ unless @wx_lv_find_item
42
+ @wx_lv_find_item = (Wx::ListView.instance_method :find_item).bind(self)
43
+ end
44
+ @wx_lv_find_item.call(*args)
45
+ end
46
+ protected :lv_find_item
47
+
48
+ # Translate string into a list selection
49
+ def set_string_value(s)
50
+ n = lv_find_item(-1, s)
51
+ if n >= 0 && n < get_item_count
52
+ select(n)
53
+ @value = n
54
+ end
55
+ end
56
+
57
+ # Get list selection as a string
58
+ def get_string_value
59
+ return get_item_text(@value) if @value >= 0
60
+ ''
61
+ end
62
+
63
+ # Do mouse hot-tracking (which is typical in list popups)
64
+ def on_mouse_move(event)
65
+ # Move selection to cursor ...
66
+ end
67
+
68
+ # On mouse left up, set the value and close the popup
69
+ def on_mouse_click(_event)
70
+ @value = get_first_selected
71
+
72
+ # Send event as well ...
73
+
74
+ dismiss
75
+ end
76
+
77
+ end
78
+
79
+ def setup
80
+ super
81
+ @combo = Wx::ComboCtrl.new(frame_win, name: 'ComboCtrl')
82
+ @combo.set_popup_control(LVComboPopup.new)
83
+ end
84
+
85
+ def cleanup
86
+ @combo.destroy
87
+ super
88
+ end
89
+
90
+ attr_reader :combo
91
+ alias :text_entry :combo
92
+
93
+ def fill_list(list)
94
+ list.insert_item(0, 'This is the first item')
95
+ list.insert_item(1, 'This is the second item')
96
+ list.insert_item(2, 'This is the third item')
97
+ list.insert_item(3, 'This is the fourth item')
98
+ end
99
+
100
+ def test_popup
101
+ assert_equal('', combo.get_value)
102
+
103
+ assert_kind_of(Wx::ComboPopup, combo.get_popup_control)
104
+ assert_kind_of(Wx::ListView, combo.get_popup_control)
105
+ assert_kind_of(Wx::ListView, combo.get_popup_control.get_control)
106
+
107
+ assert_nothing_raised { fill_list(combo.get_popup_control) }
108
+ combo.popup
109
+
110
+ combo.set_value_by_user('This is the second item')
111
+
112
+ assert_equal('This is the second item', combo.get_popup_control.get_string_value)
113
+
114
+ combo.dismiss
115
+ end
116
+
117
+ end
118
+
119
+ class OwnerDrawnCBTests < WxRuby::Test::GUITests
120
+
121
+ include TextEntryTests
122
+
123
+ class TestODComboBox < Wx::OwnerDrawnComboBox
124
+
125
+ def on_draw_item(dc, rect, item, _flags)
126
+ return if item == Wx::NOT_FOUND
127
+
128
+ dc.set_text_foreground(Wx::BLACK)
129
+ dc.draw_text(get_string(item),
130
+ rect.x + 3,
131
+ rect.y + ((rect.height - dc.char_height)/2))
132
+ end
133
+
134
+ def on_draw_background(dc, rect, item, flags)
135
+ # If item is selected or even, or we are painting the
136
+ # combo control itself, use the default rendering.
137
+ if flags.anybits?(Wx::ODCB_PAINTING_CONTROL|Wx::ODCB_PAINTING_SELECTED) || (item & 1) == 0
138
+ super(dc,rect,item,flags)
139
+ return
140
+ end
141
+
142
+ # Otherwise, draw every other background with different colour.
143
+ bgCol = Wx::Colour.new(240,240,250)
144
+ dc.set_brush(Wx::Brush.new(bgCol))
145
+ dc.set_pen(Wx::Pen.new(bgCol))
146
+ dc.draw_rectangle(rect)
147
+ end
148
+
149
+ def on_measure_item(_item)
150
+ 48
151
+ end
152
+
153
+ def on_measure_item_width(_item)
154
+ -1 # default - will be measured from text width
155
+ end
156
+
157
+ end
158
+
159
+ def setup
160
+ super
161
+ @combo = TestODComboBox.new(frame_win, name: 'ODComboBox')
162
+ end
163
+
164
+ def cleanup
165
+ @combo.destroy
166
+ super
167
+ end
168
+
169
+ attr_reader :combo
170
+ alias :text_entry :combo
171
+
172
+ def fill_list(list)
173
+ list.append('This is the first item')
174
+ list.append('This is the second item')
175
+ list.append('This is the third item')
176
+ list.append('This is the fourth item')
177
+ end
178
+
179
+ def test_popup
180
+ assert_equal('', combo.get_value)
181
+
182
+ assert_kind_of(Wx::ComboPopup, combo.get_popup_control)
183
+ assert_kind_of(Wx::ComboPopupWx, combo.get_popup_control)
184
+ assert_kind_of(Wx::VListBox, combo.get_popup_control.get_control)
185
+
186
+ assert_nothing_raised { fill_list(combo) }
187
+ combo.popup
188
+
189
+ combo.set_value_by_user('This is the third item')
190
+
191
+ assert_equal('This is the third item', combo.get_popup_control.get_string_value)
192
+
193
+ combo.dismiss
194
+ end
195
+
196
+ end
data/tests/test_config.rb CHANGED
@@ -13,21 +13,21 @@ class TestConfig < Test::Unit::TestCase
13
13
  'RootEntry4' => 3.14,
14
14
  'Group1' => {
15
15
  'Group1Entry' => 'Group1 string',
16
- 'Group1.1' => {
17
- 'Group1.1Integer' => 999,
18
- 'Group1.1Bignum' => 2**999,
19
- 'Group1.1Float' => (2**999)-0.1
16
+ 'Group1_1' => {
17
+ 'Group1_1Integer' => 999,
18
+ 'Group1_1Bignum' => 2**999,
19
+ 'Group1_1Float' => (2**999)-0.1
20
20
  }
21
21
  },
22
22
  'Group2' => {
23
- 'Group2.1' => {
24
- 'Group2.1.1' => {
23
+ 'Group2_1' => {
24
+ 'Group2_1_1' => {
25
25
  'String' => 'hello'
26
26
  },
27
- 'Group2.1.2' => {
27
+ 'Group2_1_2' => {
28
28
  'String' => 'world'
29
29
  },
30
- 'Group2.1.3' => {
30
+ 'Group2_1_3' => {
31
31
  'True' => true,
32
32
  'False' => false
33
33
  }
@@ -35,79 +35,222 @@ class TestConfig < Test::Unit::TestCase
35
35
  }
36
36
  }
37
37
 
38
+ def stringified_entry(val)
39
+ case val
40
+ when TrueClass,FalseClass
41
+ val ? '1' : '0'
42
+ when Float
43
+ '%g' % val
44
+ else
45
+ val.to_s
46
+ end
47
+ end
48
+
49
+ def stringified(val)
50
+ val.is_a?(::Hash) ? val.inject({}) { |hash, pair| hash[pair.first] = stringified(pair.last); hash } : stringified_entry(val)
51
+ end
52
+
53
+ def assert_true_cfg(val)
54
+ assert_block('expected "1" or true') do
55
+ val == '1' || val == 1 || val == true
56
+ end
57
+ end
58
+
59
+ def assert_false_cfg(val)
60
+ assert_block("expected '0' or false") do
61
+ val == '0' || val == 0 || val == false
62
+ end
63
+ end
64
+
65
+ def assert_equal_cfg(expected, val)
66
+ assert_block("expected #{expected.is_a?(::Hash) ? stringified(expected) : %Q['#{stringified(expected)}']} \nor #{expected}\nbut got #{val}") do
67
+ expected == val || stringified(expected) == stringified(val)
68
+ end
69
+ end
70
+
38
71
  def run_config_tests(cfg)
39
- assert_equal(DEMO_CONFIG, cfg.to_h)
72
+ assert_equal_cfg(DEMO_CONFIG, cfg.to_h)
40
73
 
41
- assert_equal(4, cfg.number_of_entries)
42
- assert_equal(2, cfg.number_of_groups)
43
- assert_equal(12, cfg.number_of_entries(recurse: true))
44
- assert_equal(7, cfg.number_of_groups(recurse: true))
74
+ assert_equal_cfg(4, cfg.number_of_entries)
75
+ assert_equal_cfg(2, cfg.number_of_groups)
76
+ assert_equal_cfg(12, cfg.number_of_entries(recurse: true))
77
+ assert_equal_cfg(7, cfg.number_of_groups(recurse: true))
45
78
 
46
79
  assert_true(cfg.has_entry?('/RootEntry2'))
47
80
  assert_true(cfg.has_entry?('/Group1/Group1Entry'))
48
- assert_true(cfg.has_entry?('/Group2/Group2.1/Group2.1.2/String'))
81
+ assert_true(cfg.has_entry?('/Group2/Group2_1/Group2_1_2/String'))
49
82
 
50
- assert_false(cfg.has_entry?('/Group2/Group2.2/Group2.1.2/String'))
83
+ assert_false(cfg.has_entry?('/Group2/Group2.2/Group2_1_2/String'))
51
84
 
52
85
  assert_true(cfg.has_group?('/Group2'))
53
- assert_true(cfg.has_group?('/Group1/Group1.1'))
54
- assert_true(cfg.has_group?('/Group2/Group2.1/Group2.1.2'))
86
+ assert_true(cfg.has_group?('/Group1/Group1_1'))
87
+ assert_true(cfg.has_group?('/Group2/Group2_1/Group2_1_2'))
55
88
 
56
- assert_false(cfg.has_group?('/Group2/Group2.1/Group2.1.2/String'))
89
+ assert_false(cfg.has_group?('/Group2/Group2_1/Group2_1_2/String'))
57
90
 
58
- grp = cfg['/Group1/Group1.1']
91
+ grp = cfg['/Group1/Group1_1']
59
92
 
60
- assert_equal(DEMO_CONFIG['Group1']['Group1.1'], grp.to_h)
93
+ assert_equal_cfg(DEMO_CONFIG['Group1']['Group1_1'], grp.to_h)
61
94
 
62
95
  assert_equal(3, grp.number_of_entries)
63
96
  assert_equal(0, grp.number_of_groups)
64
97
 
65
- assert_true(grp.has_entry?('Group1.1Integer'))
98
+ assert_true(grp.has_entry?('Group1_1Integer'))
66
99
  assert_false(grp.has_entry?('Group1Entry'))
67
100
  assert_true(grp.has_entry?('../Group1Entry'))
68
101
 
69
- assert_true(grp.has_group?('/Group2/Group2.1/Group2.1.2'))
102
+ assert_true(grp.has_group?('/Group2/Group2_1/Group2_1_2'))
70
103
 
71
104
  assert_equal('This is a string value', cfg['/RootEntry1'])
72
- assert_equal(true, cfg['/RootEntry2'])
73
- assert_equal(101, cfg['/RootEntry3'])
74
- assert_equal(3.14, cfg['/RootEntry4'])
105
+ assert_equal_cfg(true, cfg['/RootEntry2'])
106
+ assert_equal_cfg(101, cfg['/RootEntry3'])
107
+ assert_equal_cfg(3.14, cfg['/RootEntry4'])
75
108
 
76
- grp = cfg['/Group2/Group2.1/Group2.1.3']
77
- assert_true(grp.get('True'))
78
- assert_false(grp.get('False'))
79
- assert_nil(grp.get('../Group2.1.2/String'))
109
+ grp = cfg['/Group2/Group2_1/Group2_1_3']
110
+ assert_true_cfg(grp.get('True'))
111
+ assert_false_cfg(grp.get('False'))
112
+ assert_raise(ArgumentError) { grp.get('../Group2_1_2/String') }
80
113
 
81
- assert_true(grp['True'])
82
- assert_false(grp['False'])
83
- assert_equal('world', grp['../Group2.1.2/String'])
114
+ assert_true_cfg(grp['True'])
115
+ assert_false_cfg(grp['False'])
116
+ assert_equal('world', grp['../Group2_1_2/String'])
84
117
 
85
118
  cfg.set('RootEntry1', 'Altered string value')
86
119
  assert_equal('Altered string value', cfg['RootEntry1'])
87
120
  assert_equal('Altered string value', cfg['/RootEntry1'])
88
121
  assert_equal('Altered string value', cfg.get('RootEntry1'))
89
122
 
90
- cfg.set('RootEntry3', cfg.get('RootEntry3')+99)
91
- assert_equal(200, cfg['/RootEntry3'])
123
+ cfg.set('RootEntry3', cfg.read('RootEntry3', ::Integer)+99)
124
+ assert_equal_cfg(200, cfg['/RootEntry3'])
125
+
126
+ cfg.set('Group1', { 'Group1_2' => { 'Integer' => 777 }})
127
+ assert_equal_cfg(777, cfg['/Group1/Group1_2/Integer'])
128
+
129
+ cfg['/Group1/Group1_2/Integer'] = 666
130
+ assert_equal_cfg(666, cfg['/Group1/Group1_2'].get('Integer'))
131
+
132
+ cfg['/Group1/Group1_2'] = { 'Float' => 0.3330 }
133
+ assert_equal_cfg(0.3330, cfg['/Group1/Group1_2'].get('Float'))
134
+
135
+ assert_equal(0.3330, cfg.read('/Group1/Group1_2/Float').to_f)
136
+ assert_equal(0.3330, cfg.read('/Group1/Group1_2/Float', Float))
137
+ assert_equal(0.3330, cfg.read('/Group1/Group1_2/Float', ->(v) { v.to_f }))
138
+
139
+ cfg.replace(DEMO_CONFIG) # reset
140
+ end
141
+
142
+ def run_auto_accessor_tests(cfg)
143
+ assert_not_nil(cfg.RootEntry2)
144
+ assert_not_nil(cfg.Group1.Group1Entry)
145
+ assert_not_nil(cfg.Group2.Group2_1.Group2_1_2.String)
146
+
147
+ assert_nil(cfg.Group2.Group2_1.Group2_1_2.AString)
148
+
149
+ assert_kind_of(cfg.class::Group, cfg.Group2)
150
+ assert_kind_of(cfg.class::Group, cfg.Group1.Group1_1)
151
+ assert_kind_of(cfg.class::Group, cfg.Group2.Group2_1.Group2_1_2)
152
+
153
+ assert_not_kind_of(cfg.class::Group, cfg.Group2.Group2_1.Group2_1_2.String)
154
+
155
+ grp = cfg.Group1
156
+
157
+ assert_equal_cfg(DEMO_CONFIG['Group1'], grp.to_h)
158
+
159
+ assert_not_nil(grp.Group1Entry)
160
+ assert_nil(grp.Group1_1Integer)
161
+
162
+ assert_kind_of(grp.class, grp.Group1_1)
163
+ assert_not_nil(grp.Group1_1.Group1_1Integer)
164
+
165
+ assert_true(grp.has_entry?('../RootEntry1'))
166
+
167
+ assert_true(grp.has_group?('/Group2/Group2_1/Group2_1_2'))
168
+
169
+ assert_equal_cfg('This is a string value', cfg.RootEntry1)
170
+ assert_equal_cfg(true, cfg.RootEntry2)
171
+ assert_equal_cfg(101, cfg.RootEntry3)
172
+ assert_equal_cfg(3.14, cfg.RootEntry4)
92
173
 
93
- cfg.set('Group1', { 'Group1.2' => { 'Integer' => 777 }})
94
- assert_equal(777, cfg['/Group1/Group1.2/Integer'])
174
+ grp = cfg.Group2.Group2_1.Group2_1_3
175
+ assert_true_cfg(grp.True)
176
+ assert_false_cfg(grp.False)
95
177
 
96
- cfg['/Group1/Group1.2/Integer'] = 666
97
- assert_equal(666, cfg['/Group1/Group1.2'].get('Integer'))
178
+ assert_true_cfg(grp['True'])
179
+ assert_false_cfg(grp['False'])
180
+ assert_equal_cfg('world', grp['../Group2_1_2/String'])
98
181
 
99
- cfg['/Group1/Group1.2'] = { 'Float' => 0.3330 }
100
- assert_equal(0.3330, cfg['/Group1/Group1.2'].get('Float'))
182
+ cfg.RootEntry1 = 'Altered string value'
183
+ assert_equal_cfg('Altered string value', cfg['RootEntry1'])
184
+ assert_equal_cfg('Altered string value', cfg['/RootEntry1'])
185
+ assert_equal_cfg('Altered string value', cfg.get('RootEntry1'))
186
+ assert_equal_cfg('Altered string value', cfg.RootEntry1)
187
+
188
+ cfg.RootEntry3 = (Kernel.Integer(cfg.RootEntry3) rescue 0)+99
189
+ assert_equal_cfg(200, cfg.RootEntry3)
190
+
191
+ cfg.Group1 = { 'Group1_2' => { 'Integer' => 777 }}
192
+ assert_equal_cfg(777, cfg.Group1.Group1_2.Integer)
193
+
194
+ cfg.Group1.Group1_2.Integer = 666
195
+ assert_equal_cfg(666, cfg.Group1.Group1_2.get('Integer'))
196
+
197
+ cfg.Group1.Group1_2 = { 'Float' => 0.3330 }
198
+ assert_equal_cfg(0.3330, cfg.Group1.Group1_2.get('Float'))
199
+
200
+ cfg.replace(DEMO_CONFIG) # reset
201
+ end
202
+
203
+ def run_env_var_tests(cfg)
204
+ # by default expansion is on
205
+
206
+ # add a number of entries for env var in new group 'Environment'
207
+ cfg['/Environment/HOME'] = '$HOME'
208
+ cfg['Environment'].USER = Wx::PLATFORM == 'WXMSW' ? '%USERNAME%' : '${USER}'
209
+ cfg['/Environment/PATH'] = '$(PATH)'
210
+
211
+ assert_equal(ENV['HOME'], cfg.Environment['HOME'])
212
+ assert_equal(ENV[Wx::PLATFORM == 'WXMSW' ? 'USERNAME' : 'USER'], cfg['/Environment/USER'])
213
+ assert_equal(ENV['PATH'], cfg.Environment.PATH)
214
+
215
+ # test escaping
216
+ cfg['/Environment/Escaped_HOME'] = '\$HOME'
217
+ cfg['/Environment/Escaped_HOME2'] = '\\$HOME'
218
+ cfg['/Environment/Escaped_HOME3'] = '\\\$HOME'
219
+
220
+ assert_equal('$HOME', cfg.Environment['Escaped_HOME'])
221
+ assert_equal('$HOME', cfg.Environment['Escaped_HOME2'])
222
+ assert_equal('\$HOME', cfg.Environment['Escaped_HOME3'])
223
+
224
+ cfg['/Environment/NONSENSE'] = '${NonExistingLongNonsenseVariable}'
225
+
226
+ assert_equal('${NonExistingLongNonsenseVariable}', cfg.Environment['NONSENSE'])
227
+
228
+ cfg['/Environment/MULTIPLE'] = "$HOME / #{Wx::PLATFORM == 'WXMSW' ? '%USERNAME%' : '${USER}'}"
229
+
230
+ assert_equal("#{ENV['HOME']} / #{Wx::PLATFORM == 'WXMSW' ? ENV['USERNAME'] : ENV['USER']}", cfg.Environment['MULTIPLE'])
231
+
232
+ # disable env var expansion
233
+ cfg.expand_env_vars = false
234
+ begin
235
+ assert_equal('$HOME', cfg.Environment['HOME'])
236
+ ensure
237
+ # re-enable
238
+ cfg.set_expand_env_vars(true)
239
+ end
101
240
  end
102
241
 
103
242
  def test_basic
104
243
  cfg = Wx::Config.new(DEMO_CONFIG)
105
244
 
106
245
  run_config_tests(cfg)
246
+ run_auto_accessor_tests(cfg)
247
+ run_env_var_tests(cfg)
107
248
  end
108
249
 
109
250
  def test_global
110
- cfg = Wx::ConfigBase.create
251
+ cfg = Wx::ConfigBase.create(true, use_hash_config: true)
252
+
253
+ assert_kind_of(Wx::Config, cfg)
111
254
 
112
255
  cfg.replace(DEMO_CONFIG)
113
256
 
@@ -116,6 +259,8 @@ class TestConfig < Test::Unit::TestCase
116
259
  assert_equal(cfg, Wx::ConfigBase.get(false))
117
260
 
118
261
  run_config_tests(cfg)
262
+ run_auto_accessor_tests(cfg)
263
+ run_env_var_tests(cfg)
119
264
 
120
265
  cfg_old = Wx::ConfigBase.set(nil)
121
266
 
@@ -123,8 +268,28 @@ class TestConfig < Test::Unit::TestCase
123
268
  assert_nil(Wx::ConfigBase.get(false))
124
269
  end
125
270
 
271
+ # default registry based config does not seem to do well in CI build env
272
+ unless is_ci_build? && Wx::PLATFORM == 'WXMSW'
273
+
274
+ def test_default_wx
275
+ Wx::ConfigBase.set(nil) # reset global instance
276
+ cfg = Wx::ConfigBase.get # forced auto creation of default config
277
+
278
+ assert_kind_of(Wx::ConfigWx, cfg)
279
+
280
+ cfg.replace(DEMO_CONFIG)
281
+
282
+ run_config_tests(cfg)
283
+ run_auto_accessor_tests(cfg)
284
+ run_env_var_tests(cfg)
285
+
286
+ assert_true(cfg.clear) # cleanup
287
+ end
288
+
289
+ end
290
+
126
291
  def test_html_help
127
- cfg = Wx::ConfigBase.create
292
+ cfg = Wx::ConfigBase.create(true, use_hash_config: true)
128
293
 
129
294
  assert_true(cfg.to_h.empty?)
130
295
 
@@ -0,0 +1,142 @@
1
+ # Copyright (c) 2023 M.J.N. Corino, The Netherlands
2
+ #
3
+ # This software is released under the MIT license.
4
+
5
+ require_relative './lib/wxframe_runner'
6
+
7
+ class TopLevelPersistenceTests < WxRuby::Test::GUITests
8
+
9
+ PERSIST_ROOT = 'Persistent_Options'
10
+
11
+ def run_frame_props_tests
12
+ Wx.persistent_register_and_restore(frame_win, 'TestFrame')
13
+
14
+ frame_win.size = [450, 350]
15
+ frame_win.position = [100, 150]
16
+
17
+ Wx::PersistenceManager.get.save_and_unregister(frame_win)
18
+
19
+ cfg = Wx::ConfigBase.get
20
+ assert_kind_of(Wx::ConfigBase, cfg)
21
+ grp = cfg.get(PERSIST_ROOT)
22
+ assert_kind_of(cfg.class::Group, grp)
23
+ grp = grp.get('Window')
24
+ assert_kind_of(cfg.class::Group, grp)
25
+ grp = grp.get('TestFrame')
26
+ assert_kind_of(cfg.class::Group, grp)
27
+
28
+ assert_equal(100, Integer(grp['x']))
29
+ assert_equal(150, Integer(grp['y']))
30
+ assert_equal(450, Integer(grp.w))
31
+ assert_equal(350, Integer(grp.h))
32
+
33
+ grp.x = 110
34
+ grp.y = 140
35
+
36
+ assert_equal(110, Integer(grp['x']))
37
+ assert_equal(140, Integer(grp['y']))
38
+
39
+ Wx.persistent_register_and_restore(frame_win, 'TestFrame')
40
+
41
+ assert_equal(Wx::Point.new(110, 140), frame_win.position)
42
+
43
+ Wx::PersistenceManager.get.unregister(frame_win)
44
+ end
45
+
46
+ def test_frame_props_ruby_config
47
+ # force creation of hash based Wx::Config instance
48
+ Wx::ConfigBase.create(true, use_hash_config: true)
49
+
50
+ run_frame_props_tests
51
+
52
+ Wx::ConfigBase.get.clear
53
+ end
54
+
55
+ # default registry based config does not seem to do well in CI build env
56
+ unless is_ci_build? && Wx::PLATFORM == 'WXMSW'
57
+
58
+ def test_frame_props_default_config
59
+ # force creation of default C++ config instance
60
+ Wx::ConfigBase.create(true)
61
+
62
+ run_frame_props_tests
63
+
64
+ Wx::ConfigBase.get.clear
65
+ end
66
+
67
+ end
68
+
69
+ class PersistentButton < Wx::PersistentWindowBase
70
+
71
+ def get_kind
72
+ 'Button'
73
+ end
74
+
75
+ def save
76
+ save_value('w', get.size.width)
77
+ save_value('h', get.size.height)
78
+ save_value('label', get.label)
79
+ save_value('my_custom_value', get.my_custom_value)
80
+ end
81
+
82
+ def restore
83
+ get.size = [Integer(restore_value('w')), Integer(restore_value('h'))]
84
+ get.label = restore_value('label')
85
+ get.my_custom_value = Float(restore_value('my_custom_value'))
86
+ true
87
+ end
88
+
89
+ end
90
+
91
+ class MyButton < Wx::Button
92
+
93
+ def initialize(parent=nil, name)
94
+ super(parent, label: '', name: name)
95
+ @my_custom_value = ''
96
+ end
97
+
98
+ attr_accessor :my_custom_value
99
+
100
+ def create_persistent_object
101
+ PersistentButton.new(self)
102
+ end
103
+
104
+ end
105
+
106
+ def test_custom_persistent_object
107
+ # force creation of hash based Wx::Config instance
108
+ Wx::ConfigBase.create(true, use_hash_config: true)
109
+
110
+ assert_false(Wx::ConfigBase.get.has_group?(PERSIST_ROOT))
111
+
112
+ btn = MyButton.new(frame_win, 'AButton')
113
+ btn.label = 'Hello world'
114
+ btn.my_custom_value = 3.14
115
+
116
+ Wx::PersistenceManager.get.register(btn)
117
+
118
+ assert_false(Wx::ConfigBase.get.has_group?(PERSIST_ROOT))
119
+
120
+ # destroying window should save and unregister
121
+ btn.destroy
122
+ btn = nil
123
+
124
+
125
+ assert_true(Wx::ConfigBase.get.has_group?(PERSIST_ROOT))
126
+
127
+ cfg = Wx::ConfigBase.get[PERSIST_ROOT]['Button']['AButton']
128
+ assert_true(cfg.has_entry?('w'))
129
+ assert_true(cfg.has_entry?('h'))
130
+ assert_true(cfg.has_entry?('label'))
131
+ assert_true(cfg.has_entry?('my_custom_value'))
132
+
133
+
134
+ btn = MyButton.new(frame_win, 'AButton')
135
+
136
+ Wx::PersistenceManager.get.register_and_restore(btn)
137
+
138
+ assert_equal('Hello world', btn.label)
139
+ assert_equal(3.14, btn.my_custom_value)
140
+ end
141
+
142
+ end