scarpe 0.2.2 → 0.3.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 (223) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/CHANGELOG.md +16 -2
  4. data/Gemfile.lock +7 -3
  5. data/README.md +24 -8
  6. data/Rakefile +1 -1
  7. data/examples/animate.rb +20 -0
  8. data/examples/arrow.rb +10 -0
  9. data/examples/btn_tooltip.rb +7 -0
  10. data/examples/button_style_changed.rb +7 -0
  11. data/examples/button_styles_default.rb +6 -0
  12. data/examples/gen.rb +8 -8
  13. data/examples/highlander.rb +3 -3
  14. data/examples/legacy/README.md +6 -0
  15. data/examples/legacy/not_checked/shoes-contrib/basic/shoes-notes.rb +1 -1
  16. data/examples/legacy/not_checked/simple/anim-shapes.rb +1 -1
  17. data/examples/legacy/not_checked/speedometer_app.rb +55 -0
  18. data/examples/legacy/working/simple/image-icon.rb +3 -0
  19. data/examples/legacy/{not_checked → working}/simple/image.rb +1 -1
  20. data/examples/list_box_choose.rb +17 -0
  21. data/examples/local_assets/local_file_server.rb +82 -0
  22. data/examples/local_assets/sample.gif +0 -0
  23. data/examples/local_assets/sample.mp4 +0 -0
  24. data/examples/local_fonts.rb +2 -2
  25. data/examples/local_images.rb +2 -3
  26. data/examples/para/para_text.rb +14 -0
  27. data/examples/progress.rb +31 -0
  28. data/examples/radio/radio_groups.rb +2 -2
  29. data/examples/rect.rb +4 -0
  30. data/examples/rotate_shapes.rb +17 -0
  31. data/examples/simpler-menu.rb +21 -0
  32. data/exe/scarpe +2 -1
  33. data/lacci/Gemfile +2 -0
  34. data/lacci/Gemfile.lock +8 -1
  35. data/lacci/lacci.gemspec +1 -1
  36. data/lacci/lib/lacci/scarpe_cli.rb +2 -1
  37. data/lacci/lib/lacci/scarpe_core.rb +2 -1
  38. data/lacci/lib/lacci/version.rb +1 -1
  39. data/lacci/lib/scarpe/niente/app.rb +23 -0
  40. data/lacci/lib/scarpe/niente/display_service.rb +62 -0
  41. data/lacci/lib/scarpe/niente/drawable.rb +57 -0
  42. data/lacci/lib/scarpe/niente/logger.rb +29 -0
  43. data/lacci/lib/scarpe/niente/shoes_spec.rb +87 -0
  44. data/lacci/lib/scarpe/niente.rb +20 -0
  45. data/lacci/lib/shoes/app.rb +88 -43
  46. data/lacci/lib/shoes/background.rb +2 -2
  47. data/lacci/lib/shoes/border.rb +2 -2
  48. data/lacci/lib/shoes/builtins.rb +63 -0
  49. data/lacci/lib/shoes/changelog.rb +52 -0
  50. data/lacci/lib/shoes/colors.rb +3 -1
  51. data/lacci/lib/shoes/constants.rb +19 -1
  52. data/lacci/lib/shoes/display_service.rb +39 -16
  53. data/lacci/lib/shoes/download.rb +2 -2
  54. data/lacci/lib/shoes/drawable.rb +380 -0
  55. data/lacci/lib/shoes/drawables/arc.rb +49 -0
  56. data/lacci/lib/shoes/drawables/arrow.rb +41 -0
  57. data/lacci/lib/shoes/drawables/button.rb +73 -0
  58. data/lacci/lib/shoes/{widgets → drawables}/check.rb +5 -4
  59. data/lacci/lib/shoes/{widgets → drawables}/document_root.rb +3 -3
  60. data/lacci/lib/shoes/{widgets → drawables}/edit_box.rb +6 -6
  61. data/lacci/lib/shoes/{widgets → drawables}/edit_line.rb +6 -6
  62. data/lacci/lib/shoes/{widgets → drawables}/flow.rb +6 -6
  63. data/lacci/lib/shoes/{widgets → drawables}/image.rb +6 -6
  64. data/lacci/lib/shoes/{widgets → drawables}/line.rb +7 -5
  65. data/lacci/lib/shoes/drawables/link.rb +34 -0
  66. data/lacci/lib/shoes/drawables/list_box.rb +56 -0
  67. data/lacci/lib/shoes/drawables/para.rb +118 -0
  68. data/lacci/lib/shoes/drawables/progress.rb +14 -0
  69. data/lacci/lib/shoes/drawables/radio.rb +33 -0
  70. data/lacci/lib/shoes/drawables/rect.rb +17 -0
  71. data/lacci/lib/shoes/{widgets → drawables}/shape.rb +6 -7
  72. data/lacci/lib/shoes/{widgets → drawables}/slot.rb +32 -20
  73. data/lacci/lib/shoes/{widgets → drawables}/span.rb +8 -7
  74. data/lacci/lib/shoes/{widgets → drawables}/stack.rb +6 -4
  75. data/lacci/lib/shoes/drawables/star.rb +50 -0
  76. data/lacci/lib/shoes/drawables/subscription_item.rb +93 -0
  77. data/lacci/lib/shoes/drawables/text_drawable.rb +63 -0
  78. data/lacci/lib/shoes/drawables/video.rb +16 -0
  79. data/lacci/lib/shoes/drawables/widget.rb +69 -0
  80. data/lacci/lib/shoes/drawables.rb +31 -0
  81. data/lacci/lib/shoes/errors.rb +28 -0
  82. data/lacci/lib/shoes/log.rb +2 -2
  83. data/lacci/lib/shoes/ruby_extensions.rb +15 -0
  84. data/lacci/lib/shoes/spacing.rb +2 -2
  85. data/lacci/lib/shoes-spec.rb +93 -0
  86. data/lacci/lib/shoes.rb +27 -7
  87. data/lacci/test/test_helper.rb +54 -0
  88. data/lacci/test/test_lacci.rb +12 -3
  89. data/lacci/test/test_shoes_errors.rb +49 -0
  90. data/lib/scarpe/cats_cradle.rb +81 -59
  91. data/lib/scarpe/errors.rb +77 -0
  92. data/lib/scarpe/evented_assertions.rb +50 -17
  93. data/lib/scarpe/shoes_spec.rb +181 -0
  94. data/lib/scarpe/version.rb +2 -2
  95. data/lib/scarpe/wv/app.rb +20 -20
  96. data/lib/scarpe/wv/arc.rb +4 -47
  97. data/lib/scarpe/wv/arrow.rb +9 -0
  98. data/lib/scarpe/wv/button.rb +7 -35
  99. data/lib/scarpe/wv/check.rb +3 -5
  100. data/lib/scarpe/wv/control_interface.rb +18 -20
  101. data/lib/scarpe/wv/document_root.rb +81 -4
  102. data/lib/scarpe/wv/{widget.rb → drawable.rb} +66 -43
  103. data/lib/scarpe/wv/edit_box.rb +4 -17
  104. data/lib/scarpe/wv/edit_line.rb +4 -18
  105. data/lib/scarpe/wv/flow.rb +2 -18
  106. data/lib/scarpe/wv/image.rb +8 -28
  107. data/lib/scarpe/wv/line.rb +3 -25
  108. data/lib/scarpe/wv/link.rb +3 -16
  109. data/lib/scarpe/wv/list_box.rb +6 -29
  110. data/lib/scarpe/wv/para.rb +11 -30
  111. data/lib/scarpe/wv/progress.rb +19 -0
  112. data/lib/scarpe/wv/radio.rb +9 -10
  113. data/lib/scarpe/wv/rect.rb +13 -0
  114. data/lib/scarpe/wv/shape.rb +3 -8
  115. data/lib/scarpe/wv/slot.rb +8 -25
  116. data/lib/scarpe/wv/span.rb +3 -27
  117. data/lib/scarpe/wv/stack.rb +2 -18
  118. data/lib/scarpe/wv/star.rb +3 -53
  119. data/lib/scarpe/wv/subscription_item.rb +38 -4
  120. data/lib/scarpe/wv/text_drawable.rb +32 -0
  121. data/lib/scarpe/wv/video.rb +15 -15
  122. data/lib/scarpe/wv/web_wrangler.rb +299 -329
  123. data/lib/scarpe/wv/webview_local_display.rb +48 -33
  124. data/lib/scarpe/wv/webview_relay_display.rb +12 -12
  125. data/lib/scarpe/wv/webview_relay_util.rb +7 -10
  126. data/lib/scarpe/wv/wv_display_worker.rb +2 -2
  127. data/lib/scarpe/wv.rb +45 -12
  128. data/lib/scarpe/wv_local.rb +1 -1
  129. data/lib/scarpe/wv_relay.rb +1 -1
  130. data/lib/scarpe.rb +1 -0
  131. data/logger/debug_web_wrangler.json +1 -1
  132. data/logger/scarpe_wv_test.json +1 -1
  133. data/scarpe-components/Gemfile.lock +86 -0
  134. data/scarpe-components/lib/scarpe/components/base64.rb +3 -7
  135. data/scarpe-components/lib/scarpe/components/calzini/alert.rb +49 -0
  136. data/scarpe-components/lib/scarpe/components/calzini/art_widgets.rb +203 -0
  137. data/scarpe-components/lib/scarpe/components/calzini/button.rb +39 -0
  138. data/scarpe-components/lib/scarpe/components/calzini/misc.rb +146 -0
  139. data/scarpe-components/lib/scarpe/components/calzini/para.rb +35 -0
  140. data/scarpe-components/lib/scarpe/components/calzini/slots.rb +155 -0
  141. data/scarpe-components/lib/scarpe/components/calzini/text_widgets.rb +65 -0
  142. data/scarpe-components/lib/scarpe/components/calzini.rb +149 -0
  143. data/scarpe-components/lib/scarpe/components/errors.rb +20 -0
  144. data/scarpe-components/lib/scarpe/components/file_helpers.rb +1 -0
  145. data/scarpe-components/lib/scarpe/components/html.rb +131 -0
  146. data/scarpe-components/lib/scarpe/components/minitest_export_reporter.rb +75 -0
  147. data/scarpe-components/lib/scarpe/components/minitest_import_runnable.rb +98 -0
  148. data/scarpe-components/lib/scarpe/components/minitest_result.rb +86 -0
  149. data/scarpe-components/lib/scarpe/components/modular_logger.rb +5 -5
  150. data/scarpe-components/lib/scarpe/components/print_logger.rb +9 -5
  151. data/scarpe-components/lib/scarpe/components/promises.rb +14 -14
  152. data/scarpe-components/lib/scarpe/components/segmented_file_loader.rb +36 -17
  153. data/scarpe-components/lib/scarpe/components/string_helpers.rb +10 -0
  154. data/scarpe-components/lib/scarpe/components/tiranti.rb +225 -0
  155. data/scarpe-components/lib/scarpe/components/unit_test_helpers.rb +45 -5
  156. data/scarpe-components/lib/scarpe/components/version.rb +2 -2
  157. data/scarpe-components/test/calzini/test_calzini_alert.rb +30 -0
  158. data/scarpe-components/test/calzini/test_calzini_art_drawables.rb +105 -0
  159. data/scarpe-components/test/calzini/test_calzini_button.rb +52 -0
  160. data/scarpe-components/test/calzini/test_calzini_misc.rb +115 -0
  161. data/scarpe-components/test/calzini/test_calzini_para.rb +37 -0
  162. data/scarpe-components/test/calzini/test_calzini_slots.rb +130 -0
  163. data/scarpe-components/test/calzini/test_calzini_text_drawables.rb +41 -0
  164. data/scarpe-components/test/mtr_data/exception.json +1 -0
  165. data/scarpe-components/test/mtr_data/fail_with_message.json +1 -0
  166. data/scarpe-components/test/mtr_data/skipped_no_message.json +1 -0
  167. data/scarpe-components/test/mtr_data/skipped_w_msg.json +1 -0
  168. data/scarpe-components/test/mtr_data/succeed_2_asserts.json +1 -0
  169. data/scarpe-components/test/test_dimensions.rb +26 -0
  170. data/scarpe-components/test/test_helper.rb +20 -0
  171. data/scarpe-components/test/test_html.rb +65 -0
  172. data/scarpe-components/test/test_minitest_result.rb +61 -0
  173. data/scarpe-components/test/test_promises.rb +5 -4
  174. data/scarpe-components/test/test_segmented_app_files.rb +8 -6
  175. data/scarpegen.rb +14 -14
  176. data/sig/scarpe.rbs +1 -1
  177. data/templates/basic_class_template.erb +13 -14
  178. data/templates/class_template_with_event_bind.erb +4 -4
  179. data/templates/class_template_with_shapes.erb +8 -17
  180. data/templates/example_template.erb +1 -1
  181. data/templates/module_template.erb +4 -4
  182. data/templates/webview_template.erb +3 -2
  183. metadata +113 -55
  184. data/examples/legacy/not_checked/shoes-contrib/elements/image-icon.rb +0 -3
  185. data/lacci/lib/shoes/widget.rb +0 -218
  186. data/lacci/lib/shoes/widgets/alert.rb +0 -19
  187. data/lacci/lib/shoes/widgets/arc.rb +0 -51
  188. data/lacci/lib/shoes/widgets/button.rb +0 -35
  189. data/lacci/lib/shoes/widgets/font.rb +0 -14
  190. data/lacci/lib/shoes/widgets/link.rb +0 -25
  191. data/lacci/lib/shoes/widgets/list_box.rb +0 -25
  192. data/lacci/lib/shoes/widgets/para.rb +0 -68
  193. data/lacci/lib/shoes/widgets/radio.rb +0 -35
  194. data/lacci/lib/shoes/widgets/star.rb +0 -44
  195. data/lacci/lib/shoes/widgets/subscription_item.rb +0 -60
  196. data/lacci/lib/shoes/widgets/text_widget.rb +0 -51
  197. data/lacci/lib/shoes/widgets/video.rb +0 -15
  198. data/lacci/lib/shoes/widgets.rb +0 -29
  199. data/lib/scarpe/wv/alert.rb +0 -66
  200. data/lib/scarpe/wv/background.rb +0 -27
  201. data/lib/scarpe/wv/border.rb +0 -24
  202. data/lib/scarpe/wv/control_interface_test.rb +0 -238
  203. data/lib/scarpe/wv/dimensions.rb +0 -22
  204. data/lib/scarpe/wv/font.rb +0 -36
  205. data/lib/scarpe/wv/html.rb +0 -108
  206. data/lib/scarpe/wv/spacing.rb +0 -41
  207. data/lib/scarpe/wv/text_widget.rb +0 -30
  208. /data/examples/legacy/not_checked/{expert → shoes-contrib/basic}/definr.rb +0 -0
  209. /data/examples/legacy/not_checked/{expert → shoes-contrib/basic}/funnies.rb +0 -0
  210. /data/examples/legacy/not_checked/shoes-contrib/{elements → basic}/list_box-select-class.rb +0 -0
  211. /data/examples/legacy/{not_checked/shoes-contrib/basic → working/simple}/basic-edit-box.rb +0 -0
  212. /data/examples/legacy/{not_checked/shoes-contrib/elements → working/simple}/basic-fps.rb +0 -0
  213. /data/examples/legacy/{not_checked/shoes-contrib/elements → working/simple}/border-cat.rb +0 -0
  214. /data/examples/legacy/{not_checked/shoes-contrib/elements → working/simple}/check-mate.rb +0 -0
  215. /data/examples/legacy/{not_checked/shoes-contrib/manipulation → working/simple}/clear-slot.rb +0 -0
  216. /data/examples/legacy/{not_checked/shoes-contrib/basic → working/simple}/clock.rb +0 -0
  217. /data/examples/legacy/{not_checked/shoes-contrib/basic → working/simple}/gradient-shoes.rb +0 -0
  218. /data/examples/legacy/{not_checked/shoes-contrib/basic → working/simple}/list_box-shape-report.rb +0 -0
  219. /data/examples/legacy/{not_checked/shoes-contrib/elements → working/simple}/list_box.rb +0 -0
  220. /data/examples/legacy/{not_checked/shoes-contrib/elements → working/simple}/phat-button.rb +0 -0
  221. /data/examples/legacy/{not_checked/shoes-contrib → working}/simple/simple-calc.rb +0 -0
  222. /data/examples/legacy/{not_checked/shoes-contrib/position → working/simple}/stack-width.rb +0 -0
  223. /data/examples/legacy/{not_checked/shoes-contrib/elements → working/simple}/width-introspec.rb +0 -0
