glimmer-dsl-swt 4.18.3.0 → 4.18.3.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +59 -0
  3. data/README.md +440 -18
  4. data/VERSION +1 -1
  5. data/glimmer-dsl-swt.gemspec +11 -7
  6. data/lib/ext/glimmer/config.rb +24 -7
  7. data/lib/ext/rouge/themes/glimmer.rb +29 -0
  8. data/lib/glimmer-dsl-swt.rb +0 -1
  9. data/lib/glimmer/data_binding/table_items_binding.rb +8 -5
  10. data/lib/glimmer/data_binding/widget_binding.rb +22 -4
  11. data/lib/glimmer/dsl/swt/image_expression.rb +14 -6
  12. data/lib/glimmer/dsl/swt/layout_data_expression.rb +4 -4
  13. data/lib/glimmer/dsl/swt/layout_expression.rb +5 -3
  14. data/lib/glimmer/swt/custom/code_text.rb +132 -37
  15. data/lib/glimmer/swt/custom/drawable.rb +3 -2
  16. data/lib/glimmer/swt/custom/shape.rb +25 -11
  17. data/lib/glimmer/swt/date_time_proxy.rb +1 -3
  18. data/lib/glimmer/swt/directory_dialog_proxy.rb +3 -3
  19. data/lib/glimmer/swt/display_proxy.rb +26 -5
  20. data/lib/glimmer/swt/file_dialog_proxy.rb +3 -3
  21. data/lib/glimmer/swt/font_proxy.rb +1 -0
  22. data/lib/glimmer/swt/image_proxy.rb +68 -1
  23. data/lib/glimmer/swt/shell_proxy.rb +23 -3
  24. data/lib/glimmer/swt/table_proxy.rb +43 -15
  25. data/lib/glimmer/swt/widget_listener_proxy.rb +14 -5
  26. data/lib/glimmer/swt/widget_proxy.rb +7 -3
  27. data/lib/glimmer/ui/custom_shell.rb +11 -9
  28. data/lib/glimmer/ui/custom_widget.rb +32 -17
  29. data/samples/elaborate/meta_sample.rb +81 -24
  30. data/samples/elaborate/tetris.rb +146 -42
  31. data/samples/elaborate/tetris/model/game.rb +259 -137
  32. data/samples/elaborate/tetris/{view/game_over_dialog.rb → model/past_game.rb} +11 -44
  33. data/samples/elaborate/tetris/model/tetromino.rb +45 -29
  34. data/samples/elaborate/tetris/view/block.rb +8 -13
  35. data/samples/elaborate/tetris/view/high_score_dialog.rb +131 -0
  36. data/samples/elaborate/tetris/view/playfield.rb +1 -1
  37. data/samples/elaborate/tetris/view/score_lane.rb +11 -11
  38. data/samples/elaborate/tetris/view/tetris_menu_bar.rb +139 -0
  39. data/samples/elaborate/tic_tac_toe.rb +4 -4
  40. data/samples/hello/hello_canvas.rb +4 -4
  41. data/samples/hello/hello_canvas_animation.rb +3 -3
  42. data/samples/hello/hello_canvas_transform.rb +1 -1
  43. data/samples/hello/hello_code_text.rb +84 -0
  44. data/samples/hello/hello_link.rb +1 -1
  45. metadata +9 -5
