glimmer-dsl-swt 4.18.7.3 → 4.19.0.0

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -0
  3. data/README.md +126 -20
  4. data/RUBY_VERSION +1 -1
  5. data/VERSION +1 -1
  6. data/bin/girb +10 -9
  7. data/bin/girb_runner.rb +8 -3
  8. data/bin/glimmer +10 -1
  9. data/bin/glimmer-setup +58 -0
  10. data/bin/glimmer_runner.rb +4 -0
  11. data/docs/reference/GLIMMER_COMMAND.md +98 -32
  12. data/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md +9 -7
  13. data/docs/reference/GLIMMER_PACKAGING_AND_DISTRIBUTION.md +7 -1
  14. data/docs/reference/GLIMMER_SAMPLES.md +25 -0
  15. data/glimmer-dsl-swt.gemspec +0 -0
  16. data/lib/glimmer-dsl-swt.rb +8 -0
  17. data/lib/glimmer/data_binding/widget_binding.rb +1 -1
  18. data/lib/glimmer/dsl/swt/c_tab_item_expression.rb +58 -0
  19. data/lib/glimmer/dsl/swt/combo_selection_data_binding_expression.rb +2 -1
  20. data/lib/glimmer/dsl/swt/tab_item_expression.rb +7 -3
  21. data/lib/glimmer/dsl/swt/widget_expression.rb +1 -1
  22. data/lib/glimmer/launcher.rb +27 -51
  23. data/lib/glimmer/rake_task.rb +37 -7
  24. data/lib/glimmer/rake_task/list.rb +8 -0
  25. data/lib/glimmer/rake_task/package.rb +7 -2
  26. data/lib/glimmer/rake_task/scaffold.rb +283 -207
  27. data/lib/glimmer/swt/c_tab_item_proxy.rb +53 -0
  28. data/lib/glimmer/swt/custom/code_text.rb +19 -3
  29. data/lib/glimmer/swt/custom/shape.rb +22 -4
  30. data/lib/glimmer/swt/image_proxy.rb +5 -1
  31. data/lib/glimmer/swt/sash_form_proxy.rb +6 -0
  32. data/lib/glimmer/swt/shell_proxy.rb +3 -1
  33. data/lib/glimmer/swt/tab_folder_proxy.rb +1 -0
  34. data/lib/glimmer/swt/tab_item_proxy.rb +17 -18
  35. data/lib/glimmer/swt/widget_proxy.rb +9 -0
  36. data/samples/elaborate/mandelbrot_fractal.rb +1 -1
  37. data/samples/elaborate/tetris.rb +1 -4
  38. data/samples/elaborate/tetris/view/bevel.rb +4 -1
  39. data/samples/elaborate/tetris/view/block.rb +1 -3
  40. data/samples/hello/hello_c_combo.rb +68 -0
  41. data/samples/hello/hello_c_tab.rb +271 -0
  42. data/samples/hello/hello_code_text.rb +141 -73
  43. data/samples/hello/hello_tab.rb +2 -0
  44. data/vendor/swt/linux/swt.jar +0 -0
  45. data/vendor/swt/mac/swt.jar +0 -0
  46. data/vendor/swt/windows/swt.jar +0 -0
  47. metadata +45 -15
@@ -0,0 +1,53 @@
1
+ # Copyright (c) 2007-2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer/swt/widget_proxy'
23
+ require 'glimmer/swt/tab_item_proxy'
24
+
25
+ module Glimmer
26
+ module SWT
27
+ # Proxy for org.eclipse.swt.custom.CTabItem
28
+ #
29
+ # Functions differently from other widget proxies.
30
+ #
31
+ # Glimmer instantiates an SWT Composite alongside the SWT TabItem
32
+ # and returns it for `#swt_widget` to allow adding widgets into it.
33
+ #
34
+ # In order to get the SWT TabItem object, one must call `#swt_tab_item`.
35
+ #
36
+ # Behind the scenes, this creates a tab item widget proxy separately from a composite that
37
+ # is set as the control of the tab item and `#swt_widget`.
38
+ #
39
+ # In order to retrieve the tab item widget proxy, one must call `#widget_proxy`
40
+ #
41
+ # Follows the Proxy Design Pattern
42
+ class CTabItemProxy < TabItemProxy
43
+ ATTRIBUTES = TabItemProxy::ATTRIBUTES + %w[foreground selection_foreground show_close font]
44
+
45
+ def attributes
46
+ ATTRIBUTES
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+
53
+ end
@@ -64,14 +64,30 @@ module Glimmer
64
64
  respond_to?(method_name)