@@ -1,13 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Shoes
3
+ class Shoes
4
4
  class Stack < Shoes::Slot
5
5
  include Shoes::Background
6
6
  include Shoes::Border
7
7
  include Shoes::Spacing
8
8
 
9
9
  # TODO: sort out various margin and padding properties, including putting stuff into spacing
10
- display_properties :width, :height, :scroll
10
+ shoes_styles :width, :height, :scroll
11
+
12
+ shoes_events # No Stack-specific events yet
11
13
 
12
14
  def initialize(width: nil, height: nil, margin: nil, padding: nil, scroll: false, margin_top: nil, margin_bottom: nil, margin_left: nil,
13
15
  margin_right: nil, **options, &block)
@@ -16,8 +18,8 @@ module Shoes
16
18
 
17
19
  super
18
20
 
19
- create_display_widget
20
- # Create the display-side widget *before* running the block, which will add child widgets with their display widgets
21
+ create_display_drawable
22
+ # Create the display-side drawable *before* running the block, which will add child drawables with their display drawables
21
23
  Shoes::App.instance.with_slot(self, &block) if block_given?
22
24
  end
23
25
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Shoes
4
+ class Star < Shoes::Drawable
5
+ shoes_styles :left, :top, :draw_context
6
+
7
+ shoes_style(:points) { |val| convert_to_integer(val, "points") }
8
+ shoes_style(:outer) { |val| convert_to_float(val, "outer") }
9
+ shoes_style(:inner) { |val| convert_to_float(val, "inner") }
10
+
11
+ shoes_events # No Star-specific events yet
12
+
13
+ def initialize(left, top, points = 10, outer = 100, inner = 50)
14
+ super
15
+ self.left = left
16
+ self.top = top
17
+ self.points = points
18
+ self.outer = outer
19
+ self.inner = inner
20
+
21
+ @draw_context = Shoes::App.instance.current_draw_context
22
+
23
+ create_display_drawable
24
+ end
25
+
26
+ def self.convert_to_integer(value, attribute_name)
27
+ begin
28
+ value = Integer(value)
29
+ raise Shoes::Errors::InvalidAttributeValueError, "Negative num '#{value}' not allowed for attribute '#{attribute_name}'" if value < 0
30
+
31
+ value
32
+ rescue ArgumentError
33
+ error_message = "Invalid value '#{value}' provided for attribute '#{attribute_name}'. The value should be a number."
34
+ raise Shoes::Errors::InvalidAttributeValueError, error_message
35
+ end
36
+ end
37
+
38
+ def self.convert_to_float(value, attribute_name)
39
+ begin
40
+ value = Float(value)
41
+ raise Shoes::Errors::InvalidAttributeValueError, "Negative num '#{value}' not allowed for attribute '#{attribute_name}'" if value < 0
42
+
43
+ value
44
+ rescue ArgumentError
45
+ error_message = "Invalid value '#{value}' provided for attribute '#{attribute_name}'. The value should be a number."
46
+ raise Shoes::Errors::InvalidAttributeValueError, error_message
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Certain Shoes calls like motion and keydown are basically an
4
+ # event subscription, with no other visible presence. However,
5
+ # they have a place in the drawable tree and can be deleted.
6
+ #
7
+ # Depending on the display library they may not have any
8
+ # direct visual (or similar) presence there either.
9
+ #
10
+ # Inheriting from Drawable gives these a parent slot and a
11
+ # linkable_id automatically.
12
+ #
13
+ # Events not yet implemented: start, finish events for slots -
14
+ # start is first draw, finish is drawable destroyed
15
+ class Shoes::SubscriptionItem < Shoes::Drawable
16
+ shoes_styles :shoes_api_name, :args
17
+ shoes_events :animate, :every, :timer, :hover, :leave, :motion, :click, :release, :keypress
18
+
19
+ def initialize(args: [], shoes_api_name:, &block)
20
+ super
21
+
22
+ @callback = block
23
+
24
+ case shoes_api_name
25
+ when "animate"
26
+ @unsub_id = bind_self_event("animate") do |frame|
27
+ @callback.call(frame)
28
+ end
29
+ when "every"
30
+ @unsub_id = bind_self_event("every") do |count|
31
+ @callback.call(count)
32
+ end
33
+ when "timer"
34
+ @unsub_id = bind_self_event("timer") do
35
+ @callback.call
36
+ end
37
+ when "hover"
38
+ # Hover passes the Shoes drawable as the block param
39
+ @unsub_id = bind_self_event("hover") do
40
+ @callback&.call(self)
41
+ end
42
+ when "leave"
43
+ # Leave passes the Shoes drawable as the block param
44
+ @unsub_id = bind_self_event("leave") do
45
+ @callback&.call(self)
46
+ end
47
+ when "motion"
48
+ # Shoes sends back x, y, mods as the args.
49
+ # Shoes3 uses the strings "control" "shift" and
50
+ # "control_shift" as the mods arg.
51
+ @unsub_id = bind_self_event("motion") do |x, y, ctrl_key, shift_key, **_kwargs|
52
+ mods = [ctrl_key ? "control" : nil, shift_key ? "shift" : nil].compact.join("_")
53
+ @callback&.call(x, y, mods)
54
+ end
55
+ when "click"
56
+ # Click has block params button, left, top
57
+ # button is the button number, left and top are coords
58
+ @unsub_id = bind_self_event("click") do |button, x, y, **_kwargs|
59
+ @callback&.call(button, x, y)
60
+ end
61
+ when "release"
62
+ # Click has block params button, left, top
63
+ # button is the button number, left and top are coords
64
+ @unsub_id = bind_self_event("release") do |button, x, y, **_kwargs|
65
+ @callback&.call(button, x, y)
66
+ end
67
+ when "keypress"
68
+ # Keypress passes the key string or symbol to the handler
69
+ # Do anything special for serialisation here?
70
+ @unsub_id = bind_self_event("keypress") do |key|
71
+ @callback&.call(key)
72
+ end
73
+ else
74
+ raise "Unknown Shoes event #{shoes_api_name.inspect} passed to SubscriptionItem!"
75
+ end
76
+
77
+ @unsub_id = bind_self_event(shoes_api_name) do |*args|
78
+ @callback&.call(*args)
79
+ end
80
+
81
+ # This won't create a visible display drawable, but will turn into
82
+ # an invisible drawable and a stream of events.
83
+ create_display_drawable
84
+ end
85
+
86
+ def destroy
87
+ # TODO: we need a better way to do this automatically. See https://github.com/scarpe-team/scarpe/issues/291
88
+ unsub_shoes_event(@unsub_id) if @unsub_id
89
+ @unsub_id = nil
90
+
91
+ super
92
+ end
93
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Shoes
4
+ # TextDrawable is the parent class of various classes of
5
+ # text that can go inside a para. This includes normal
6
+ # text, but also links, italic text, bold text, etc.
7
+ class TextDrawable < Shoes::Drawable
8
+ class << self
9
+ # rubocop:disable Lint/MissingSuper
10
+ def inherited(subclass)
11
+ Shoes::Drawable.drawable_classes ||= []
12
+ Shoes::Drawable.drawable_classes << subclass
13
+
14
+ Shoes::Drawable.drawable_default_styles ||= {}
15
+ Shoes::Drawable.drawable_default_styles[subclass] = {}
16
+ end
17
+ # rubocop:enable Lint/MissingSuper
18
+ end
19
+
20
+ shoes_events # No TextDrawable-specific events yet
21
+ end
22
+
23
+ class << self
24
+ def default_text_drawable_with(element)
25
+ class_name = element.capitalize
26
+
27
+ drawable_class = Class.new(Shoes::TextDrawable) do
28
+ # Can we just change content to text to match the Shoes API?
29
+ shoes_style :content
30
+
31
+ def initialize(content)
32
+ super
33
+
34
+ @content = content
35
+
36
+ create_display_drawable
37
+ end
38
+
39
+ def text
40
+ self.content
41
+ end
42
+
43
+ def to_s
44
+ self.content
45
+ end
46
+
47
+ def text=(new_text)
48
+ self.content = new_text
49
+ end
50
+ end
51
+ Shoes.const_set class_name, drawable_class
52
+ drawable_class.class_eval do
53
+ shoes_style :content
54
+
55
+ shoes_events # No specific events
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ Shoes.default_text_drawable_with(:code)
62
+ Shoes.default_text_drawable_with(:em)
63
+ Shoes.default_text_drawable_with(:strong)
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Shoes
4
+ class Video < Shoes::Drawable
5
+ shoes_styles :url
6
+ shoes_events # No specific events yet
7
+
8
+ def initialize(url)
9
+ super
10
+ @url = url
11
+ create_display_drawable
12
+ end
13
+
14
+ # other methods
15
+ end
16
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ # A Shoes::Widget is mostly a Slot (related to the
4
+ # old Shoes concepts of Canvas) that creates drawables
5
+ # inside itself. When a subclass of Widget is created,
6
+ # it adds a method to create new objects of that type
7
+ # on Shoes::App and all Shoes slots.
8
+ #
9
+ # The hardest part with a Shoes::Widget is that it
10
+ # should work fine even if initialize() doesn't call
11
+ # super, which would make it hard to set up a Shoes
12
+ # linkable_id, create a display widget, etc.
13
+ #
14
+ # It would be possible to add an extra method to set
15
+ # these up and call it on every created drawable
16
+ # in case a Widget's initialize method doesn't call
17
+ # super, which happens quite often. But then we wouldn't
18
+ # support automatic setting of styles (e.g. padding)
19
+ # for the widget object itself, which is mostly a Flow.
20
+ # We also couldn't support default styles -- I can't tell
21
+ # whether Shoes supports these things either.
22
+ #
23
+ # But there's another way to do all of this. When a
24
+ # subclass of Widget defines an initialize method,
25
+ # we can catch the method_added hook, save a copy of
26
+ # that initialize method, and substitute our own
27
+ # initialize that calls super. We have to be a little
28
+ # careful -- if the widget's initialize *does* call
29
+ # super that shouldn't be an error. But that's
30
+ # workable by defining an extra method with the
31
+ # copied-method name that does nothing.
32
+
33
+ ##### TODO: when this is subclassed, grab :initialize out
34
+ # of the subclass, put it into :initialize_widget, and
35
+ # replace with an initialize that creates the display
36
+ # widget propertly, sets the linkable_id, etc.
37
+
38
+ class Shoes::Widget < Shoes::Slot
39
+ include Shoes::Background
40
+ include Shoes::Border
41
+ include Shoes::Spacing
42
+
43
+ shoes_events
44
+
45
+ def self.method_added(name)
46
+ # We're only looking for the initialize() method, and only on subclasses
47
+ # of Shoes::Widget, not Shoes::Widget itself.
48
+ return if self == ::Shoes::Widget || name != :initialize
49
+
50
+ # Need to avoid infinite adding of initialize() if we're re-adding the default initialize
51
+ return if @midway_through_adding_initialize
52
+
53
+ # Take the user-provided initialize method and save a copy named __widget_initialize
54
+ alias_method :__widget_initialize, :initialize
55
+
56
+ # And add the default initialize back where it belongs
57
+ @midway_through_adding_initialize = true
58
+ define_method(:initialize) do |*args, **kwargs, &block|
59
+ super(*args, **kwargs, &block)
60
+ @options = kwargs
61
+ create_display_drawable
62
+ __widget_initialize(*args, **kwargs, &block)
63
+
64
+ # Do Widgets do this?
65
+ Shoes::App.instance.with_slot(self, &block) if block
66
+ end
67
+ @midway_through_adding_initialize = false
68
+ end
69
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "shoes/drawables/slot"
4
+ require "shoes/drawables/stack"
5
+ require "shoes/drawables/flow"
6
+ require "shoes/drawables/widget"
7
+ require "shoes/drawables/document_root"
8
+
9
+ require "shoes/drawables/text_drawable"
10
+
11
+ require "shoes/drawables/subscription_item"
12
+
13
+ require "shoes/drawables/arc"
14
+ require "shoes/drawables/line"
15
+ require "shoes/drawables/rect"
16
+ require "shoes/drawables/shape"
17
+ require "shoes/drawables/star"
18
+ require "shoes/drawables/arrow"
19
+
20
+ require "shoes/drawables/button"
21
+ require "shoes/drawables/check"
22
+ require "shoes/drawables/edit_box"
23
+ require "shoes/drawables/edit_line"
24
+ require "shoes/drawables/image"
25
+ require "shoes/drawables/link"
26
+ require "shoes/drawables/list_box"
27
+ require "shoes/drawables/para"
28
+ require "shoes/drawables/radio"
29
+ require "shoes/drawables/span"
30
+ require "shoes/drawables/video"
31
+ require "shoes/drawables/progress"
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Shoes; end
4
+ class Shoes::Errors
5
+ class InvalidAttributeValueError < Shoes::Error; end
6
+
7
+ class TooManyInstancesError < Shoes::Error; end
8
+
9
+ class NoSuchListItemError < Shoes::Error; end
10
+
11
+ class DuplicateCreateDrawableError < Shoes::Error; end
12
+
13
+ class MultipleDrawablesFoundError < Shoes::Error; end
14
+
15
+ class NoDrawablesFoundError < Shoes::Error; end
16
+
17
+ class NoSuchStyleError < Shoes::Error; end
18
+
19
+ class NoLinkableIdError < Shoes::Error; end
20
+
21
+ class BadLinkableIdError < Shoes::Error; end
22
+
23
+ class UnregisteredShoesEvent < Shoes::Error; end
24
+
25
+ class SingletonError < Shoes::Error; end
26
+
27
+ class MultipleShoesSpecRunsError < Shoes::Error; end
28
+ end
@@ -8,7 +8,7 @@
8
8
  # If used alone, this will fail because the @instance is nil. It needs