data/VERSION CHANGED
@@ -1 +1 @@
1
- 4.18.3.0
1
+ 4.18.3.5
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: glimmer-dsl-swt 4.18.3.0 ruby lib
5
+ # stub: glimmer-dsl-swt 4.18.3.5 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "glimmer-dsl-swt".freeze
9
- s.version = "4.18.3.0"
9
+ s.version = "4.18.3.5"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["AndyMaleh".freeze]
14
- s.date = "2021-01-26"
14
+ s.date = "2021-02-01"
15
15
  s.description = "Glimmer DSL for SWT (JRuby Desktop Development GUI Framework) is a native-GUI cross-platform desktop development library written in JRuby, an OS-threaded faster JVM version of Ruby. Glimmer's main innovation is a declarative Ruby DSL that enables productive and efficient authoring of desktop application user-interfaces by relying on the robust Eclipse SWT library. Glimmer additionally innovates by having built-in data-binding support, which greatly facilitates synchronizing the GUI with domain models, thus achieving true decoupling of object oriented components and enabling developers to solve business problems (test-first) without worrying about GUI concerns, or alternatively drive development GUI-first, and then write clean business models (test-first) afterwards. Not only does Glimmer provide a large set of GUI widgets, but it also supports drawing Canvas Graphics like Shapes and Animations. To get started quickly, Glimmer offers scaffolding options for Apps, Gems, and Custom Widgets. Glimmer also includes native-executable packaging support, sorely lacking in other libraries, thus enabling the delivery of desktop apps written in Ruby as truly native DMG/PKG/APP files on the Mac + App Store, MSI/EXE files on Windows, and Gem Packaged Shell Scripts on Linux.".freeze
16
16
  s.email = "andy.am@gmail.com".freeze
17
17
  s.executables = ["glimmer".freeze, "girb".freeze]
@@ -34,6 +34,7 @@ Gem::Specification.new do |s|
34
34
  "icons/scaffold_app.png",
35
35
  "lib/ext/glimmer.rb",
36
36
  "lib/ext/glimmer/config.rb",
37
+ "lib/ext/rouge/themes/glimmer.rb",
37
38
  "lib/glimmer-dsl-swt.rb",
38
39
  "lib/glimmer/Rakefile",
39
40
  "lib/glimmer/data_binding/list_selection_binding.rb",
@@ -138,11 +139,13 @@ Gem::Specification.new do |s|
138
139
  "samples/elaborate/tetris.rb",
139
140
  "samples/elaborate/tetris/model/block.rb",
140
141
  "samples/elaborate/tetris/model/game.rb",
142
+ "samples/elaborate/tetris/model/past_game.rb",
141
143
  "samples/elaborate/tetris/model/tetromino.rb",
142
144
  "samples/elaborate/tetris/view/block.rb",
143
- "samples/elaborate/tetris/view/game_over_dialog.rb",
145
+ "samples/elaborate/tetris/view/high_score_dialog.rb",
144
146
  "samples/elaborate/tetris/view/playfield.rb",
145
147
  "samples/elaborate/tetris/view/score_lane.rb",
148
+ "samples/elaborate/tetris/view/tetris_menu_bar.rb",
146
149
  "samples/elaborate/tic_tac_toe.rb",
147
150
  "samples/elaborate/tic_tac_toe/board.rb",
148
151
  "samples/elaborate/tic_tac_toe/cell.rb",
@@ -154,6 +157,7 @@ Gem::Specification.new do |s|
154
157
  "samples/hello/hello_canvas_transform.rb",
155
158
  "samples/hello/hello_checkbox.rb",
156
159
  "samples/hello/hello_checkbox_group.rb",
160
+ "samples/hello/hello_code_text.rb",
157
161
  "samples/hello/hello_combo.rb",
158
162
  "samples/hello/hello_computed.rb",
159
163
  "samples/hello/hello_computed/contact.rb",
@@ -194,7 +198,7 @@ Gem::Specification.new do |s|
194
198
  s.specification_version = 4
195
199
 
196
200
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
197
- s.add_runtime_dependency(%q<glimmer>.freeze, ["~> 1.0.9"])
201
+ s.add_runtime_dependency(%q<glimmer>.freeze, ["~> 1.0.11"])
198
202
  s.add_runtime_dependency(%q<super_module>.freeze, [">= 1.4.1", "< 2.0.0"])
199
203
  s.add_runtime_dependency(%q<nested_inherited_jruby_include_package>.freeze, [">= 0.3.0", "< 2.0.0"])