65
65
  end
66
66
 
67
+ def can_add_observer?(attribute_name)
68
+ @styled_text_proxy&.can_add_observer?(attribute_name) || super
69
+ end
70
+
71
+ def add_observer(observer, attribute_name)
72
+ if @styled_text_proxy&.can_add_observer?(attribute_name)
73
+ @styled_text_proxy.add_observer(observer, attribute_name)
74
+ else
75
+ super
76
+ end
77
+ end
78
+
67
79
  def can_handle_observation_request?(observation_request)
68
- @styled_text_proxy.can_handle_observation_request?(observation_request)
80
+ @styled_text_proxy&.can_handle_observation_request?(observation_request) || super
69
81
  rescue
70
82
  super
71
83
  end
72
-
84
+
73
85
  def handle_observation_request(observation_request, &block)
74
- @styled_text_proxy.handle_observation_request(observation_request, &block)
86
+ if @styled_text_proxy&.can_handle_observation_request?(observation_request)
87
+ @styled_text_proxy.handle_observation_request(observation_request, &block)
88
+ else
89
+ super
90
+ end
75
91
  rescue
76
92
  super
77
93
  end
@@ -94,8 +94,10 @@ module Glimmer
94
94
 
95
95
  def arg_options(args, extract: false)
96
96
  arg_options_method = extract ? :pop : :last
97
- options = args.send(arg_options_method) if args.last.is_a?(Hash)
98
- options.nil? ? {} : options.symbolize_keys
97
+ options = args.send(arg_options_method).symbolize_keys if args.last.is_a?(Hash)
98
+ # normalize :filled option as an alias to :fill
99
+ # options[:fill] = options.delete(:filled) if options&.keys&.include?(:filled)
100
+ options.nil? ? {} : options
99
101
  end
100
102
 
101
103
  def method_name(keyword, method_arg_options)
@@ -400,7 +402,7 @@ module Glimmer
400
402
  def amend_method_name_options_based_on_properties!
401
403
  @original_method_name = @method_name
402
404
  return if @name == 'point'
403
- if @name != 'text' && @name != 'string' && has_some_background? && !has_some_foreground?
405
+ if (@name != 'text' && @name != 'string' && has_some_background? && !has_some_foreground?) || (@name == 'path' && has_some_background?)
404
406
  @options[:fill] = true
405
407
  elsif !has_some_background? && has_some_foreground?
406
408
  @options[:fill] = false
@@ -474,7 +476,9 @@ module Glimmer
474
476
  # TODO consider this optimization of preconverting args (removing conversion from other methods) to reject equal args
475
477
  args = apply_property_arg_conversions(ruby_attribute_getter_name, args)
476
478
  return if @properties[ruby_attribute_getter_name] == args
479
+ new_property = !@properties.keys.include?(ruby_attribute_getter_name)
477
480
  @properties[ruby_attribute_getter_name] = args
481
+ amend_method_name_options_based_on_properties! if @content_added && new_property
478
482
  property_change = true
479
483
  end
480
484
  if @content_added && perform_redraw && !drawable.is_disposed
@@ -568,7 +572,7 @@ module Glimmer
568
572
  end
569
573
 
570
574
  def dispose(dispose_images: true, dispose_patterns: true, redraw: true)
571
- shapes.each { |shape| shape.is_a?(Shape::Path) && shape.dispose }
575
+ shapes.each { |shape| shape.is_a?(Shape::Path) && shape.dispose } # TODO look into why I'm only disposing paths
572
576
  if dispose_patterns
573
577
  @background_pattern&.dispose
574
578
  @background_pattern = nil
@@ -583,6 +587,20 @@ module Glimmer
583
587
  drawable.redraw if redraw && !drawable.is_a?(ImageProxy)