9
9
  # an implementation to be plugged in.
10
10
 
11
- module Shoes
11
+ class Shoes
12
12
  LOG_LEVELS = [:debug, :info, :warn, :error, :fatal].freeze
13
13
 
14
14
  # Include this module to get a @log instance variable to log as your
@@ -28,7 +28,7 @@ module Shoes
28
28
  attr_reader :current_log_config
29
29
 
30
30
  def instance=(impl_object)
31
- raise(Shoes::Error, "Already have an instance for Shoes::Log!") if @instance
31
+ raise(Shoes::Errors::TooManyInstancesError, "Already have an instance for Shoes::Log!") if @instance
32
32
 
33
33
  @instance = impl_object
34
34
  end
@@ -0,0 +1,15 @@
1
+
2
+ class Range
3
+ def rand
4
+ conv = (Integer === self.end && Integer === self.begin ? :to_i : :to_f)
5
+ ((Kernel.rand * (self.end - self.begin)) + self.begin).send(conv)
6
+ end
7
+ end
8
+
9
+ unless Time.respond_to? :today
10
+ def Time.today
11
+ t = Time.now
12
+ t - (t.to_i % 86_400)
13
+ end
14
+ end
15
+
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Shoes
3
+ class Shoes
4
4
  module Spacing
5
5
  def self.included(includer)