200
204
  s.add_runtime_dependency(%q<puts_debuggerer>.freeze, [">= 0.11.0", "< 2.0.0"])
@@ -213,7 +217,7 @@ Gem::Specification.new do |s|
213
217
  s.add_development_dependency(%q<simplecov>.freeze, ["~> 0.16.1"])
214
218
  s.add_development_dependency(%q<simplecov-lcov>.freeze, ["~> 0.7.0"])
215
219
  else
216
- s.add_dependency(%q<glimmer>.freeze, ["~> 1.0.9"])
220
+ s.add_dependency(%q<glimmer>.freeze, ["~> 1.0.11"])
217
221
  s.add_dependency(%q<super_module>.freeze, [">= 1.4.1", "< 2.0.0"])
218
222
  s.add_dependency(%q<nested_inherited_jruby_include_package>.freeze, [">= 0.3.0", "< 2.0.0"])
219
223
  s.add_dependency(%q<puts_debuggerer>.freeze, [">= 0.11.0", "< 2.0.0"])
@@ -233,7 +237,7 @@ Gem::Specification.new do |s|
233
237
  s.add_dependency(%q<simplecov-lcov>.freeze, ["~> 0.7.0"])
234
238
  end
235
239
  else
236
- s.add_dependency(%q<glimmer>.freeze, ["~> 1.0.9"])
240
+ s.add_dependency(%q<glimmer>.freeze, ["~> 1.0.11"])
237
241
  s.add_dependency(%q<super_module>.freeze, [">= 1.4.1", "< 2.0.0"])
238
242
  s.add_dependency(%q<nested_inherited_jruby_include_package>.freeze, [">= 0.3.0", "< 2.0.0"])
239
243
  s.add_dependency(%q<puts_debuggerer>.freeze, [">= 0.11.0", "< 2.0.0"])
@@ -32,6 +32,8 @@ module Glimmer
32
32
  'org.eclipse.swt.custom',
33
33
  'org.eclipse.swt.dnd',
34
34
  ]
35
+ DEFAULT_AUTO_SYNC_EXEC = false
36
+ GUI_THREAD = Thread.current
35
37
 
36
38
  class << self
37
39
  # Tells Glimmer to import SWT packages into including class (default: true)
@@ -45,6 +47,22 @@ module Glimmer
45
47
  @@import_swt_packages
46
48
  end
47
49
 
50
+ # Tells Glimmer to avoid automatic use of sync_exec when invoking GUI calls from another thread (default: true)
51
+ def auto_sync_exec=(value)
52
+ @@auto_sync_exec = value
53
+ end
54
+
55
+ # Returns whether Glimmer will import SWT packages into including class
56
+ def auto_sync_exec
57
+ @@auto_sync_exec = DEFAULT_AUTO_SYNC_EXEC if !defined?(@@auto_sync_exec)
58
+ @@auto_sync_exec
59
+ end
60
+ alias auto_sync_exec? auto_sync_exec
61
+
62
+ def require_sync_exec?
63
+ Thread.current != GUI_THREAD
64
+ end
65
+
48
66
  # Returns Logging Devices. Default is [:stdout, :syslog]
49
67
  def logging_devices
50
68
  unless defined? @@logging_devices
@@ -140,11 +158,10 @@ end
140
158
 
141
159
  Glimmer::Config.excluded_keyword_checkers << lambda do |method_symbol, *args|
142
160
  method = method_symbol.to_s
