wxruby3 0.9.4 → 0.9.7

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/INSTALL.md +315 -78
  3. data/README.md +32 -21
  4. data/ext/wxruby3/include/wxruby-ComboPopup.h +777 -0
  5. data/lib/wx/core/combo_ctrl.rb +171 -0
  6. data/lib/wx/core/ext.rb +22 -3
  7. data/lib/wx/doc/comboctrl.rb +128 -3
  8. data/lib/wx/doc/owner_drawn_combobox.rb +5 -1
  9. data/lib/wx/version.rb +1 -1
  10. data/lib/wx/wxruby/base.rb +6 -4
  11. data/lib/wx/wxruby/cmd/sampler.rb +39 -29
  12. data/lib/wx/wxruby/cmd/setup.rb +122 -0
  13. data/lib/wx/wxruby/cmd/test.rb +56 -6
  14. data/rakefile +14 -0
  15. data/rakelib/bin.rake +48 -0
  16. data/rakelib/bin.rb +62 -0
  17. data/rakelib/build.rb +11 -7
  18. data/rakelib/config.rake +3 -1
  19. data/rakelib/configure.rb +28 -8
  20. data/rakelib/doc.rake +3 -1
  21. data/rakelib/gem.rake +169 -0
  22. data/rakelib/gem.rb +82 -0
  23. data/rakelib/install.rb +2 -0
  24. data/rakelib/lib/config/linux.rb +24 -2
  25. data/rakelib/lib/config/macosx.rb +16 -0
  26. data/rakelib/lib/config/mingw.rb +133 -9
  27. data/rakelib/lib/config/pkgman/arch.rb +53 -0
  28. data/rakelib/lib/config/pkgman/base.rb +169 -0
  29. data/rakelib/lib/config/pkgman/debian.rb +66 -0
  30. data/rakelib/lib/config/pkgman/macosx.rb +183 -0
  31. data/rakelib/lib/config/pkgman/rhel.rb +54 -0
  32. data/rakelib/lib/config/pkgman/suse.rb +54 -0
  33. data/rakelib/lib/config/unixish.rb +36 -19
  34. data/rakelib/lib/config.rb +254 -61
  35. data/rakelib/lib/core/include/funcall.inc +2 -1
  36. data/rakelib/lib/core/package.rb +47 -49
  37. data/rakelib/lib/director/comboctrl.rb +104 -3
  38. data/rakelib/lib/director/defs.rb +1 -3
  39. data/rakelib/lib/director/gdicommon.rb +5 -0
  40. data/rakelib/lib/director/menu_item.rb +1 -1
  41. data/rakelib/lib/director/num_validator.rb +5 -7
  42. data/rakelib/lib/director/owner_drawn_combobox.rb +1 -0
  43. data/rakelib/lib/director/persistent_window.rb +2 -2
  44. data/rakelib/lib/director/pgeditor.rb +1 -1
  45. data/rakelib/lib/director/pgproperties.rb +3 -3
  46. data/rakelib/lib/director/pgproperty.rb +5 -1
  47. data/rakelib/lib/director/richtext_style_listbox.rb +5 -0
  48. data/rakelib/lib/director/sizer.rb +1 -1
  49. data/rakelib/lib/director/window.rb +4 -0
  50. data/rakelib/lib/extractor/module.rb +15 -0
  51. data/rakelib/lib/generate/doc/combo_ctrl.yaml +135 -0
  52. data/rakelib/lib/generate/doc/file_dialog_customize_hook.yaml +62 -0
  53. data/rakelib/lib/generate/doc/file_system.yaml +28 -0
  54. data/rakelib/lib/generate/doc.rb +29 -14
  55. data/rakelib/lib/generate/interface.rb +16 -6
  56. data/rakelib/lib/swig_runner.rb +18 -15
  57. data/rakelib/lib/typemap/combo_popup.rb +42 -0
  58. data/rakelib/prepost.rake +9 -4
  59. data/rakelib/yard/templates/default/fulldoc/html/css/wxruby3.css +14 -0
  60. data/rakelib/yard/templates/default/fulldoc/html/setup.rb +5 -5
  61. data/rakelib/yard/yard/relative_markdown_links.rb +7 -1
  62. data/tests/test_combo_ctrl.rb +196 -0
  63. metadata +28 -17
  64. data/ext/mkrf_conf_srcgem.rb +0 -67
  65. data/rakelib/run.rake +0 -52