6
- includer.display_properties :margin, :padding, :margin_top, :margin_left, :margin_right, :margin_bottom, :options
6
+ includer.shoes_styles :margin, :padding, :margin_top, :margin_left, :margin_right, :margin_bottom, :options
7
7
  end
8
8
  end
9
9
  end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Shoes
4
+ # A Scarpe-compatible display service can set Shoes::Spec.instance to
5
+ # a ShoesSpec testing class, and use it to run Shoes-Spec code.
6
+ # A Shoes application should never do this. It's intended to be used
7
+ # by display services.
8
+ module Spec
9
+ def self.instance
10
+ @instance
11
+ end
12
+
13
+ def self.instance=(spec_inst)
14
+ if @instance && @instance != spec_inst
15
+ raise "Lacci can only use a single ShoesSpec implementation at one time!"
16
+ end
17
+
18
+ @instance = spec_inst
19
+ end
20
+ end
21
+
22
+ # ShoesSpec testing objects can optionally inherit from this object,
23
+ # which shows the ShoesSpec testing API.
24
+ #
25
+ # @see {Shoes::Spec.instance=}
26
+ class SpecInstance
27
+ # Once a Shoes app has been created, this method can be called to
28
+ # execute Shoes-Spec testing code for that application. Shoes-Spec
29
+ # uses Minitest for most of its APIs, and Minitest generally reports
30
+ # results with a class name and test name. If those aren't passed
31
+ # explicitly, the SpecInstance can choose reasonable defaults.
32
+ #
33
+ # The test code should be set up to run automatically from the
34
+ # display service's existing hooks. For instance, the code might
35
+ # run in response to the first heartbeat, if the display service
36
+ # uses heartbeats.
37
+ #
38
+ # The test code will export assertion data in its native format.
39
+ # Multiple display services choose to use the Scarpe-Component
40
+ # for Minitest data export, which is straightforward to import
41
+ # into the Shoes-Spec test harness.
42
+ #
43
+ # @param code [String] the ShoesSpec code to execute
44
+ # @param class_name [String|NilClass] the Minitest class name for reporting or nil
45
+ # @param test_name [String|NilClass] the Minitest test name for reporting or nil
46
+ # @return [void]
47
+ def run_shoes_spec_test_code(code, class_name: nil, test_name: nil)
48
+ raise "Child class should override this!"
49
+ end
50
+ end
51
+
52
+ # ShoesSpec instances support finder methods like button() that return
53
+ # a proxy to the corresponding drawable. Those proxies should support
54
+ # standard Shoes::Drawable methods, including the ones appropriate to
55
+ # the same drawable object. They should also support certain other
56
+ # testing-specific methods like "trigger_click" that are used to
57
+ # simulate display-side events during testing.
58
+ #
59
+ # Keep in mind that a proxy will often be in a different process from
60
+ # the Shoes app. So the proxy can't portably return the object or
61
+ # display object, though it could possibly return another proxy for such
62
+ # a thing.
63
+ class SpecProxy
64
+ # The proxy will have finder methods for all drawables, such as
65
+ # button(), edit_line(), etc. How to document those?
66
+
67
+ # Trigger a click on a button or button-like drawable. Not every
68
+ # drawable will support this operation.
69
+ def trigger_click()
70
+ raise "Child class should override this!"
71
+ end
72
+
73
+ # Trigger a hover over a hoverable drawable. Not every
74
+ # drawable will support this operation. A drawable that supports
75
+ # hover should support leave and vice-versa.
76
+ def trigger_hover()
77
+ raise "Child class should override this!"
78
+ end
79
+
80
+ # Trigger ending hover over a hoverable drawable. Not every
81
+ # drawable will support this operation. A drawable that supports
82
+ # hover should support leave and vice-versa.
83
+ def trigger_leave()
84
+ raise "Child class should override this!"
85
+ end
86
+
87
+ # Trigger a change in value for a drawable like a list_box
88
+ # with multiple values.
89
+ def trigger_change(value)
90
+ raise "Child class should override this!"
91
+ end
92
+ end
93
+ end
data/lacci/lib/shoes.rb CHANGED
@@ -12,38 +12,53 @@ if RUBY_VERSION[0..2] < "3.2"
12
12
  exit(-1)