143
- result = false
144
- result ||= method == 'dispose' && is_a?(Glimmer::UI::CustomWidget) && respond_to?(method)
145
- result ||= ['drag_source_proxy', 'drop_target_proxy'].include?(method) && is_a?(Glimmer::UI::CustomWidget)
146
- result ||= method == 'post_initialize_child'
147
- result ||= method == 'handle'
148
- result ||= method.end_with?('=')
149
- result ||= ['finish_edit!', 'search', 'all_tree_items', 'depth_first_search'].include?(method) && is_a?(Glimmer::UI::CustomWidget) && body_root.respond_to?(method)
161
+ return true if method == 'post_initialize_child'
162
+ return true if method == 'handle'
163
+ return true if method.end_with?('=')
164
+ return true if ['drag_source_proxy', 'drop_target_proxy'].include?(method) && is_a?(Glimmer::UI::CustomWidget)
165
+ return true if method == 'dispose' && is_a?(Glimmer::UI::CustomWidget) && respond_to?(method)
166
+ return true if ['finish_edit!', 'search', 'all_tree_items', 'depth_first_search'].include?(method) && is_a?(Glimmer::UI::CustomWidget) && body_root.respond_to?(method)
150
167
  end
@@ -0,0 +1,29 @@
1
+ module Rouge
2
+ module Themes
3
+ # A port of the pastie style from Pygments.
4
+ # See https://bitbucket.org/birkenfeld/pygments-main/src/default/pygments/styles/pastie.py
5
+ class Glimmer < Github
6
+ name 'glimmer'
7
+ style Comment::Single, fg: [106,115,125], italic: true # Also, Comments
8
+ style Keyword::Pseudo, fg: [:dark_red]
9
+ style Keyword, fg: [:blue]
10
+ style Literal::String::Single, fg: [106,115,125] # Also, Comments
11
+ style Literal::String::Double, fg: [0,92,197]
12
+ style Literal::String::Escape, fg: [:red]
13
+ style Literal::Number::Integer, fg: [:blue]
14
+ style Literal::String::Interpol, fg: [:blue]
15
+ style Literal::String::Symbol, fg: [:dark_green]
16
+ style Literal::String, fg: [:dark_blue]
17
+ style Name::Builtin, fg: [215,58,73]
18
+ style Name::Class, fg: [3,47,98]
19
+ style Name::Namespace, fg: [3,47,98]
20
+ style Name::Constant, fg: [0,92,197]
21
+ style Name::Function, fg: [:blue]
22
+ style Name::Variable::Instance, fg: [227,98,9]
23
+ style Name, fg: [111,66,193] #purple
24
+ style Operator, fg: [:red]
25
+ style Punctuation, fg: [:blue]
26
+ style Text, fg: [75, 75, 75]
27
+ end
28
+ end
29
+ end
@@ -34,7 +34,6 @@ require 'glimmer'
34
34
  require 'logging'
35
35
  require 'nested_inherited_jruby_include_package'
36
36
  require 'super_module'
37
- require 'rouge'
38
37
  require 'date'
39
38
  require 'facets/string/capitalized'
40
39
  require 'facets/hash/symbolize_keys'
@@ -36,7 +36,9 @@ module Glimmer
36
36
  def initialize(parent, model_binding, column_properties)
37
37
  @table = parent
38
38
  @model_binding = model_binding
39
+ @read_only_sort = @model_binding.binding_options[:read_only_sort]
39
40
  @table.swt_widget.data = @model_binding
41
+ @table.swt_widget.set_data('table_items_binding', self)
40
42
  @column_properties = column_properties
41
43
  @table.on_widget_disposed do |dispose_event|
42
44
  unregister_all_observables
@@ -50,8 +52,8 @@ module Glimmer
50
52
  call
51
53
  end
52
54
 
53
- def call(new_model_collection=nil)
54
- new_model_collection = @model_binding.evaluate_property # this ensures applying converters (e.g. :on_read)
55
+ def call(new_model_collection=nil, internal_sort: false)
56
+ new_model_collection = model_binding_evaluated_property = @model_binding.evaluate_property unless internal_sort # this ensures applying converters (e.g. :on_read)
55
57
  table_cells = @table.swt_widget.items.map {|item| @table.column_properties.size.times.map {|i| item.get_text(i)} }
56
58
  model_cells = new_model_collection.to_a.map {|m| @table.cells_for(m)}
57
59
  return if table_cells == model_cells
@@ -61,10 +63,10 @@ module Glimmer
61
63
  add_dependent(@table_observer_registration => @table_items_observer_registration)