584
588
  end
585
589
 
590
+ # clear all shapes
591
+ # indicate whether to dispose images, dispose patterns, and redraw after clearing shapes.
592
+ # redraw can be `:all` or `true` to mean redraw after all shapes are disposed, `:each` to mean redraw after each shape is disposed, or `false` to avoid redraw altogether
593
+ def clear_shapes(dispose_images: true, dispose_patterns: true, redraw: :all)
594
+ if redraw == true || redraw == :all
595
+ shapes.dup.each {|shape| shape.dispose(dispose_images: dispose_images, dispose_patterns: dispose_patterns, redraw: false) }
596
+ drawable.redraw if redraw && !drawable.is_a?(ImageProxy)
597
+ elsif redraw == :each
598
+ shapes.dup.each {|shape| shape.dispose(dispose_images: dispose_images, dispose_patterns: dispose_patterns, redraw: true) }
599
+ else
600
+ shapes.dup.each {|shape| shape.dispose(dispose_images: dispose_images, dispose_patterns: dispose_patterns, redraw: false) }
601
+ end
602
+ end
603
+
586
604
  # Indicate if this is a container shape (meaning a shape bag that is just there to contain nested shapes, but doesn't render anything of its own)
587
605
  def container?
588
606
  @name == 'shape'
@@ -57,7 +57,7 @@ module Glimmer
57
57
  include_package 'org.eclipse.swt.widgets'
58
58
  include_package 'org.eclipse.swt.graphics'
59
59
 
60
- attr_reader :file_path, :jar_file_path, :image_data, :swt_image
60
+ attr_reader :file_path, :jar_file_path, :image_data, :swt_image, :parent_proxy, :parent
61
61
 
62
62
  # Initializes a proxy for an SWT Image object
63
63
  #
@@ -141,6 +141,10 @@ module Glimmer
141
141
  self
142
142
  end
143
143
 
144
+ def size
145
+ org.eclipse.swt.graphics.Point.new(bounds.width, bounds.height)
146
+ end
147
+
144
148
  def gc
145
149
  @gc ||= reset_gc
146
150
  end
@@ -20,6 +20,7 @@
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
22
  require 'glimmer/swt/widget_proxy'
23
+ require 'glimmer/swt/color_proxy'
23
24
 
24
25
  module Glimmer
25
26
  module SWT
@@ -27,6 +28,11 @@ module Glimmer
27
28
  #
28
29
  # Follows the Proxy Design Pattern
29
30
  class SashFormProxy < WidgetProxy
31
+ def initialize(*args, &block)
32
+ super
33
+ self.background = ColorProxy.new(230, 230, 230).swt_color
34
+ end
35
+
30
36
  def post_add_content
31
37
  self.weights = @weights unless @weights.nil?
32
38
  end
@@ -74,7 +74,9 @@ module Glimmer
74
74
  # TODO make this an option not the default
75
75
  shell_swt_display = Glimmer::SWT::DisplayProxy.instance.swt_display
76
76
  on_swt_show do
77
- @swt_widget.set_size(@display.bounds.width, @display.bounds.height) if fill_screen
77
+ if @filled_screen.nil? && fill_screen # only the first time
78
+ @swt_widget.set_size(@display.bounds.width, @display.bounds.height)
79
+ end
78
80
  Thread.new do
79
81
  sleep(0.25)
80
82
  shell_swt_display.async_exec do
@@ -48,5 +48,6 @@ module Glimmer
48
48
  end
49
49
  end
50
50
  end
51
+ CTabFolderProxy = TabFolderProxy
51
52
  end
52
53
  end
@@ -39,44 +39,40 @@ module Glimmer
39
39
  #
40
40
  # Follows the Proxy Design Pattern
41
41
  class TabItemProxy < WidgetProxy
42
- ATTRIBUTES = %w[text image]
42
+ ATTRIBUTES = %w[text image tool_tip_text]
43
43
  include_package 'org.eclipse.swt.widgets'
44
44
 
45
45
  attr_reader :widget_proxy, :swt_tab_item
46
46
 
47
47
  def initialize(parent, style, &contents)
48
48
  super("composite", parent, style, &contents)
