scarpe 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
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