62
64
  @model_collection = new_model_collection
63
65
  end
64
- populate_table(@model_collection, @table, @column_properties)
66
+ populate_table(@model_collection, @table, @column_properties, internal_sort: internal_sort)
65
67
  end
66
68
 
67
- def populate_table(model_collection, parent, column_properties)
69
+ def populate_table(model_collection, parent, column_properties, internal_sort: false)
68
70
  selected_table_item_models = parent.swt_widget.getSelection.map(&:get_data)
69
71
  parent.finish_edit!
70
72
  parent.swt_widget.items.each(&:dispose)
@@ -78,7 +80,8 @@ module Glimmer
78
80
  end
79
81
  selected_table_items = parent.search {|item| selected_table_item_models.include?(item.get_data) }
80
82
  parent.swt_widget.setSelection(selected_table_items)
81
- parent.sort!
83
+ sorted_model_collection = parent.sort!(internal_sort: internal_sort)
84
+ call(sorted_model_collection, internal_sort: true) if @read_only_sort && !internal_sort && !sorted_model_collection.nil?
82
85
  parent.swt_widget.redraw if parent&.swt_widget&.respond_to?(:redraw)
83
86
  end
84
87
  end
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2007-2021 Andy Maleh
2
- #
2
+ #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
5
5
  # "Software"), to deal in the Software without restriction, including
@@ -7,10 +7,10 @@
7
7
  # distribute, sublicense, and/or sell copies of the Software, and to
8
8
  # permit persons to whom the Software is furnished to do so, subject to
9
9
  # the following conditions:
10
- #
10
+ #
11
11
  # The above copyright notice and this permission notice shall be
12
12
  # included in all copies or substantial portions of the Software.
13
- #
13
+ #
14
14
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
15
  # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
16
  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -22,6 +22,8 @@
22
22
  require 'glimmer/data_binding/observable'
23
23
  require 'glimmer/data_binding/observer'
24
24
 
25
+ require 'glimmer/swt/display_proxy'
26
+
25
27
  module Glimmer
26
28
  module DataBinding
27
29
  class WidgetBinding
@@ -44,10 +46,26 @@ module Glimmer
44
46
 
45
47
  def call(value)
46
48
  converted_value = translated_value = @translator.call(value)
47
- @widget.set_attribute(@property, converted_value) unless evaluate_property == converted_value
49
+
50
+ update_operation = lambda do
51
+ if @widget.respond_to?(:disposed?) && @widget.disposed?
52
+ unregister_all_observables
53
+ return
54
+ end
55
+ @widget.set_attribute(@property, converted_value) unless evaluate_property == converted_value
56
+ end
57
+ if Config.auto_sync_exec? && Config.require_sync_exec?
58
+ SWT::DisplayProxy.instance.sync_exec(&update_operation)
59
+ else
60
+ update_operation.call
61
+ end
48
62
  end
49
63
 
50
64
  def evaluate_property
65
+ if @widget.respond_to?(:disposed?) && @widget.disposed?
66
+ unregister_all_observables
67
+ return
68
+ end
51
69
  @widget.get_attribute(@property)
52
70
  end
53
71
  end
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2007-2021 Andy Maleh
2
- #
2
+ #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
5
5
  # "Software"), to deal in the Software without restriction, including
@@ -7,10 +7,10 @@
7
7
  # distribute, sublicense, and/or sell copies of the Software, and to
8
8
  # permit persons to whom the Software is furnished to do so, subject to
9
9
  # the following conditions:
10
- #
10
+ #
11
11
  # The above copyright notice and this permission notice shall be
12
12
  # included in all copies or substantial portions of the Software.
13
- #
13
+ #
14
14
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
15
  # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
16
  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -28,13 +28,21 @@ module Glimmer
28
28
  # image expression
29
29
  # Note: Cannot be a static expression because it clashes with image property expression
30
30
  class ImageExpression < Expression