13
13
  end
14
14
 
15
- module Shoes; end
15
+ class Shoes; end
16
16
  class Shoes::Error < StandardError; end
17
+ require_relative "shoes/errors"
17
18
 
18
19
  require_relative "shoes/constants"
20
+ require_relative "shoes/ruby_extensions"
19
21
 
20
22
  # Shoes adds some top-level methods and constants that can be used everywhere. Kernel is where they go.
21
23
  module Kernel
22
24
  include Shoes::Constants
23
25
  end
24
26
 
25
- require_relative "shoes/log"
26
27
  require_relative "shoes/display_service"
28
+
29
+ # Pre-declare classes that get referenced outside their own require file
30
+ class Shoes::Drawable < Shoes::Linkable; end
31
+ class Shoes::Slot < Shoes::Drawable; end
32
+ class Shoes::Widget < Shoes::Slot; end
33
+
34
+ require_relative "shoes/log"
27
35
  require_relative "shoes/colors"
28
36
 
37
+ require_relative "shoes/builtins"
38
+
29
39
  require_relative "shoes/background"
30
40
  require_relative "shoes/border"
31
41
  require_relative "shoes/spacing"
32
42
 
33
- require_relative "shoes/widget"
43
+ require_relative "shoes/drawable"
34
44
  require_relative "shoes/app"