@@ -26,22 +26,30 @@ module WXRuby3
26
26
  WX_GLOBAL_CONSTANTS=false
27
27
  require 'wx'
28
28
  def handle_module(mod, table)
29
+ Wx.delayed_constants_for(mod).each do |key, delayed_const|
30
+ table[key.sym.to_s] = { type: true, value: delayed_const.to_s }
31
+ end
29
32
  mod.constants.each do |c|
30
33
  a_const = mod.const_get(c)
31
34
  if (::Module === a_const || ::Class === a_const) && a_const.name.start_with?('Wx::') # Wx:: Package submodule or Class (possibly Enum)
32
35
  handle_module(a_const, table[c.to_s] = {})
33
36
  elsif Wx::Enum === a_const
34
- table[c.to_s] = { type: a_const.class.name.split('::').last, value: "\#{a_const.class}.new(\#{a_const.to_i})" }
35
- elsif !(::Hash === a_const || ::Array === a_const)
36
- table[c.to_s] = { type: a_const.class.name.split('::').last, value: a_const } unless c == :THE_APP
37
+ table[c.to_s] = { type: true, value: "\#{a_const.class}.new(\#{a_const.to_i})" }
38
+ elsif !(::Hash === a_const || ::Array === a_const)
39
+ case a_const
40
+ when Wx::Size
41
+ table[c.to_s] = { type: true, value: "Wx::Size.new(\#{a_const.width}, \#{a_const.height})" }
42
+ when Wx::Point
43
+ table[c.to_s] = { type: true, value: "Wx::Point.new(\#{a_const.x}, \#{a_const.y})" }
44
+ else
45
+ table[c.to_s] = { type: true, value: a_const } unless c == :THE_APP
46
+ end
37
47
  end
38
48
  end
39
49
  end
40
- Wx::App.run do
41
- table = { 'Wx' => {}}
42
- handle_module(Wx, table['Wx'])
43
- STDOUT.puts JSON.dump(table)
44
- end
50
+ table = { 'Wx' => {}}
51
+ handle_module(Wx, table['Wx'])
52
+ STDOUT.puts JSON.dump(table)
45
53
  __SCRIPT
46
54
  STDERR.puts "* executing constants collection script:\n#{script}" if Director.trace?
47
55
  begin
@@ -693,15 +701,22 @@ module WXRuby3
693
701
  # at least 2 newlines to make Yard skip/forget the header comment
694
702
  fdoc.puts
695
703
  fdoc.puts
696
- fdoc.puts "module #{package.fullname}"
697
- fdoc.puts
698
- fdoc.indent do
704
+ mod_indent = 0
705
+ package.all_modules.each do |modnm|
706
+ fdoc.iputs("module #{package.fullname}", mod_indent)
707
+ fdoc.puts
708
+ mod_indent += 1
709
+ end
710
+ fdoc.indent(mod_indent) do
699
711
  gen_constants_doc(fdoc)
700
712
  gen_functions_doc(fdoc) unless no_gen?(:functions)
701
713
  gen_class_doc(fdoc) unless no_gen?(:classes)
702
714
  end
703
- fdoc.puts
704
- fdoc.puts 'end'
715
+ package.all_modules.each do |_|
716
+ fdoc.puts
717
+ fdoc.iputs('end', mod_indent)
718
+ mod_indent -= 1
719
+ end
705
720
  end
706
721
  end