49
- @widget_proxy = SWT::WidgetProxy.new('tab_item', parent, style)
49
+ keyword = self.class.name.split('::').last.underscore.sub('_proxy', '')
50
+ @widget_proxy = SWT::WidgetProxy.new(keyword, parent, style)
50
51
  @swt_tab_item = @widget_proxy.swt_widget
51
52
  @swt_tab_item.control = swt_widget
52
- # parent.pack # TODO auto fix the tab folder to avoid having to do this manually (including maintaining bounds and minimum size of shell)
53
+ end
54
+
55
+ def attributes
56
+ ATTRIBUTES
53
57
  end
54
58
 
55
59
  def has_attribute?(attribute_name, *args)
56
- if ATTRIBUTES.include?(attribute_name.to_s)
57
- true
58
- else
59
- super(attribute_name, *args)
60
- end
60
+ attributes.include?(attribute_name.to_s) || super(attribute_name, *args)
61
61
  end
62
62
 
63
63
  def set_attribute(attribute_name, *args)
64
- attribute_name
65
- if attribute_name.to_s == "text"
66
- text_value = args[0]
67
- @swt_tab_item.setText text_value
68
- elsif attribute_name.to_s == "image"
69
- widget_proxy.set_attribute('image', *args)
64
+ attribute_name = attribute_name.to_s
65
+ if attributes.include?(attribute_name)
66
+ widget_proxy.set_attribute(attribute_name, *args)
70
67
  else
71
68
  super(attribute_name, *args)
72
69
  end
73
70
  end
74
71
 
75
72
  def get_attribute(attribute_name)
76
- if attribute_name.to_s == "text"
77
- @swt_tab_item.getText
78
- elsif attribute_name.to_s == "image"
79
- widget_proxy.get_attribute('image')
73
+ attribute_name = attribute_name.to_s
74
+ if attributes.include?(attribute_name)
75
+ widget_proxy.get_attribute(attribute_name)
80
76
  else
81
77
  super(attribute_name)
82
78
  end
@@ -89,6 +85,9 @@ module Glimmer
89
85
  swt_tab_item.dispose
90
86
  end
91
87
  end
88
+
92
89
  end
90
+
93
91
  end
92
+
94
93
  end
@@ -51,6 +51,7 @@ module Glimmer
51
51
  'arrow' => [:arrow],
52
52
  'button' => [:push],
53
53
  'canvas' => ([:double_buffered] unless OS.mac?),
54
+ 'ccombo' => [:border],
54
55
  'checkbox' => [:check],
55
56
  'check' => [:check],
56
57
  'drag_source' => [:drop_copy],
@@ -339,6 +340,13 @@ module Glimmer
339
340
  }
340
341
  end,
341
342
  },
343
+ Java::OrgEclipseSwtCustom::CCombo => {
344
+ :text => lambda do |observer|
345
+ on_modify_text { |modify_event|
346
+ observer.call(@swt_widget.getText)
347
+ }
348
+ end,
349
+ },
342
350
  Java::OrgEclipseSwtWidgets::Table => {
343
351
  :selection => lambda do |observer|
344
352
  on_widget_selected { |selection_event|
@@ -985,6 +993,7 @@ module Glimmer
985
993
  !!value
986
994
  end,
987
995
  foreground: color_converter,
996
+ selection_foreground: color_converter,
988
997
  link_foreground: color_converter,
989
998
  font: lambda do |value|
990
999
  if value.is_a?(Hash) || value.is_a?(FontData)
@@ -56,7 +56,7 @@ class Mandelbrot
56
56
  end
57
57
 
58
58
  def processor_count
59
- @processor_count ||= Concurrent.physical_processor_count
59
+ @processor_count ||= Concurrent.processor_count
60
60
  end
61
61
  end
62
62
 
@@ -156,10 +156,7 @@ class Tetris
156
156
  color = colored ? color(([:white] + Model::Tetromino::LETTER_COLORS.values).sample) : color(:white)
157
157
  x = column * icon_block_size
158
158
  y = row * icon_block_size
159
- rectangle(x, y, icon_block_size, icon_block_size) {
160
- background color
161
- bevel(base_color: color, size: icon_block_size)
162
- }
159
+ bevel(x: x, y: y, base_color: color, size: icon_block_size)
163
160
  }
164
161
  }