35
- require_relative "shoes/widgets"
45
+ require_relative "shoes/drawables"
36
46
 
37
47
  require_relative "shoes/download"
38
48
 
49
+ # No easy way to tell at this point whether
50
+ # we will later load Shoes-Spec code, e.g.
51
+ # by running a segmented app with test code.
52
+ require_relative "shoes-spec"
53
+
39
54
  # The module containing Shoes in all its glory.
40
55
  # Shoes is a platform-independent GUI library, designed to create
41
56
  # small visual applications in Ruby.
42
57
  #
43
- module Shoes
58
+ class Shoes
44
59
  class << self
45
60
  # Creates a Shoes app with a new window. The block parameter is used to create
46
- # widgets and set up handlers. Arguments are passed to Shoes::App.new internally.
61
+ # drawables and set up handlers. Arguments are passed to Shoes::App.new internally.
47
62
  #
48
63
  # @incompatibility In Shoes3, this method will return normally.
49
64
  # In Scarpe, after the block is executed, the method will not return and Scarpe
@@ -81,11 +96,16 @@ module Shoes
81
96
  # more loaders, a Lacci-based display library can accept new file formats as
82
97
  # well, not just raw Shoes .rb files.
83
98
  #
84
- # @param path [String] The current-dir-relative path to the file
99
+ # @param relative_path [String] The current-dir-relative path to the file
85
100
  # @return [void]
