wxruby3 0.9.3 → 0.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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