165
162
  }
@@ -26,13 +26,16 @@ class Tetris
26
26
  include Glimmer::UI::CustomShape
27
27
 
28
28
  options :base_color, :size, :bevel_pixel_size
29
+ option :x, default: 0
30
+ option :y, default: 0
29
31
 
30
32
  before_body {
31
33
  self.bevel_pixel_size = 0.16*size.to_f if bevel_pixel_size.nil?
32
34
  }
33
35
 
34
36
  body {
35
- shape(0, 0, size, size) {
37
+ rectangle(x, y, size, size) {
38
+ background bind(self, :base_color)
36
39
  polygon(0, 0, size, 0, size - bevel_pixel_size, bevel_pixel_size, bevel_pixel_size, bevel_pixel_size) {
37
40
  background bind(self, :base_color) { |color_value|
38
41
  unless color_value.nil?
@@ -30,9 +30,7 @@ class Tetris
30
30
 
31
31
  body {
32
32
  canvas { |canvas_proxy|
33
- background bind(game_playfield[row][column], :color)
34
-
35
- bevel(base_color: game_playfield[row][column].color, size: block_size) {
33
+ bevel(size: block_size) {
36
34
  base_color bind(game_playfield[row][column], :color)
37
35
  }
38
36
  }
@@ -0,0 +1,68 @@
1
+ # Copyright (c) 2007-2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer-dsl-swt'
23
+
24
+ class HelloCCombo
25
+ class Person
26
+ attr_accessor :country, :country_options
27
+
28
+ def initialize
29
+ self.country_options = ['', 'Canada', 'US', 'Mexico']
30
+ reset_country!
31
+ end
32
+
33
+ def reset_country!
34
+ self.country = 'Canada'
35
+ end
36
+ end
37
+
38
+ include Glimmer::UI::CustomShell
39
+
40
+ before_body {
41
+ @person = Person.new
42
+ }
43
+
44
+ body {
45
+ shell {
46
+ row_layout(:vertical) {
47
+ fill true
48
+ }
49
+
50
+ text 'Hello, CCombo!'
51
+
52
+ c_combo(:read_only) {
53
+ selection bind(@person, :country) # also binds to country_options by convention
54
+ font height: 45 # unlike `combo`, `c_combo` changes height when setting the font height
55
+ }
56
+
57
+ button {
58
+ text 'Reset Selection'
59
+
60
+ on_widget_selected do
61
+ @person.reset_country!
62
+ end
63
+ }
64
+ }
65
+ }
66
+ end
67
+
68
+ HelloCCombo.launch
@@ -0,0 +1,271 @@
1
+ # Copyright (c) 2007-2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer-dsl-swt'
23
+
24
+ class HelloCTab
25
+ include Glimmer::UI::CustomShell
26
+
27
+ body {
28
+ shell {
29
+ row_layout
30
+ text 'Hello, C Tab!'
31
+ minimum_size 200, 200
32
+
33
+ c_tab_folder { # accepts styles: :close, :top, :bottom, :flat, :border, :single, :multi
34
+ layout_data {
35
+ width 1024
36
+ height 200
37
+ }
38
+ c_tab_item(:close) {
39
+ text 'English'
40
+ tool_tip_text 'English Greeting'
41
+ foreground :blue
42
+ selection_foreground :dark_blue
43
+ font name: 'Times New Roman', height: 30, style: [:bold, :italic]
44
+ image image(30, 30) {
45
+ ### building image on the fly with Canvas Shape DSL text shape flag emoji
46
+ text('🇺🇸', 0, 0) {
47
+ font name: 'Times New Roman', height: 30, style: [:bold, :italic]
48
+ }
49
+ }
50
+
51
+ label {
52
+ text 'Hello, World!'
53
+ font name: 'Times New Roman', height: 90, style: [:bold, :italic]
54
+ }
55
+ }
56
+
57
+ c_tab_item(:close) {
58
+ text 'French'
59
+ tool_tip_text 'French Greeting'
60
+ foreground :blue
61
+ selection_foreground :dark_blue
62
+ font name: 'Times New Roman', height: 30, style: [:bold, :italic]
63
+ image image(30, 30) {
64
+ ### building image on the fly with Canvas Shape DSL text shape flag emoji
65
+ text('🇫🇷', 0, 0) {
66
+ font name: 'Times New Roman', height: 30, style: [:bold, :italic]
67
+ }
68
+ }
69
+
70
+ label {
71
+ text 'Bonjour, Univers!'
72
+ font name: 'Times New Roman', height: 90, style: [:bold, :italic]
73
+ }
74
+ }
75
+
76
+ c_tab_item(:close) {
77
+ text 'Spanish'
78
+ tool_tip_text 'Spanish Greeting'
79
+ foreground :blue
80
+ selection_foreground :dark_blue
81
+ font name: 'Times New Roman', height: 30, style: [:bold, :italic]
82
+ image image(30, 30) {
83
+ ### building image on the fly with Canvas Shape DSL text shape flag emoji
84
+ text('🇪🇸', 0, 0) {
85
+ font name: 'Times New Roman', height: 30, style: [:bold, :italic]
86
+ }
87
+ }
88
+
89
+ label {
90
+ text 'Hola, Mundo!'
91
+ font name: 'Times New Roman', height: 90, style: [:bold, :italic]
92
+ }
93
+ }
94
+
95
+ c_tab_item(:close) {
96
+ text 'German'
97
+ tool_tip_text 'German Greeting'
98
+ foreground :blue
99
+ selection_foreground :dark_blue
100
+ font name: 'Times New Roman', height: 30, style: [:bold, :italic]
101
+ image image(30, 30) {
102
+ # building image on the fly with Canvas Shape DSL text shape flag emoji
103
+ text('🇩🇪', 0, 0) {
104
+ font name: 'Times New Roman', height: 30, style: [:bold, :italic]
105
+ }
106
+ }
107
+
108
+ label {
109
+ text 'Hallo, Welt!'
110
+ font name: 'Times New Roman', height: 90, style: [:bold, :italic]
111
+ }
112
+ }
113
+
114
+ c_tab_item(:close) {
115
+ text 'Italian'
116
+ tool_tip_text 'Italian Greeting'
117
+ foreground :blue
118
+ selection_foreground :dark_blue
119
+ font name: 'Times New Roman', height: 30, style: [:bold, :italic]
120
+ image image(30, 30) {
121
+ # building image on the fly with Canvas Shape DSL text shape flag emoji
122
+ text('🇮🇹', 0, 0) {
123
+ font name: 'Times New Roman', height: 30, style: [:bold, :italic]
124
+ }
125
+ }
126
+
127
+ label {
128
+ text 'Ciao, Mondo!'
129
+ font name: 'Times New Roman', height: 90, style: [:bold, :italic]
130
+ }
131
+ }
132
+
133
+ c_tab_item(:close) {
134
+ text 'Dutch'
135
+ tool_tip_text 'Dutch Greeting'
136
+ foreground :blue
137
+ selection_foreground :dark_blue
138
+ font name: 'Times New Roman', height: 30, style: [:bold, :italic]
139
+ image image(30, 30) {
140
+ # building image on the fly with Canvas Shape DSL text shape flag emoji
141
+ text('🇳🇱', 0, 0) {
142
+ font name: 'Times New Roman', height: 30, style: [:bold, :italic]
143
+ }
144
+ }
145
+
146
+ label {
147
+ text 'Hallo, Wereld!'
148
+ font name: 'Times New Roman', height: 90, style: [:bold, :italic]
149
+ }
150
+ }
151
+
152
+ c_tab_item(:close) {
153
+ text 'Portuguese'
154
+ tool_tip_text 'Portuguese Greeting'
155
+ foreground :blue
156
+ selection_foreground :dark_blue
157
+ font name: 'Times New Roman', height: 30, style: [:bold, :italic]
158
+ image image(30, 30) {
159
+ # building image on the fly with Canvas Shape DSL text shape flag emoji
160
+ text('🇧🇷', 0, 0) {
161
+ font name: 'Times New Roman', height: 30, style: [:bold, :italic]
162
+ }
163
+ }
164
+
165
+ label {
166
+ text 'Olá, Mundo!'
167
+ font name: 'Times New Roman', height: 90, style: [:bold, :italic]
168
+ }
169
+ }
170
+
171
+ c_tab_item(:close) {
172
+ text 'Danish'
173
+ tool_tip_text 'Danish Greeting'
174
+ foreground :blue
175
+ selection_foreground :dark_blue
176
+ font name: 'Times New Roman', height: 30, style: [:bold, :italic]
177
+ image image(30, 30) {
178
+ # building image on the fly with Canvas Shape DSL text shape flag emoji
179
+ text('🇩🇰', 0, 0) {
180
+ font name: 'Times New Roman', height: 30, style: [:bold, :italic]
181
+ }
182
+ }
183
+
184
+ label {
185
+ text 'Hej, Verden!'
186
+ font name: 'Times New Roman', height: 90, style: [:bold, :italic]
187
+ }
188
+ }
189
+
190
+ c_tab_item(:close) {
191
+ text 'Finnish'
192
+ tool_tip_text 'Finnish Greeting'
193
+ foreground :blue
194
+ selection_foreground :dark_blue
195
+ font name: 'Times New Roman', height: 30, style: [:bold, :italic]
196
+ image image(30, 30) {
197
+ # building image on the fly with Canvas Shape DSL text shape flag emoji
198
+ text('🇫🇮', 0, 0) {
199
+ font name: 'Times New Roman', height: 30, style: [:bold, :italic]
200
+ }
201
+ }
202
+
203
+ label {
204
+ text 'Hei, Maailma!'
205
+ font name: 'Times New Roman', height: 90, style: [:bold, :italic]
206
+ }
207
+ }
208
+
209
+ c_tab_item(:close) {
210
+ text 'Swedish'
211
+ tool_tip_text 'Swedish Greeting'
212
+ foreground :blue
213
+ selection_foreground :dark_blue
214
+ font name: 'Times New Roman', height: 30, style: [:bold, :italic]
215
+ image image(30, 30) {
216
+ # building image on the fly with Canvas Shape DSL text shape flag emoji
217
+ text('🇸🇪', 0, 0) {
218
+ font name: 'Times New Roman', height: 30, style: [:bold, :italic]
219
+ }
220
+ }
221
+
222
+ label {
223
+ text 'Hej, Världen!'
224
+ font name: 'Times New Roman', height: 90, style: [:bold, :italic]
225
+ }
226
+ }
227
+
228
+ c_tab_item(:close) {
229
+ text 'Norwegian'
230
+ tool_tip_text 'Norwegian Greeting'
231
+ foreground :blue
232
+ selection_foreground :dark_blue
233
+ font name: 'Times New Roman', height: 30, style: [:bold, :italic]
234
+ image image(30, 30) {
235
+ # building image on the fly with Canvas Shape DSL text shape flag emoji
236
+ text('🇳🇴', 0, 0) {
237
+ font name: 'Times New Roman', height: 30, style: [:bold, :italic]
238
+ }
239
+ }
240
+
241
+ label {
242
+ text 'Hei, Verden!'
243
+ font name: 'Times New Roman', height: 90, style: [:bold, :italic]
244
+ }
245
+ }
246
+
247
+ c_tab_item(:close) {
248
+ text 'Russian'
249
+ tool_tip_text 'Russian Greeting'
250
+ foreground :blue
251
+ selection_foreground :dark_blue
252
+ font name: 'Times New Roman', height: 30, style: [:bold, :italic]
253
+ image image(30, 30) {
254
+ # building image on the fly with Canvas Shape DSL text shape flag emoji
255
+ text('🇷🇺', 0, 0) {
256
+ font name: 'Times New Roman', height: 30, style: [:bold, :italic]
257
+ }
258
+ }
259
+
260
+ label {
261
+ text 'Привет, мир!'
262
+ font name: 'Times New Roman', height: 90, style: [:bold, :italic]
263
+ }
264
+ }
265
+
266
+ }
267
+ }
268
+ }
269
+ end
270
+
271
+ HelloCTab.launch