707
722
 
@@ -742,7 +757,7 @@ module WXRuby3
742
757
  end
743
758
 
744
759
  def gen_constant_value(val)
745
- if ::String === val && /\A(#<(.*)>|[\w:]+\.new\(.*\))\Z/ =~ val
760
+ if ::String === val && /\A(#<(.*)>|[\w:]+\.\w+\(.*\))\Z/ =~ val
746
761
  if $2
747
762
  valstr = $2
748
763
  if /\Awx/ =~ valstr
@@ -547,10 +547,18 @@ module WXRuby3
547
547
  if Extractor::EnumDef === item && !item.ignored && !item.items.all? {|e| e.ignored }
548
548
  fout.puts
549
549
  fout.puts "// from enum #{item.is_anonymous ? '' : item.name}"
550
- fout.puts "enum #{item.name};" unless item.is_anonymous
551
- item.items.each do |e|
552
- unless e.ignored
553
- fout.puts "%constant int #{e.name} = #{e.fqn};"
550
+ if item.is_anonymous
551
+ item.items.each do |e|
552
+ unless e.ignored
553
+ fout.puts "%constant int #{e.name} = #{e.fqn};"
554
+ end
555
+ end
556
+ else
557
+ fout.puts "enum #{item.name};"
558
+ item.items.each do |e|
559
+ unless e.ignored
560
+ fout.puts "%constant int #{item.name}_#{e.name} = #{e.fqn};"
561
+ end
554
562
  end
555
563
  end
556
564
  end
@@ -595,7 +603,8 @@ module WXRuby3
595
603
  elsif item.value =~ /wx(Colour|Font)(\(.*\))/
596
604
  frbext = init_rb_ext_file unless frbext
597
605
  frbext.indent do
598
- frbext.puts "Wx.add_delayed_constant(self, :#{rb_constant_name(item.name)}) { Wx::#{$1}.new#{$2} }"
606
+ code = "Wx::#{$1}.new#{$2}"
607
+ frbext.puts "Wx.add_delayed_constant(self, :#{rb_constant_name(item.name)}, '#{code}') { #{code} }"
599
608
  end
600
609
  frbext.puts
601
610
  elsif item.value =~ /wxSystemSettings::(\w+)\((.*)\)/
@@ -603,7 +612,8 @@ module WXRuby3
603
612
  setting_mtd = $1
604
613
  args = $2.split(',').collect {|a| rb_constant_value(a) }.join(', ')
605
614
  frbext.indent do
606
- frbext.puts "Wx.add_delayed_constant(self, :#{rb_constant_name(item.name)}) { Wx::SystemSettings.#{rb_method_name(setting_mtd)}(#{args}) }"
615
+ code = "Wx::SystemSettings.#{rb_method_name(setting_mtd)}(#{args})"
616
+ frbext.puts "Wx.add_delayed_constant(self, :#{rb_constant_name(item.name)}, '#{code}') { #{code} }"
607
617
  end
608
618
  frbext.puts
609
619
  else
@@ -43,24 +43,23 @@ module WXRuby3
43
43
  @swig_version
44
44
  end
45
45
 
46
-
47
46
  def check_swig
48
47
  begin
49
48
  @swig_version = `#{WXRuby3::Config.get_config('swig')} -version`[/\d+\.\d+\.\d+/]
50
49
  rescue Exception
51
- STDERR.puts "ERROR: Could not run SWIG (#{WXRuby3::Config.get_config('swig')})"
50
+ $stderr.puts "ERROR: Could not run SWIG (#{WXRuby3::Config.get_config('swig')})"
52
51
  exit(1)
53
52
  end
54
53
 
55
- # Very old versions put --version on STDERR, not STDOUT
54
+ # Very old versions put --version on $stderr, not $stdout
56
55
  unless @swig_version
57
- STDERR.puts "Could not get version info from SWIG; " +
56
+ $stderr.puts "Could not get version info from SWIG; " +
58
57
  "is a very old version installed?.\n"
59
58
  exit(1)
60
59
  end
61
60
 
62
61
  if @swig_version < SWIG_MINIMUM_VERSION
63
- STDERR.puts "SWIG version #{@swig_version} is installed, " +
62
+ $stderr.puts "SWIG version #{@swig_version} is installed, " +
64
63
  "minimum version required is #{SWIG_MINIMUM_VERSION}.\n"
65
64
  exit(1)
66
65
  end
@@ -72,11 +71,12 @@ module WXRuby3
72
71
  check_swig unless swig_state
73
72
  inc_paths = "-I#{config.wxruby_dir} -I#{config.swig_dir}/custom"
74
73
  inc_paths << " -I#{config.swig_dir}/custom/swig#{swig_major}"
75
- sh "#{config.get_config('swig')} #{config.wx_cppflags.join(' ')} " +
76
- "#{config.extra_cppflags.join(' ')} #{config.verbose_flag} #{inc_paths} " +
77
- #"-w401 -w801 -w515 -c++ -ruby " +
78
- "-w801 -c++ -ruby " +
79
- "-o #{target} #{source}"
74
+ WXRuby3.config.sh "#{config.get_config('swig')} #{config.wx_cppflags.join(' ')} " +
75
+ "#{config.extra_cppflags.join(' ')} #{config.verbose_flag} #{inc_paths} " +
76
+ #"-w401 -w801 -w515 -c++ -ruby " +
77
+ "-w801 -c++ -ruby " +
78
+ "-o #{target} #{source}",
79
+ fail_on_error: true
80
80
  end
81
81
 
82
82
  end
@@ -205,7 +205,7 @@ module WXRuby3
205
205
  end
206
206
 
207
207
  def self.run(pid, director, target)
208
- puts "Processor.#{pid}: #{target}"
208
+ Config.instance.log_progress("Processor.#{pid}: #{target}")
209
209
  const_get(camelize(pid.to_s)).new(director, target).run
210
210
  end
211
211
 
@@ -262,7 +262,7 @@ module WXRuby3
262
262
  def_items.each do |item|
263
263
  case item
264
264
  when Extractor::EnumDef
265
- item.items.each { |e| enumerators[rb_wx_name(e.name)] = item } if item.is_type
265
+ item.items.each { |e| enumerators["#{rb_wx_name(item.name)}_#{e.name}"] = item } if item.is_type
266
266
  when Extractor::ClassDef
267
267
  item.items.select { |itm| Extractor::EnumDef === itm }.each do |enum|
268
268
  enum.items.each { |e| enumerators[rb_wx_name(e.name)] = enum } if enum.is_type
@@ -334,6 +334,7 @@ module WXRuby3
334
334
  fix_enum = true
335
335
  enum_item = enum_table[md[2]]
336
336
  enum_name = rb_wx_name(enum_item.name)
337
+ enumerator_name = rb_wx_name(md[2].sub(/\A#{enum_name}_/, ''))
337
338
  enum_id = enum_item.scope.empty? ? enum_name : "#{rb_wx_name(enum_item.scope)}::#{enum_name}"
338
339
  enum_var = enum_id.gsub('::', '_')
339
340
  line = [
@@ -343,7 +344,7 @@ module WXRuby3
343
344
  # add enum class constant to current module (use unscoped name)
344
345
  " rb_define_const(#{md[1]}, \"#{enum_name}\", cWx#{enum_var}); // Inserted by fixmodule.rb",
345
346
  # create enumerator value const under new enum class
346
- " wxRuby_AddEnumValue(cWx#{enum_var}, \"#{md[2]}\"#{md[3]} // Updated by fixmodule.rb"
347
+ " wxRuby_AddEnumValue(cWx#{enum_var}, \"#{enumerator_name}\"#{md[3]} // Updated by fixmodule.rb"
347
348
  ].join("\n")
348
349
  end
349
350
  else
@@ -352,13 +353,15 @@ module WXRuby3
352
353
  # of the same enum?
353
354
  if enum_item && enum_table[md[2]] == enum_item
354
355
  enum_name = rb_wx_name(enum_item.name)
356
+ enumerator_name = rb_wx_name(md[2].sub(/\A#{enum_name}_/, ''))
355
357
  enum_id = enum_item.scope.empty? ? enum_name : "#{rb_wx_name(enum_item.scope)}::#{enum_name}"
356
358
  enum_var = enum_id.gsub('::', '_')
357
359
  # create enumerator value const under new enum class
358
- line = " wxRuby_AddEnumValue(cWx#{enum_var}, \"#{md[2]}\"#{md[3]} // Updated by fixmodule.rb"
360
+ line = " wxRuby_AddEnumValue(cWx#{enum_var}, \"#{enumerator_name}\"#{md[3]} // Updated by fixmodule.rb"
359
361
  else # we found the start of another enum
360
362
  enum_item = enum_table[md[2]]
361
363
  enum_name = rb_wx_name(enum_item.name)
364
+ enumerator_name = rb_wx_name(md[2].sub(/\A#{enum_name}_/, ''))
362
365
  enum_id = enum_item.scope.empty? ? enum_name : "#{rb_wx_name(enum_item.scope)}::#{enum_name}"
363
366
  enum_var = enum_id.gsub('::', '_')
364
367
  line = [
@@ -368,7 +371,7 @@ module WXRuby3
368
371
  # add enum class constant to current module (use unscoped name)
369
372
  " rb_define_const(#{md[1]}, \"#{enum_name}\", cWx#{enum_var}); // Inserted by fixmodule.rb",
370
373
  # create enumerator value const under new enum class
371
- " wxRuby_AddEnumValue(cWx#{enum_var}, \"#{md[2]}\"#{md[3]} // Updated by fixmodule.rb"
374
+ " wxRuby_AddEnumValue(cWx#{enum_var}, \"#{enumerator_name}\"#{md[3]} // Updated by fixmodule.rb"
372
375
  ].join("\n")
373
376
  end
374
377
  else # end of enum def
@@ -0,0 +1,42 @@
1
+ # Copyright (c) 2023 M.J.N. Corino, The Netherlands
2
+ #
3
+ # This software is released under the MIT license.
4
+
5
+ ###
6
+ # wxRuby3 wxComboPopup typemap definition
7
+ ###
8
+
9
+ require_relative '../core/mapping'
10
+
11
+ module WXRuby3
12
+
13
+ module Typemap
14
+
15
+ module ComboPopup
16
+
17
+ include Typemap::Module
18
+
19
+ define do
20
+
21
+ # for DoSetPopupControl
22
+ map 'wxComboPopup* popup' => 'Wx::ComboPopup,nil' do
23
+
24
+ add_header_code <<~__CODE
25
+ #include <wx/combo.h>
26
+
27
+ WXRUBY_EXPORT wxComboPopup* wxRuby_ComboPopupFromRuby(VALUE popup);
28
+ WXRUBY_EXPORT VALUE wxRuby_ComboPopupToRuby(wxComboPopup* popup);
29
+ __CODE
30
+
31
+ map_in code: '$1 = wxRuby_ComboPopupFromRuby($input);'
32
+
33
+ map_directorin code: '$input = wxRuby_ComboPopupToRuby($1);'
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+
42
+ end
data/rakelib/prepost.rake CHANGED
@@ -23,16 +23,21 @@ namespace 'wxruby' do
23
23
 
24
24
  namespace 'post' do
25
25
 
26
- task :srcgem => %w[gem:wxwin gem:install wxruby:doc] do
26
+ task :srcgem => %w[gem:wxwin gem:install] do
27
+ $stdout.print "Generating wxRuby3 reference documentation..." if WXRuby3.config.run_silent?
28
+ Rake::Task['wxruby:doc'].invoke
29
+ $stdout.puts 'done!' if WXRuby3.config.run_silent?
27
30
  # cleanup
28
- rm_rf('rakelib')
29
- rm_rf('ext/wxruby3')
30
- rm_rf('ext/wxWidgets') if File.exist?('ext/wxWidgets')
31
+ rm_rf('rakelib', verbose: !WXRuby3.config.run_silent?)
32
+ rm_rf('ext/wxruby3', verbose: !WXRuby3.config.run_silent?)
33
+ WXRuby3.config.cleanup_bootstrap
34
+ File.open(File.join(WXRuby3::Config.wxruby_root, 'ext', 'wxruby.setup.done'), 'w') { |f| f << '1' }
31
35
  end
32
36
 
33
37
  task :bingem => 'gem:install' do
34
38
  # cleanup
35
39
  rm_rf('rakelib')
40
+ File.open(File.join(WXRuby3::Config.wxruby_root, 'ext', 'wxruby.setup.done'), 'w') { |f| f << '1' }
36
41
  end
37
42
 
38
43
  namespace 'gem' do
@@ -77,3 +77,17 @@ div.wxrb-logo table td {
77
77
  font-weight: bold;
78
78
  font-size: 0.9em;
79
79
  }
80
+
81
+ #filecontents {
82
+ margin-right: 340px;
83
+ }
84
+
85
+ #filecontents blockquote {
86
+ border-left: .5em solid #e0e0e0;
87
+ margin: 10px;
88
+ padding-left: 10px;
89
+ }
90
+
91
+ #toc {
92
+ position: fixed;
93
+ }
@@ -1,19 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  def init
4
- # It seems YARD messes things up so that a number of classes are not properly
4
+ # It seems YARD messes things up so that a lot of classes, modules and constants are not properly
5
5
  # registered in their enclosing namespaces.
6
6
  # This hack makes sure that if that is the case we fix that here.
7
- all_classes = Registry.all(:class)
8
- all_classes.each do |c|
7
+ all_objects = Registry.all(:class, :constant, :module)
8
+ all_objects.each do |c|
9
9
  if (ns = c.namespace)
10
10
  unless ns.children.any? { |nsc| nsc.path == c.path }
11
- ns.children << c # class missing from child list of enclosing namespace -> add here
11
+ ns.children << c # class/module/constant missing from child list of enclosing namespace -> add here
12
12
  end
13
13
  end
14
14
  if (ns = Registry[c.namespace.path])
15
15
  unless ns.children.any? { |nsc| nsc.path == c.path }
16
- ns.children << c # class missing from child list of enclosing namespace -> add here
16
+ ns.children << c # class/module/constant missing from child list of enclosing namespace -> add here
17
17
  end
18
18
  end
19
19
  end
@@ -31,12 +31,18 @@ module YARD # rubocop:disable Style/Documentation
31
31
  if fnames.include?(href.path)
32
32
  link.replace "{file:#{href} #{link.inner_html}}"
33
33
  elsif href.path.end_with?('_md.html') && (fname = fnames.find {|fnm| fnm.end_with?(href.path.sub(/_md.html\Z/, '.md')) })
34
- link.replace "{file:#{fname} #{link.inner_html}}"
34
+ link.replace "{file:#{fname}#{href.fragment ? "##{fragment_to_yard(href.fragment)}" : ''} #{link.inner_html}}"
35
35
  end
36
36
  end
37
37
  end
38
38
  super(html.to_s)
39
39
  end
40
+
41
+ # this does not work with mixed case labels but is good enough for us
42
+ def fragment_to_yard(s)
43
+ s.start_with?('label-') ? s : "label-#{s.gsub('-', '+').capitalize}"
44
+ end
45
+
40
46
  end
41
47
 
42
48
  Templates::Template.extra_includes << RelativeMarkdownLinks
@@ -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