31
+ include ParentExpression
32
+
31
33
  def can_interpret?(parent, keyword, *args, &block)
32
- keyword.to_s == 'image' and
33
- (parent.nil? or !parent.respond_to?('image'))
34
+ (keyword == 'image') and
35
+ (parent.nil? or parent.respond_to?('image='))
34
36
  end
35
37
 
36
38
  def interpret(parent, keyword, *args, &block)
37
- Glimmer::SWT::ImageProxy.new(*args)
39
+ args.unshift(parent) unless parent.nil?
40
+ Glimmer::SWT::ImageProxy.new(*args, &block)
41
+ end
42
+
43
+ def add_content(parent, &block)
44
+ super
45
+ parent.post_add_content
38
46
  end
39
47
  end
40
48
  end
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2007-2021 Andy Maleh
2
- #
2
+ #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
5
5
  # "Software"), to deal in the Software without restriction, including
@@ -7,10 +7,10 @@
7
7
  # distribute, sublicense, and/or sell copies of the Software, and to
8
8
  # permit persons to whom the Software is furnished to do so, subject to
9
9
  # the following conditions:
10
- #
10
+ #
11
11
  # The above copyright notice and this permission notice shall be
12
12
  # included in all copies or substantial portions of the Software.
13
- #
13
+ #
14
14
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
15
  # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
16
  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -33,7 +33,7 @@ module Glimmer
33
33
  include ParentExpression
34
34
 
35
35
  def can_interpret?(parent, keyword, *args, &block)
36
- keyword == 'layout_data' and
36
+ super and
37
37
  parent.respond_to?(:swt_widget)
38
38
  end
39
39
 
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2007-2021 Andy Maleh
2
- #
2
+ #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
5
5
  # "Software"), to deal in the Software without restriction, including
@@ -7,10 +7,10 @@
7
7
  # distribute, sublicense, and/or sell copies of the Software, and to
8
8
  # permit persons to whom the Software is furnished to do so, subject to
9
9
  # the following conditions:
10
- #
10
+ #
11
11
  # The above copyright notice and this permission notice shall be
12
12
  # included in all copies or substantial portions of the Software.
13
- #
13
+ #
14
14
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
15
  # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
16
  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -23,6 +23,7 @@ require 'glimmer'
23
23
  require 'glimmer/dsl/expression'
24
24
  require 'glimmer/dsl/parent_expression'
25
25
  require 'glimmer/swt/layout_proxy'
26
+ require 'glimmer/swt/image_proxy'
26
27
 
27
28
  module Glimmer
28
29
  module DSL
@@ -31,6 +32,7 @@ module Glimmer
31
32
  include ParentExpression
32
33
 
33
34
  include_package 'org.eclipse.swt.widgets'
35
+ include_package 'org.eclipse.swt.graphics'
34
36
 
35
37
  def can_interpret?(parent, keyword, *args, &block)
36
38
  keyword.to_s.end_with?('_layout') and
@@ -7,36 +7,59 @@ module Glimmer
7
7
  class CodeText
8
8
  include Glimmer::UI::CustomWidget
9
9
 
10
- SYNTAX_COLOR_MAP = {
11
- Builtin: [215,58,73],
12
- Class: [3,47,98],
13
- Constant: [0,92,197],
14
- Double: [0,92,197],
15
- Escape: [:red],
16
- Function: [:blue],
17
- Instance: [227,98,9],
18
- Integer: [:blue],
19
- Interpol: [:blue],
20
- Keyword: [:blue],
21
- Name: [111,66,193], #purple
22
- Operator: [:red],
23
- Pseudo: [:dark_red],
24
- Punctuation: [:blue],
25
- Single: [106,115,125], # Also, Comments
26
- Symbol: [:dark_green],
27
- Text: [75, 75, 75],
28
- }
10
+ class << self
11
+ def languages
12
+ require 'rouge'
13
+ Rouge::Lexer.all.map {|lexer| lexer.tag}.sort
14
+ end
15
+
16
+ def lexers
17
+ require 'rouge'
18
+ Rouge::Lexer.all.sort_by(&:title)
19
+ end
20
+ end
21
+
22
+ REGEX_COLOR_HEX6 = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/
29
23
 
