scarpe 0.2.1 → 0.2.2

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 (240) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -0
  3. data/.yardopts +11 -0
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +112 -0
  6. data/README.md +31 -24
  7. data/Rakefile +13 -1
  8. data/docs/yard/catscradle.md +44 -0
  9. data/docs/yard/template/default/fulldoc/html/setup.rb +13 -0
  10. data/docs/yard/template/default/layout/html/setup.rb +9 -0
  11. data/examples/background_with_image.rb +16 -0
  12. data/examples/bloopsaphone/working/bronx_army_knife.rb +66 -0
  13. data/examples/bloopsaphone/working/morning_serenity.rb +21 -0
  14. data/examples/bloopsaphone/working/simpsons_theme_song_by_why.rb +6 -4
  15. data/examples/button_go_away.rb +1 -1
  16. data/examples/check.rb +18 -0
  17. data/examples/clear_and_append.rb +24 -0
  18. data/examples/download_and_show_image.rb +28 -0
  19. data/examples/edit_box.rb +3 -5
  20. data/examples/fonts.rb +2 -2
  21. data/examples/get_headers.rb +10 -0
  22. data/examples/highlander.rb +2 -0
  23. data/examples/link.rb +2 -2
  24. data/examples/local_fonts.rb +4 -0
  25. data/examples/local_images.rb +4 -0
  26. data/examples/motion_events.rb +20 -0
  27. data/examples/parse_xl_funnies.rb +58 -0
  28. data/examples/radio/radio.rb +16 -0
  29. data/examples/radio/radio_groups.rb +18 -0
  30. data/examples/radio/radio_same_slot.rb +6 -0
  31. data/examples/ruby_racer.rb +13 -15
  32. data/examples/selfitude.rb +18 -0
  33. data/examples/shapes/shapes_fill.rb +4 -3
  34. data/examples/shoes_school.rb +2 -4
  35. data/examples/show_hide.rb +6 -0
  36. data/examples/skip_ci/change_my_audio_source.rb +21 -0
  37. data/examples/skip_ci/guitar_fretboard.rb +137 -0
  38. data/examples/video.rb +10 -0
  39. data/exe/scarpe +42 -66
  40. data/fonts/Pacifico.ttf +0 -0
  41. data/lacci/Gemfile +22 -0
  42. data/lacci/Gemfile.lock +72 -0
  43. data/lacci/Rakefile +12 -0
  44. data/lacci/lacci.gemspec +37 -0
  45. data/lacci/lib/lacci/scarpe_cli.rb +70 -0
  46. data/lacci/lib/lacci/scarpe_core.rb +21 -0
  47. data/lacci/lib/lacci/version.rb +13 -0
  48. data/lacci/lib/shoes/app.rb +264 -0
  49. data/{lib/scarpe → lacci/lib/shoes}/background.rb +1 -1
  50. data/{lib/scarpe → lacci/lib/shoes}/border.rb +1 -1
  51. data/{lib/scarpe → lacci/lib/shoes}/colors.rb +1 -1
  52. data/lacci/lib/shoes/constants.rb +29 -0
  53. data/{lib/scarpe → lacci/lib/shoes}/display_service.rb +40 -45
  54. data/lacci/lib/shoes/download.rb +123 -0
  55. data/lacci/lib/shoes/log.rb +71 -0
  56. data/lacci/lib/shoes/spacing.rb +9 -0
  57. data/{lib/scarpe → lacci/lib/shoes}/widget.rb +63 -43
  58. data/{lib/scarpe → lacci/lib/shoes/widgets}/alert.rb +3 -3
  59. data/{lib/scarpe → lacci/lib/shoes/widgets}/arc.rb +7 -5
  60. data/{lib/scarpe → lacci/lib/shoes/widgets}/button.rb +3 -3
  61. data/lacci/lib/shoes/widgets/check.rb +28 -0
  62. data/lacci/lib/shoes/widgets/document_root.rb +20 -0
  63. data/{lib/scarpe → lacci/lib/shoes/widgets}/edit_box.rb +10 -5
  64. data/{lib/scarpe → lacci/lib/shoes/widgets}/edit_line.rb +2 -2
  65. data/lacci/lib/shoes/widgets/flow.rb +22 -0
  66. data/lacci/lib/shoes/widgets/font.rb +14 -0
  67. data/{lib/scarpe → lacci/lib/shoes/widgets}/image.rb +3 -7
  68. data/lacci/lib/shoes/widgets/line.rb +18 -0
  69. data/{lib/scarpe → lacci/lib/shoes/widgets}/link.rb +2 -2
  70. data/{lib/scarpe → lacci/lib/shoes/widgets}/list_box.rb +2 -2
  71. data/{lib/scarpe → lacci/lib/shoes/widgets}/para.rb +4 -26
  72. data/lacci/lib/shoes/widgets/radio.rb +35 -0
  73. data/lacci/lib/shoes/widgets/shape.rb +37 -0
  74. data/lacci/lib/shoes/widgets/slot.rb +75 -0
  75. data/{lib/scarpe → lacci/lib/shoes/widgets}/span.rb +2 -2
  76. data/lacci/lib/shoes/widgets/stack.rb +24 -0
  77. data/{lib/scarpe → lacci/lib/shoes/widgets}/star.rb +6 -9
  78. data/lacci/lib/shoes/widgets/subscription_item.rb +60 -0
  79. data/lacci/lib/shoes/widgets/text_widget.rb +51 -0
  80. data/lacci/lib/shoes/widgets/video.rb +15 -0
  81. data/lacci/lib/shoes/widgets.rb +29 -0
  82. data/lacci/lib/shoes.rb +127 -0
  83. data/lacci/test/test_colors.rb +39 -0
  84. data/lacci/test/test_helper.rb +9 -0
  85. data/lacci/test/test_lacci.rb +9 -0
  86. data/lib/scarpe/cats_cradle.rb +249 -0
  87. data/lib/scarpe/evented_assertions.rb +88 -0
  88. data/lib/scarpe/version.rb +1 -1
  89. data/lib/scarpe/wv/alert.rb +3 -2
  90. data/lib/scarpe/wv/app.rb +30 -8
  91. data/lib/scarpe/wv/arc.rb +5 -6
  92. data/lib/scarpe/wv/background.rb +10 -1
  93. data/lib/scarpe/wv/border.rb +5 -3
  94. data/lib/scarpe/wv/button.rb +11 -9
  95. data/lib/scarpe/wv/check.rb +29 -0
  96. data/lib/scarpe/wv/control_interface.rb +14 -20
  97. data/lib/scarpe/wv/control_interface_test.rb +13 -28
  98. data/lib/scarpe/wv/document_root.rb +3 -45
  99. data/lib/scarpe/wv/edit_box.rb +5 -7
  100. data/lib/scarpe/wv/edit_line.rb +2 -2
  101. data/lib/scarpe/wv/flow.rb +10 -20
  102. data/lib/scarpe/wv/font.rb +36 -0
  103. data/lib/scarpe/wv/html.rb +3 -2
  104. data/lib/scarpe/wv/image.rb +7 -2
  105. data/lib/scarpe/wv/line.rb +4 -7
  106. data/lib/scarpe/wv/link.rb +1 -0
  107. data/lib/scarpe/wv/list_box.rb +3 -3
  108. data/lib/scarpe/wv/para.rb +16 -14
  109. data/lib/scarpe/wv/radio.rb +34 -0
  110. data/lib/scarpe/wv/shape.rb +44 -8
  111. data/lib/scarpe/wv/slot.rb +81 -0
  112. data/lib/scarpe/wv/spacing.rb +1 -1
  113. data/lib/scarpe/wv/span.rb +10 -8
  114. data/lib/scarpe/wv/stack.rb +10 -30
  115. data/lib/scarpe/wv/star.rb +11 -12
  116. data/lib/scarpe/wv/subscription_item.rb +50 -0
  117. data/lib/scarpe/wv/video.rb +34 -0
  118. data/lib/scarpe/wv/web_wrangler.rb +238 -58
  119. data/lib/scarpe/wv/webview_local_display.rb +27 -5
  120. data/lib/scarpe/wv/webview_relay_display.rb +18 -119
  121. data/lib/scarpe/wv/webview_relay_util.rb +143 -0
  122. data/lib/scarpe/wv/widget.rb +80 -11
  123. data/lib/scarpe/wv/wv_display_worker.rb +17 -4
  124. data/lib/scarpe/wv.rb +33 -4
  125. data/lib/scarpe/wv_local.rb +1 -1
  126. data/lib/scarpe/wv_relay.rb +1 -1
  127. data/lib/scarpe.rb +3 -32
  128. data/scarpe-components/.gitignore +1 -0
  129. data/scarpe-components/Gemfile +22 -0
  130. data/scarpe-components/README.md +35 -0
  131. data/scarpe-components/Rakefile +12 -0
  132. data/scarpe-components/lib/scarpe/components/base64.rb +29 -0
  133. data/scarpe-components/lib/scarpe/components/file_helpers.rb +65 -0
  134. data/scarpe-components/lib/scarpe/components/modular_logger.rb +113 -0
  135. data/scarpe-components/lib/scarpe/components/print_logger.rb +43 -0
  136. data/{lib/scarpe → scarpe-components/lib/scarpe/components}/promises.rb +102 -35
  137. data/scarpe-components/lib/scarpe/components/segmented_file_loader.rb +170 -0
  138. data/scarpe-components/lib/scarpe/components/unit_test_helpers.rb +217 -0
  139. data/scarpe-components/lib/scarpe/components/version.rb +7 -0
  140. data/scarpe-components/scarpe-components.gemspec +38 -0
  141. data/scarpe-components/test/test_components.rb +9 -0
  142. data/scarpe-components/test/test_helper.rb +23 -0
  143. data/scarpe-components/test/test_promises.rb +260 -0
  144. data/scarpe-components/test/test_segmented_app_files.rb +182 -0
  145. data/{lib/scarpe → spikes}/glibui/widget.rb +2 -2
  146. data/{lib/scarpe → spikes}/glibui.rb +1 -1
  147. data/templates/basic_class_template.erb +1 -1
  148. data/templates/class_template_with_event_bind.erb +1 -1
  149. data/templates/class_template_with_shapes.erb +1 -1
  150. data/templates/webview_template.erb +0 -3
  151. metadata +151 -118
  152. data/examples/fill.rb +0 -25
  153. data/examples/legacy/not_checked/shoes-contrib/basic/class-book.yaml +0 -387
  154. data/examples/legacy/not_checked/shoes-contrib/good/good-clock.rb +0 -51
  155. data/examples/legacy/not_checked/shoes-contrib/good/good-follow.rb +0 -26
  156. data/examples/legacy/not_checked/shoes-contrib/good/good-reminder.rb +0 -174
  157. data/examples/legacy/not_checked/shoes-contrib/good/good-vjot.rb +0 -56
  158. data/examples/legacy/not_checked/shoes-contrib/simple/simple-timer.rb +0 -13
  159. data/examples/legacy/not_checked/shoes-dep-samples/good-clock.rb +0 -51
  160. data/examples/legacy/not_checked/shoes-dep-samples/good-follow.rb +0 -26
  161. data/examples/legacy/not_checked/shoes-dep-samples/good-reminder.rb +0 -174
  162. data/examples/legacy/not_checked/shoes-dep-samples/good-vjot.rb +0 -56
  163. data/examples/legacy/not_checked/shoes-dep-samples/simple-accordion.rb +0 -75
  164. data/examples/legacy/not_checked/shoes-dep-samples/simple-anim-shapes.rb +0 -17
  165. data/examples/legacy/not_checked/shoes-dep-samples/simple-anim-text.rb +0 -13
  166. data/examples/legacy/not_checked/shoes-dep-samples/simple-arc.rb +0 -23
  167. data/examples/legacy/not_checked/shoes-dep-samples/simple-bounce.rb +0 -24
  168. data/examples/legacy/not_checked/shoes-dep-samples/simple-calc.rb +0 -70
  169. data/examples/legacy/not_checked/shoes-dep-samples/simple-chipmunk.rb +0 -26
  170. data/examples/legacy/not_checked/shoes-dep-samples/simple-control-sizes.rb +0 -24
  171. data/examples/legacy/not_checked/shoes-dep-samples/simple-curve.rb +0 -26
  172. data/examples/legacy/not_checked/shoes-dep-samples/simple-dialogs.rb +0 -29
  173. data/examples/legacy/not_checked/shoes-dep-samples/simple-draw.rb +0 -13
  174. data/examples/legacy/not_checked/shoes-dep-samples/simple-editor.rb +0 -28
  175. data/examples/legacy/not_checked/shoes-dep-samples/simple-form.rb +0 -28
  176. data/examples/legacy/not_checked/shoes-dep-samples/simple-form.shy +0 -0
  177. data/examples/legacy/not_checked/shoes-dep-samples/simple-mask.rb +0 -21
  178. data/examples/legacy/not_checked/shoes-dep-samples/simple-menu.rb +0 -31
  179. data/examples/legacy/not_checked/shoes-dep-samples/simple-menu1.rb +0 -35
  180. data/examples/legacy/not_checked/shoes-dep-samples/simple-rubygems.rb +0 -29
  181. data/examples/legacy/not_checked/shoes-dep-samples/simple-slide.rb +0 -45
  182. data/examples/legacy/not_checked/shoes-dep-samples/simple-sphere.rb +0 -28
  183. data/examples/legacy/not_checked/shoes-dep-samples/simple-sqlite3.rb +0 -13
  184. data/examples/legacy/not_checked/shoes-dep-samples/simple-timer.rb +0 -13
  185. data/examples/legacy/not_checked/shoes-dep-samples/simple-video.rb +0 -13
  186. data/examples/legacy/not_checked/simple/anim-text.rb +0 -13
  187. data/examples/legacy/not_checked/simple/arc.rb +0 -23
  188. data/examples/legacy/not_checked/simple/bounce.rb +0 -24
  189. data/examples/legacy/not_checked/simple/chipmunk.rb +0 -26
  190. data/examples/legacy/not_checked/simple/curve.rb +0 -26
  191. data/examples/legacy/not_checked/simple/dialogs.rb +0 -29
  192. data/examples/legacy/not_checked/simple/downloader.rb +0 -40
  193. data/examples/legacy/not_checked/simple/draw.rb +0 -13
  194. data/examples/legacy/not_checked/simple/mask.rb +0 -21
  195. data/examples/legacy/not_checked/simple/slide.rb +0 -45
  196. data/examples/legacy/not_checked/simple/sphere.rb +0 -28
  197. data/lib/constants.rb +0 -5
  198. data/lib/scarpe/app.rb +0 -78
  199. data/lib/scarpe/document_root.rb +0 -20
  200. data/lib/scarpe/fill.rb +0 -23
  201. data/lib/scarpe/flow.rb +0 -19
  202. data/lib/scarpe/line.rb +0 -25
  203. data/lib/scarpe/logger.rb +0 -155
  204. data/lib/scarpe/shape.rb +0 -19
  205. data/lib/scarpe/spacing.rb +0 -9
  206. data/lib/scarpe/stack.rb +0 -70
  207. data/lib/scarpe/text_widget.rb +0 -42
  208. data/lib/scarpe/unit_test_helpers.rb +0 -163
  209. data/lib/scarpe/widgets.rb +0 -30
  210. data/lib/scarpe/wv/fill.rb +0 -30
  211. data/lib/scarpe/wv/shape_helper.rb +0 -44
  212. data/scarpe-0.2.0.gem +0 -0
  213. /data/{lib/scarpe → spikes}/glibui/README.md +0 -0
  214. /data/{lib/scarpe → spikes}/glibui/alert.rb +0 -0
  215. /data/{lib/scarpe → spikes}/glibui/app.rb +0 -0
  216. /data/{lib/scarpe → spikes}/glibui/background.rb +0 -0
  217. /data/{lib/scarpe → spikes}/glibui/border.rb +0 -0
  218. /data/{lib/scarpe → spikes}/glibui/button.rb +0 -0
  219. /data/{lib/scarpe → spikes}/glibui/dimensions.rb +0 -0
  220. /data/{lib/scarpe → spikes}/glibui/document_root.rb +0 -0
  221. /data/{lib/scarpe → spikes}/glibui/edit_box.rb +0 -0
  222. /data/{lib/scarpe → spikes}/glibui/edit_line.rb +0 -0
  223. /data/{lib/scarpe → spikes}/glibui/flow.rb +0 -0
  224. /data/{lib/scarpe → spikes}/glibui/html.rb +0 -0
  225. /data/{lib/scarpe → spikes}/glibui/image.rb +0 -0
  226. /data/{lib/scarpe → spikes}/glibui/link.rb +0 -0
  227. /data/{lib/scarpe → spikes}/glibui/local_display.rb +0 -0
  228. /data/{lib/scarpe → spikes}/glibui/para.rb +0 -0
  229. /data/{lib/scarpe → spikes}/glibui/spacing.rb +0 -0
  230. /data/{lib/scarpe → spikes}/glibui/stack.rb +0 -0
  231. /data/{lib/scarpe → spikes}/glibui/text_widget.rb +0 -0
  232. /data/{lib/scarpe → spikes}/libui/alert.rb +0 -0
  233. /data/{lib/scarpe → spikes}/libui/button.rb +0 -0
  234. /data/{lib/scarpe → spikes}/libui/colors.rb +0 -0
  235. /data/{lib/scarpe → spikes}/libui/core.rb +0 -0
  236. /data/{lib/scarpe → spikes}/libui/flow.rb +0 -0
  237. /data/{lib/scarpe → spikes}/libui/libui.rb +0 -0
  238. /data/{lib/scarpe → spikes}/libui/notepad.md +0 -0
  239. /data/{lib/scarpe → spikes}/libui/para.rb +0 -0
  240. /data/{lib/scarpe → spikes}/libui/stack.rb +0 -0
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/lacci/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "lacci"
7
+ spec.version = Lacci::VERSION
8
+ spec.authors = ["Marco Concetto Rudilosso", "Noah Gibbs"]
9
+ spec.email = ["marcoc.r@outlook.com", "the.codefolio.guy@gmail.com"]
10
+
11
+ spec.summary = "Lacci - a portable Shoes DSL with switchable display backends, part of Scarpe"
12
+ spec.homepage = "https://github.com/scarpe-team/scarpe"
13
+ spec.license = "MIT"
14
+ spec.required_ruby_version = ">= 3.2.0"
15
+
16
+ # spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
17
+
18
+ spec.metadata["homepage_uri"] = spec.homepage
19
+ spec.metadata["source_code_uri"] = "https://github.com/scarpe-team/scarpe"
20
+ spec.metadata["changelog_uri"] = "https://github.com/scarpe-team/scarpe/blob/main/CHANGELOG.md"
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
+ `git ls-files -z`.split("\x0").reject do |f|
26
+ (f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
27
+ end
28
+ end
29
+ spec.bindir = "exe"
30
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ["lib"]
32
+
33
+ #spec.add_dependency ""
34
+
35
+ # For more information and examples about making a new gem, check out our
36
+ # guide at: https://bundler.io/guides/creating_gem.html
37
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Scarpe
4
+ module CLI
5
+ DEFAULT_USAGE = <<~'USAGE'
6
+ Usage: scarpe_core [OPTIONS] <scarpe app file> # Same as "scarpe run"
7
+ scarpe_core [OPTIONS] run <scarpe app file>
8
+ scarpe_core [OPTIONS] env # print Scarpe environment settings
9
+ scarpe_core -v # print the Scarpe gem version and exit
10
+ Options:
11
+ --dev Use development local scarpe, not an installed gem
12
+ --debug Turn on application debug mode
13
+ USAGE
14
+
15
+ def version_check
16
+ if RUBY_VERSION[0..2] < "3.2"
17
+ raise "Scarpe and Lacci require Ruby 3.2 or higher!"
18
+ end
19
+ end
20
+
21
+ def env_or_default(env_var, default_val)
22
+ [env_var, ENV[env_var] ? ENV[env_var].inspect : default_val]
23
+ end
24
+
25
+ def default_env_categories
26
+ {
27
+ "Lacci" => [
28
+ env_or_default("SCARPE_DISPLAY_SERVICE", "(none)"),
29
+ env_or_default("SCARPE_LOG_CONFIG", "(default)#{Shoes::Log::DEFAULT_LOG_CONFIG.inspect}"),
30
+ env_or_default("SCARPE_APP_TEST", "(none)"),
31
+ ],
32
+ "Ruby and Shell" => [
33
+ ["RUBY_DESCRIPTION", RUBY_DESCRIPTION],
34
+ ["RUBY_PLATFORM", RUBY_PLATFORM],
35
+ ["RUBY_ENGINE", RUBY_ENGINE],
36
+ env_or_default("SHELL", "(none)"),
37
+ env_or_default("PATH", "(none)"),
38
+ env_or_default("LD_LIBRARY_PATH", "(none)"),
39
+ env_or_default("DYLD_LIBRARY_PATH", "(none)"),
40
+ env_or_default("GEM_ROOT", "(none)"),
41
+ env_or_default("GEM_HOME", "(none)"),
42
+ env_or_default("GEM_PATH", "(none)"),
43
+ ],
44
+ }
45
+ end
46
+
47
+ def env_categories
48
+ @env_categories ||= default_env_categories
49
+ @env_categories
50
+ end
51
+
52
+ def add_env_categories(categories)
53
+ unless categories.is_a?(Hash)
54
+ raise("Please supply a hash with categories names as keys and an array of two-elt arrays as values!")
55
+ end
56
+
57
+ # Try to get categories into the *start* of the hash, insertion-order-wise
58
+ @env_categories = categories.merge(env_categories)
59
+ end
60
+
61
+ def print_env
62
+ env_categories.each do |category, entries|
63
+ puts "#{category} environment:"
64
+ entries.each do |name, val|
65
+ puts " #{name}: #{val}"
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Lacci is one place we put components that have no hard gem requirements.
4
+ # It needs to connect to Scarpe, mostly via the Shoes DisplayService.
5
+ # Some display services, like Wasm, cannot install or otherwise tolerate
6
+ # Scarpe's need for a variety of gems, especially those with native extensions.
7
+ # So we need a simple require-able Scarpe core here in Lacci that can be
8
+ # required from display services that cannot link with the base Scarpe gem.
9
+
10
+ if RUBY_VERSION[0..2] < "3.2"
11
+ Shoes::Log.logger("Scarpe").error("Scarpe requires Ruby 3.2 or higher!")
12
+ exit(-1)
13
+ end
14
+
15
+ class Scarpe; end
16
+
17
+ # The base error class for Scarpe errors, but not necessarily {Shoes::Error}s
18
+ class Scarpe::Error < StandardError; end
19
+
20
+ require "lacci/version"
21
+ require "scarpe/components/version"
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The name "scarpe" means "shoes" in Italian. "Lacci"
4
+ # means "laces." Lacci is the display-independent part
5
+ # of Scarpe, tying the Shoes API to the display service
6
+ # of your choice.
7
+ #
8
+ # Almost nothing is called Lacci in your program. It's
9
+ # mostly invisible. Instead, look at the {Shoes} module
10
+ # to see what's in Lacci.
11
+ module Lacci
12
+ VERSION = "0.2.2"
13
+ end
@@ -0,0 +1,264 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shoes
4
+ class App < Shoes::Widget
5
+ include Shoes::Log
6
+
7
+ class << self
8
+ attr_accessor :instance
9
+ end
10
+
11
+ attr_reader :document_root
12
+
13
+ display_properties :title, :width, :height, :resizable
14
+
15
+ CUSTOM_EVENT_LOOP_TYPES = ["displaylib", "return", "wait"]
16
+
17
+ def initialize(
18
+ title: "Shoes!",
19
+ width: 480,
20
+ height: 420,
21
+ resizable: true,
22
+ &app_code_body
23
+ )
24
+ log_init("Shoes::App")
25
+
26
+ if Shoes::App.instance
27
+ @log.error("Trying to create a second Shoes::App in the same process! Fail!")
28
+ raise "Cannot create multiple Shoes::App objects!"
29
+ else
30
+ Shoes::App.instance = self
31
+ end
32
+
33
+ @do_shutdown = false
34
+ @event_loop_type = "displaylib" # the default
35
+
36
+ super
37
+
38
+ # The draw context tracks current settings like fill and stroke,
39
+ # plus potentially other current state that changes from widget
40
+ # to widget and slot to slot.
41
+ @draw_context = {
42
+ "fill" => "",
43
+ "stroke" => "",
44
+ }
45
+
46
+ # This creates the DocumentRoot, including its corresponding display widget
47
+ @document_root = Shoes::DocumentRoot.new
48
+
49
+ @slots = []
50
+
51
+ # Now create the App display widget
52
+ create_display_widget
53
+
54
+ # Set up testing events *after* Display Service basic objects exist
55
+ if ENV["SCARPE_APP_TEST"]
56
+ test_code = File.read ENV["SCARPE_APP_TEST"]
57
+ if test_code != ""
58
+ self.instance_eval test_code
59
+ end
60
+ end
61
+
62
+ @app_code_body = app_code_body
63
+
64
+ # Try to de-dup as much as possible and not send repeat or multiple
65
+ # destroy events
66
+ @watch_for_destroy = bind_shoes_event(event_name: "destroy") do
67
+ Shoes::DisplayService.unsub_from_events(@watch_for_destroy) if @watch_for_destroy
68
+ @watch_for_destroy = nil
69
+ self.destroy(send_event: false)
70
+ end
71
+
72
+ @watch_for_event_loop = bind_shoes_event(event_name: "custom_event_loop") do |loop_type|
73
+ raise("Unknown event loop type: #{loop_type.inspect}!") unless CUSTOM_EVENT_LOOP_TYPES.include?(loop_type)
74
+
75
+ @event_loop_type = loop_type
76
+ end
77
+
78
+ Signal.trap("INT") do
79
+ @log.warn("App interrupted by signal, stopping...")
80
+ puts "\nStopping Shoes app..."
81
+ destroy
82
+ end
83
+ end
84
+
85
+ def init
86
+ send_shoes_event(event_name: "init")
87
+ return if @do_shutdown
88
+
89
+ ::Shoes::App.instance.with_slot(@document_root, &@app_code_body)
90
+ end
91
+
92
+ # "Container" widgets like flows, stacks, masks and the document root
93
+ # are considered "slots" in Shoes parlance. When a new slot is created,
94
+ # we push it here in order to track what widgets are found in that slot.
95
+ def push_slot(slot)
96
+ @slots.push(slot)
97
+ end
98
+
99
+ def pop_slot
100
+ return if @slots.size <= 1
101
+
102
+ @slots.pop
103
+ end
104
+
105
+ def current_slot
106
+ @slots[-1]
107
+ end
108
+
109
+ def with_slot(slot_item, &block)
110
+ return unless block_given?
111
+
112
+ push_slot(slot_item)
113
+ Shoes::App.instance.instance_eval(&block)
114
+ ensure
115
+ pop_slot
116
+ end
117
+
118
+ def current_draw_context
119
+ @draw_context.dup
120
+ end
121
+
122
+ # This usually doesn't return. The display service may take control
123
+ # of the main thread. Local Webview even stops any background threads.
124
+ # However, some display libraries don't want to shut down and don't
125
+ # want to (and/or can't) take control of the event loop.
126
+ def run
127
+ if @do_shutdown
128
+ $stderr.puts "Destroy has already been signaled, but we just called Shoes::App.run!"
129
+ return
130
+ end
131
+
132
+ # The display lib can send us an event to customise the event loop handling.
133
+ # But it must do so before the "run" event returns.
134
+ send_shoes_event(event_name: "run")
135
+
136
+ case @event_loop_type
137
+ when "wait"
138
+ # Display lib wants us to busy-wait instead of it.
139
+ sleep 0.1 until @do_shutdown
140
+ when "displaylib"
141
+ # If run event returned, that means we're done.
142
+ destroy
143
+ when "return"
144
+ # We can just return to the main event loop. But we shouldn't call destroy.
145
+ # Presumably some event loop *outside* our event loop is handling things.
146
+ else
147
+ raise "Internal error! Incorrect event loop type: #{@event_loop_type.inspect}!"
148
+ end
149
+ end
150
+
151
+ def destroy(send_event: true)
152
+ @do_shutdown = true
153
+ send_shoes_event(event_name: "destroy") if send_event
154
+ end
155
+
156
+ def all_widgets
157
+ out = []
158
+
159
+ to_add = @document_root.children
160
+ until to_add.empty?
161
+ out.concat(to_add)
162
+ to_add = to_add.flat_map { |w| w.respond_to?(:children) ? w.children : [] }.compact
163
+ end
164
+
165
+ out
166
+ end
167
+
168
+ # We can add various ways to find widgets here.
169
+ # These are sort of like Shoes selectors, used for testing.
170
+ def find_widgets_by(*specs)
171
+ widgets = all_widgets
172
+ specs.each do |spec|
173
+ if spec.is_a?(Class)
174
+ widgets.select! { |w| spec === w }
175
+ elsif spec.is_a?(Symbol) || spec.is_a?(String)
176
+ s = spec.to_s
177
+ case s[0]
178
+ when "$"
179
+ begin
180
+ # I'm not finding a global_variable_get or similar...
181
+ global_value = eval s
182
+ widgets &= [global_value]
183
+ rescue
184
+ raise "Error getting global variable: #{spec.inspect}"
185
+ end
186
+ when "@"
187
+ if Shoes::App.instance.instance_variables.include?(spec.to_sym)
188
+ widgets &= [self.instance_variable_get(spec)]
189
+ else
190
+ raise "Can't find top-level instance variable: #{spec.inspect}!"
191
+ end
192
+ else
193
+ end
194
+ else
195
+ raise("Don't know how to find widgets by #{spec.inspect}!")
196
+ end
197
+ end
198
+ widgets
199
+ end
200
+ end
201
+ end
202
+
203
+ # DSL methods
204
+ class Shoes::App
205
+ def background(...)
206
+ current_slot.background(...)
207
+ end
208
+
209
+ def border(...)
210
+ current_slot.border(...)
211
+ end
212
+
213
+ def motion(&block)
214
+ subscription_item(shoes_api_name: "motion", &block)
215
+ end
216
+
217
+ def hover(&block)
218
+ subscription_item(shoes_api_name: "hover", &block)
219
+ end
220
+
221
+ def click(&block)
222
+ subscription_item(shoes_api_name: "click", &block)
223
+ end
224
+
225
+ # Draw context methods
226
+
227
+ def fill(color)
228
+ @draw_context["fill"] = color
229
+ end
230
+
231
+ def nofill
232
+ @draw_context["fill"] = ""
233
+ end
234
+
235
+ def stroke(color)
236
+ @draw_context["stroke"] = color
237
+ end
238
+
239
+ def nostroke
240
+ @draw_context["stroke"] = ""
241
+ end
242
+
243
+ # Shape DSL methods
244
+
245
+ def move_to(x, y)
246
+ raise("Pass only Numeric arguments to move_to!") unless x.is_a?(Numeric) && y.is_a?(Numeric)
247
+
248
+ if current_slot.is_a?(::Shoes::Shape)
249
+ current_slot.add_shape_command(["move_to", x, y])
250
+ end
251
+ end
252
+
253
+ def line_to(x, y)
254
+ raise("Pass only Numeric arguments to line_to!") unless x.is_a?(Numeric) && y.is_a?(Numeric)
255
+
256
+ if current_slot.is_a?(::Shoes::Shape)
257
+ current_slot.add_shape_command(["line_to", x, y])
258
+ end
259
+ end
260
+
261
+ # Not implemented yet: curve_to, arc_to
262
+
263
+ alias_method :info, :puts
264
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Scarpe
3
+ module Shoes
4
4
  module Background
5
5
  def self.included(includer)
6
6
  includer.display_property(:background_color)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Scarpe
3
+ module Shoes
4
4
  module Border
5
5
  def self.included(includer)
6
6
  includer.display_properties :border_color, :options
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Scarpe
3
+ module Shoes
4
4
  module Colors
5
5
  COLORS = {
6
6
  aliceblue: [240, 248, 255],
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shoes
4
+ module Constants
5
+ def self.find_lib_dir
6
+ begin
7
+ require "tmpdir"
8
+ rescue LoadError
9
+ return nil
10
+ end
11
+ homes = [
12
+ [ENV["LOCALAPPDATA"], "Shoes"],
13
+ [ENV["APPDATA"], "Shoes"],
14
+ [ENV["HOME"], ".shoes"],
15
+ [Dir.tmpdir, "shoes"],
16
+ ]
17
+ top, file = homes.detect { |home_top, _| home_top && File.exist?(home_top) }
18
+ File.join(top, file)
19
+ end
20
+
21
+ LIB_DIR = find_lib_dir
22
+
23
+ # Math constants from Shoes3
24
+ RAD2PI = 0.01745329251994329577
25
+ TWO_PI = 6.28318530717958647693
26
+ HALF_PI = 1.57079632679489661923
27
+ PI = 3.14159265358979323846
28
+ end
29
+ end
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Scarpe Shoes apps operate in multiple layers. A Shoes widget tree exists as fairly
3
+ # Lacci Shoes apps operate in multiple layers. A Shoes widget tree exists as fairly
4
4
  # plain, simple Ruby objects. And then a display-service widget tree integrates with
5
- # the display technology, initially Webview. This lets us use Ruby as our API while
6
- # not tying it too closely to the limitations of Webview.
5
+ # the display technology. This lets us use Ruby as our API while
6
+ # not tying it too closely to the limitations of Webview, WASM, LibUI, etc.
7
7
  #
8
8
  # ## Choosing Display Services
9
9
  #
10
- # Before running a Scarpe app, you can set SCARPE_DISPLAY_SERVICE. If you
10
+ # Before running a Lacci app, you can set SCARPE_DISPLAY_SERVICE. If you
11
11
  # set it to "whatever_service", Scarpe will require "scarpe/whatever_service",
12
12
  # which can be supplied by the Scarpe gem or another Scarpe-based gem.
13
13
  # Currently leaving the environment variable empty is equivalent to requesting
@@ -17,7 +17,7 @@
17
17
  #
18
18
  # Events are a lot of what tie the Shoes widgets and the display service together.
19
19
  #
20
- # Scarpe widgets *expect* to operate in a fairly "hands off" mode where they record
20
+ # Shoes widgets *expect* to operate in a fairly "hands off" mode where they record
21
21
  # to an event queue to send to the display service, and the display service records
22
22
  # events to send back.
23
23
  #
@@ -26,25 +26,11 @@
26
26
  # And when a display-side event occurs (e.g. user pushes a button,) it will be
27
27
  # dispatched as a :shoes event, to be sent to the Shoes tree of widgets.
28
28
  #
29
- # It is normally assumed that there will only be a single 'real' consumer for each
30
- # queue. You wouldn't usually want multiple display services sending back click
31
- # events, for example. But that's not a technical limitation of the system.
32
- # You could have no display service and not get any events back, or a debug
33
- # display service that just records what happened. But in real situations,
34
- # exactly one display service is a reasonable and appropriate assumption.
35
- #
36
- # The event types are "queues" in the sense that they're separate from each
37
- # other. You need to subscribe separately to :shoes and :display events if
38
- # you want both. But internally they're immediately dispatched as events
39
- # rather than keeping a literal array of items.
40
- #
41
- class Scarpe
29
+ module Shoes
42
30
  class DisplayService
43
31
  class << self
44
32
  # This is in the eigenclass/metaclass, *not* instances of DisplayService
45
- include Scarpe::Log
46
-
47
- attr_accessor :json_debug_serialize
33
+ include Shoes::Log
48
34
 
49
35
  # An event_target may be nil, to indicate there is no target.
50
36
  def dispatch_event(event_name, event_target, *args, **kwargs)
@@ -58,7 +44,8 @@ class Scarpe
58
44
 
59
45
  @log.debug("Dispatch event: #{event_name.inspect} T: #{event_target.inspect} A: #{args.inspect} KW: #{kwargs.inspect}")
60
46
 
61
- if DisplayService.json_debug_serialize
47
+ # When true, this makes sure all events and properties are 100% strings, no symbols.
48
+ if ENV["SCARPE_DEBUG"]
62
49
  args = JSON.parse JSON.dump(args)
63
50
  new_kw = {}
64
51
  kwargs.each do |k, v|
@@ -139,29 +126,6 @@ class Scarpe
139
126
  end
140
127
  end
141
128
 
142
- # This is for objects that can be referred to via events, using their
143
- # IDs. There are also convenience functions for binding and sending
144
- # events.
145
- class Linkable
146
- attr_reader :linkable_id
147
-
148
- def initialize(linkable_id: object_id)
149
- @linkable_id = linkable_id
150
- end
151
-
152
- def send_self_event(*args, event_name:, **kwargs)
153
- DisplayService.dispatch_event(event_name, self.linkable_id, *args, **kwargs)
154
- end
155
-
156
- def send_shoes_event(*args, event_name:, target: nil, **kwargs)
157
- DisplayService.dispatch_event(event_name, target, *args, **kwargs)
158
- end
159
-
160
- def bind_shoes_event(event_name:, target: nil, &handler)
161
- DisplayService.subscribe_to_event(event_name, target, &handler)
162
- end
163
- end
164
-
165
129
  # These methods are an interface to DisplayService objects.
166
130
 
167
131
  def create_display_widget_for(widget_class_name, widget_id, properties)
@@ -186,4 +150,35 @@ class Scarpe
186
150
  raise "Override in DisplayService implementation!"
187
151
  end
188
152
  end
153
+
154
+ # This is for objects that can be referred to via events, using their
155
+ # IDs. There are also convenience functions for binding and sending
156
+ # events.
157
+ #
158
+ # Linkable objects may be event targets. Technically anything, linkable
159
+ # or not, can be an event subscriber, but linkables get easy convenience
160
+ # functions for subscription.
161
+ class Linkable
162
+ attr_reader :linkable_id
163
+
164
+ def initialize(linkable_id: object_id)
165
+ @linkable_id = linkable_id
166
+ end
167
+
168
+ def send_self_event(*args, event_name:, **kwargs)
169
+ DisplayService.dispatch_event(event_name, self.linkable_id, *args, **kwargs)
170
+ end
171
+
172
+ def send_shoes_event(*args, event_name:, target: nil, **kwargs)
173
+ DisplayService.dispatch_event(event_name, target, *args, **kwargs)
174
+ end
175
+
176
+ def bind_shoes_event(event_name:, target: nil, &handler)
177
+ DisplayService.subscribe_to_event(event_name, target, &handler)
178
+ end
179
+
180
+ def unsub_shoes_event(unsub_id)
181
+ DisplayService.unsub_from_events(unsub_id)
182
+ end
183
+ end
189
184
  end