86
101
  # @see Shoes.add_file_loader
87
102
  def run_app(relative_path)
88
103
  path = File.expand_path relative_path
104
+ dir = File.dirname(path)
105
+
106
+ # Shoes assumes we're starting from the app code's path
107
+ Dir.chdir(dir)
108
+
89
109
  loaded = false
90
110
  file_loaders.each do |loader|
91
111
  if loader.call(path)
@@ -3,7 +3,61 @@
3
3
  $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
4
4
  require "shoes"
5
5
 
6
+ require "scarpe/components/unit_test_helpers"
7
+
6
8
  require "minitest/autorun"
7
9
 
8
10
  require "minitest/reporters"
9
11
  Minitest::Reporters.use! [Minitest::Reporters::SpecReporter.new]
12
+
13
+ # For testing Lacci, it's kind of silly to start a Webview application.
14
+ # They're slow, unreliable and memory-hungry. So instead we start a
15
+ # Niente do-nothing application for a simple API test. It's a lot like
16
+ # mocking.
17
+ class NienteTest < Minitest::Test
18
+ include ::Scarpe::Test::Helpers
19
+
20
+ SCARPE_EXE = File.expand_path("../../exe/scarpe", __dir__)
21
+
22
+ def run_test_niente_code(
23
+ scarpe_app_code,
24
+ test_extension: ".rb",
25
+ **opts
26
+ )
27
+ with_tempfile(["scarpe_test_app", test_extension], scarpe_app_code) do |test_app_location|
28
+ run_test_niente_app(test_app_location, **opts)
29
+ end
30
+ end
31
+
32
+ def run_test_niente_app(
33
+ test_app_location,
34
+ app_test_code: "",
35
+ timeout: 5.0,
36
+ class_name: self.class,
37
+ method_name: self.name,
38
+ display_service: "niente"
39
+ )
40
+ with_tempfiles([
41
+ #["scarpe_log_config.json", JSON.dump(log_config_for_test)],
42
+ [["shoes_spec_code", ".rb"], app_test_code],
43
+ ]) do |shoes_spec_path,_|
44
+ system(
45
+ "LOCALAPPDATA=\"#{Dir.tmpdir}\" " +
46
+ "SHOES_SPEC_TEST=\"#{shoes_spec_path}\" " +
47
+ "SCARPE_DISPLAY_SERVICE=\"#{display_service}\" " +
48
+ "SHOES_MINITEST_EXPORT_FILE=niente_test.json " +
49
+ "SHOES_MINITEST_CLASS_NAME=\"#{class_name}\" " +
50
+ "SHOES_MINITEST_METHOD_NAME=\"#{method_name}\" " +
51
+ "ruby #{SCARPE_EXE} --dev #{test_app_location}"
52
+ )
53
+ end
54
+
55
+ # Check if the process exited normally or crashed (segfault, failure, timeout)
56
+ unless $?.success?
57
+ assert(false, "Niente app failed with exit code: #{$?.exitstatus}")
58
+ return
59
+ end
60
+
61
+
62
+ end
63
+ end
@@ -2,8 +2,17 @@
2
2
 
3
3
  require_relative "test_helper"
4
4
 
5
- class TestLacci < Minitest::Test
6
- def test_truth
7
- assert_equal true, true
5
+ class TestLacci < NienteTest
6
+ def test_simple_button_click
7
+ run_test_niente_code(<<~SHOES_APP, app_test_code: <<~SHOES_SPEC)
8
+ Shoes.app do
9
+ @b = button "OK" do
10
+ @b.text = "Yup"
11
+ end
12
+ end
13
+ SHOES_APP
14
+ button().trigger_click
15
+ assert_equal "Yup", button().text
16
+ SHOES_SPEC
8
17
  end
9
18
  end