30
- # TODO support `option :language`
31
24
  # TODO support auto language detection
25
+ # TODO support end of line via CMD+E and beginning of line via CMD+A
26
+ # TODO support select all via CMD+A
27
+
28
+ option :language, default: 'ruby'
29
+ # TODO consider supporting data-binding of language
30
+ # TODO support switcher of language that automatically updates the lexer
31
+ # TODO support method for redrawing the syntax highlighting
32
+ option :theme, default: 'glimmer'
33
+ # option :lines, default: false
32
34
 
33
- def text=(value)
34
- swt_widget&.text = value
35
- end
35
+ # attr_accessor :code_text_widget_text, :code_text_widget_top_pixel
36
+ # attr_reader :styled_text_proxy
36
37
 
37
- def text
38
- swt_widget&.text
39
- end
38
+
39
+
40
+ # def text=(value)
41
+ # if lines
42
+ # @styled_text_proxy&.swt_widget&.text = value
43
+ # else
44
+ # super
45
+ # end
46
+ # end
47
+
48
+ # def text(*args)
49
+ # if lines
50
+ # @styled_text_proxy&.swt_widget&.text
51
+ # else
52
+ # super
53
+ # end
54
+ # end
55
+
56
+ # def lines_width
57
+ # if lines == true
58
+ # 4
59
+ # elsif lines.is_a?(Hash)
60
+ # lines[:width]
61
+ # end
62
+ # end
40
63
 
41
64
  def syntax_highlighting(text)
42
65
  return [] if text.to_s.strip.empty?
@@ -56,16 +79,68 @@ module Glimmer
56
79
 
57
80
  def lexer
58
81
  # TODO Try to use Rouge::Lexer.find_fancy('guess', code) in the future to guess the language or otherwise detect it from file extension
59
- @lexer ||= Rouge::Lexer.find_fancy('ruby')
82
+ @lexer ||= Rouge::Lexer.find_fancy(language)
60
83
  end
61
84
 
62
85
  before_body {
86
+ require 'rouge'
87
+ require 'ext/rouge/themes/glimmer'
63
88
  @swt_style = swt_style == 0 ? [:border, :multi, :v_scroll, :h_scroll] : swt_style
64
89
  @font_name = display.get_font_list(nil, true).map(&:name).include?('Consolas') ? 'Consolas' : 'Courier'
65
90
  }
66
91
 
67
92
  body {
68
- styled_text(swt_style) {
93
+ # TODO enable this once fully implemented
94
+ # if lines
95
+ # composite {
96
+ # grid_layout(2, false)
97
+ # layout_data :fill, :fill, true, true
98
+ # @line_numbers_text = styled_text(:multi, :border) {
99
+ # layout_data(:right, :fill, false, true)
100
+ # text ' 1'
101
+ # line_count = code_text_widget_text.to_s.split("\n").count
102
+ # line_count = 1 if line_count == 0
103
+ # lines_text_size = [line_count.to_s.size, 4].max
104
+ # text line_count.times.map {|n| (' ' * (lines_text_size - (n+1).to_s.size)) + (n+1).to_s }.join("\n")
105
+ # text bind(self, :code_text_widget_text, read_only: true) { |text_value|
106
+ # line_count = text_value.to_s.split("\n").count
107
+ # line_count = 1 if line_count == 0
108
+ # lines_text_size = [line_count.to_s.size, 4].max
109
+ # line_count.times.map {|n| (' ' * (lines_text_size - (n+1).to_s.size)) + (n+1).to_s }.join("\n")
110
+ # }
111
+ # top_pixel bind(self, :code_text_widget_top_pixel, read_only: true)
112
+ # font name: @font_name, height: OS.mac? ? 15 : 12
113
+ # background color(:widget_background)
114
+ # foreground :dark_blue
115
+ # top_margin 5
116
+ # right_margin 5
117
+ # bottom_margin 5
118
+ # left_margin 5
119
+ # editable false
120
+ # caret nil
121
+ # on_focus_gained {
122
+ # @styled_text_proxy&.swt_widget.setFocus
123
+ # }
124
+ # on_key_pressed {
125
+ # @styled_text_proxy&.swt_widget.setFocus
126
+ # }
127
+ # on_mouse_up {
128
+ # @styled_text_proxy&.swt_widget.setFocus
129
+ # }
130
+ # }
131
+ #
132
+ # code_text_widget
133
+ # }
134
+ # else
135
+ code_text_widget
136
+ # end
137
+ }
138
+
139
+ def code_text_widget
140
+ @styled_text_proxy = styled_text(swt_style) {
141
+ # layout_data :fill, :fill, true, true if lines
142
+ # text bind(self, :code_text_widget_text) if lines
143
+ # top_pixel bind(self, :code_text_widget_top_pixel) if lines
69
144
  font name: @font_name, height: 15
70
145
  foreground rgb(75, 75, 75)
71
146
  left_margin 5
@@ -88,18 +163,38 @@ module Glimmer
88
163
  }
89
164
 
90
165
  on_line_get_style { |line_style_event|
91
- styles = []
92
- syntax_highlighting(line_style_event.lineText).to_a.each do |token_hash|
93
- start_index = token_hash[:token_index]
94
- size = token_hash[:token_text].size
95
- token_color = SYNTAX_COLOR_MAP[token_hash[:token_type].name] || [:black]
96
- token_color = color(*token_color).swt_color
97
- styles << StyleRange.new(line_style_event.lineOffset + start_index, size, token_color, nil)
166
+ begin
167
+ styles = []
168
+ style_data = nil
169
+ syntax_highlighting(line_style_event.lineText).to_a.each do |token_hash|
170
+ start_index = token_hash[:token_index]
171
+ size = token_hash[:token_text].size
172
+ style_data = Rouge::Theme.find(theme).new.style_for(token_hash[:token_type])
173
+ foreground_color = hex_color_to_swt_color(style_data[:fg], [:black])
174
+ background_color = hex_color_to_swt_color(style_data[:bg], [:white])
175
+ font_styles = []
176
+ font_styles << :bold if style_data[:bold]
177
+ font_styles << :italic if style_data[:italic]
178
+ font_style = SWTProxy[*font_styles]
179
+ styles << StyleRange.new(line_style_event.lineOffset + start_index, size, foreground_color, background_color, font_style)
180
+ end
181
+ line_style_event.styles = styles.to_java(StyleRange) unless styles.empty?
182
+ rescue => e
183
+ Glimmer::Config.logger.error {"Error encountered with style data: #{style_data}"}
184
+ Glimmer::Config.logger.error {e.message}
185
+ Glimmer::Config.logger.error {e.full_message}
98
186
  end
99
- line_style_event.styles = styles.to_java(StyleRange) unless styles.empty?
100
187
  }
101
188
  }
102
- }
189
+ end
190
+
191
+ def hex_color_to_swt_color(color_data, default_color)
192
+ color_data = "##{color_data.chars.drop(1).map {|c| c*2}.join}" if color_data.is_a?(String) && color_data.start_with?('#') && color_data&.size == 4
193
+ color_data = color_data.match(REGEX_COLOR_HEX6).to_a.drop(1).map {|c| "0x#{c}".hex}.to_a if color_data.is_a?(String) && color_data.start_with?('#')
194
+ color_data = [color_data] unless color_data.nil? || color_data.empty? || color_data.is_a?(Array)
195
+ color_data = default_color if color_data.nil? || color_data.empty?
196
+ color(*color_data).swt_color
197
+ end
103
198
  end
104
199
  end
105
200
  end