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.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -0
- data/.yardopts +11 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +112 -0
- data/README.md +31 -24
- data/Rakefile +13 -1
- data/docs/yard/catscradle.md +44 -0
- data/docs/yard/template/default/fulldoc/html/setup.rb +13 -0
- data/docs/yard/template/default/layout/html/setup.rb +9 -0
- data/examples/background_with_image.rb +16 -0
- data/examples/bloopsaphone/working/bronx_army_knife.rb +66 -0
- data/examples/bloopsaphone/working/morning_serenity.rb +21 -0
- data/examples/bloopsaphone/working/simpsons_theme_song_by_why.rb +6 -4
- data/examples/button_go_away.rb +1 -1
- data/examples/check.rb +18 -0
- data/examples/clear_and_append.rb +24 -0
- data/examples/download_and_show_image.rb +28 -0
- data/examples/edit_box.rb +3 -5
- data/examples/fonts.rb +2 -2
- data/examples/get_headers.rb +10 -0
- data/examples/highlander.rb +2 -0
- data/examples/link.rb +2 -2
- data/examples/local_fonts.rb +4 -0
- data/examples/local_images.rb +4 -0
- data/examples/motion_events.rb +20 -0
- data/examples/parse_xl_funnies.rb +58 -0
- data/examples/radio/radio.rb +16 -0
- data/examples/radio/radio_groups.rb +18 -0
- data/examples/radio/radio_same_slot.rb +6 -0
- data/examples/ruby_racer.rb +13 -15
- data/examples/selfitude.rb +18 -0
- data/examples/shapes/shapes_fill.rb +4 -3
- data/examples/shoes_school.rb +2 -4
- data/examples/show_hide.rb +6 -0
- data/examples/skip_ci/change_my_audio_source.rb +21 -0
- data/examples/skip_ci/guitar_fretboard.rb +137 -0
- data/examples/video.rb +10 -0
- data/exe/scarpe +42 -66
- data/fonts/Pacifico.ttf +0 -0
- data/lacci/Gemfile +22 -0
- data/lacci/Gemfile.lock +72 -0
- data/lacci/Rakefile +12 -0
- data/lacci/lacci.gemspec +37 -0
- data/lacci/lib/lacci/scarpe_cli.rb +70 -0
- data/lacci/lib/lacci/scarpe_core.rb +21 -0
- data/lacci/lib/lacci/version.rb +13 -0
- data/lacci/lib/shoes/app.rb +264 -0
- data/{lib/scarpe → lacci/lib/shoes}/background.rb +1 -1
- data/{lib/scarpe → lacci/lib/shoes}/border.rb +1 -1
- data/{lib/scarpe → lacci/lib/shoes}/colors.rb +1 -1
- data/lacci/lib/shoes/constants.rb +29 -0
- data/{lib/scarpe → lacci/lib/shoes}/display_service.rb +40 -45
- data/lacci/lib/shoes/download.rb +123 -0
- data/lacci/lib/shoes/log.rb +71 -0
- data/lacci/lib/shoes/spacing.rb +9 -0
- data/{lib/scarpe → lacci/lib/shoes}/widget.rb +63 -43
- data/{lib/scarpe → lacci/lib/shoes/widgets}/alert.rb +3 -3
- data/{lib/scarpe → lacci/lib/shoes/widgets}/arc.rb +7 -5
- data/{lib/scarpe → lacci/lib/shoes/widgets}/button.rb +3 -3
- data/lacci/lib/shoes/widgets/check.rb +28 -0
- data/lacci/lib/shoes/widgets/document_root.rb +20 -0
- data/{lib/scarpe → lacci/lib/shoes/widgets}/edit_box.rb +10 -5
- data/{lib/scarpe → lacci/lib/shoes/widgets}/edit_line.rb +2 -2
- data/lacci/lib/shoes/widgets/flow.rb +22 -0
- data/lacci/lib/shoes/widgets/font.rb +14 -0
- data/{lib/scarpe → lacci/lib/shoes/widgets}/image.rb +3 -7
- data/lacci/lib/shoes/widgets/line.rb +18 -0
- data/{lib/scarpe → lacci/lib/shoes/widgets}/link.rb +2 -2
- data/{lib/scarpe → lacci/lib/shoes/widgets}/list_box.rb +2 -2
- data/{lib/scarpe → lacci/lib/shoes/widgets}/para.rb +4 -26
- data/lacci/lib/shoes/widgets/radio.rb +35 -0
- data/lacci/lib/shoes/widgets/shape.rb +37 -0
- data/lacci/lib/shoes/widgets/slot.rb +75 -0
- data/{lib/scarpe → lacci/lib/shoes/widgets}/span.rb +2 -2
- data/lacci/lib/shoes/widgets/stack.rb +24 -0
- data/{lib/scarpe → lacci/lib/shoes/widgets}/star.rb +6 -9
- data/lacci/lib/shoes/widgets/subscription_item.rb +60 -0
- data/lacci/lib/shoes/widgets/text_widget.rb +51 -0
- data/lacci/lib/shoes/widgets/video.rb +15 -0
- data/lacci/lib/shoes/widgets.rb +29 -0
- data/lacci/lib/shoes.rb +127 -0
- data/lacci/test/test_colors.rb +39 -0
- data/lacci/test/test_helper.rb +9 -0
- data/lacci/test/test_lacci.rb +9 -0
- data/lib/scarpe/cats_cradle.rb +249 -0
- data/lib/scarpe/evented_assertions.rb +88 -0
- data/lib/scarpe/version.rb +1 -1
- data/lib/scarpe/wv/alert.rb +3 -2
- data/lib/scarpe/wv/app.rb +30 -8
- data/lib/scarpe/wv/arc.rb +5 -6
- data/lib/scarpe/wv/background.rb +10 -1
- data/lib/scarpe/wv/border.rb +5 -3
- data/lib/scarpe/wv/button.rb +11 -9
- data/lib/scarpe/wv/check.rb +29 -0
- data/lib/scarpe/wv/control_interface.rb +14 -20
- data/lib/scarpe/wv/control_interface_test.rb +13 -28
- data/lib/scarpe/wv/document_root.rb +3 -45
- data/lib/scarpe/wv/edit_box.rb +5 -7
- data/lib/scarpe/wv/edit_line.rb +2 -2
- data/lib/scarpe/wv/flow.rb +10 -20
- data/lib/scarpe/wv/font.rb +36 -0
- data/lib/scarpe/wv/html.rb +3 -2
- data/lib/scarpe/wv/image.rb +7 -2
- data/lib/scarpe/wv/line.rb +4 -7
- data/lib/scarpe/wv/link.rb +1 -0
- data/lib/scarpe/wv/list_box.rb +3 -3
- data/lib/scarpe/wv/para.rb +16 -14
- data/lib/scarpe/wv/radio.rb +34 -0
- data/lib/scarpe/wv/shape.rb +44 -8
- data/lib/scarpe/wv/slot.rb +81 -0
- data/lib/scarpe/wv/spacing.rb +1 -1
- data/lib/scarpe/wv/span.rb +10 -8
- data/lib/scarpe/wv/stack.rb +10 -30
- data/lib/scarpe/wv/star.rb +11 -12
- data/lib/scarpe/wv/subscription_item.rb +50 -0
- data/lib/scarpe/wv/video.rb +34 -0
- data/lib/scarpe/wv/web_wrangler.rb +238 -58
- data/lib/scarpe/wv/webview_local_display.rb +27 -5
- data/lib/scarpe/wv/webview_relay_display.rb +18 -119
- data/lib/scarpe/wv/webview_relay_util.rb +143 -0
- data/lib/scarpe/wv/widget.rb +80 -11
- data/lib/scarpe/wv/wv_display_worker.rb +17 -4
- data/lib/scarpe/wv.rb +33 -4
- data/lib/scarpe/wv_local.rb +1 -1
- data/lib/scarpe/wv_relay.rb +1 -1
- data/lib/scarpe.rb +3 -32
- data/scarpe-components/.gitignore +1 -0
- data/scarpe-components/Gemfile +22 -0
- data/scarpe-components/README.md +35 -0
- data/scarpe-components/Rakefile +12 -0
- data/scarpe-components/lib/scarpe/components/base64.rb +29 -0
- data/scarpe-components/lib/scarpe/components/file_helpers.rb +65 -0
- data/scarpe-components/lib/scarpe/components/modular_logger.rb +113 -0
- data/scarpe-components/lib/scarpe/components/print_logger.rb +43 -0
- data/{lib/scarpe → scarpe-components/lib/scarpe/components}/promises.rb +102 -35
- data/scarpe-components/lib/scarpe/components/segmented_file_loader.rb +170 -0
- data/scarpe-components/lib/scarpe/components/unit_test_helpers.rb +217 -0
- data/scarpe-components/lib/scarpe/components/version.rb +7 -0
- data/scarpe-components/scarpe-components.gemspec +38 -0
- data/scarpe-components/test/test_components.rb +9 -0
- data/scarpe-components/test/test_helper.rb +23 -0
- data/scarpe-components/test/test_promises.rb +260 -0
- data/scarpe-components/test/test_segmented_app_files.rb +182 -0
- data/{lib/scarpe → spikes}/glibui/widget.rb +2 -2
- data/{lib/scarpe → spikes}/glibui.rb +1 -1
- data/templates/basic_class_template.erb +1 -1
- data/templates/class_template_with_event_bind.erb +1 -1
- data/templates/class_template_with_shapes.erb +1 -1
- data/templates/webview_template.erb +0 -3
- metadata +151 -118
- data/examples/fill.rb +0 -25
- data/examples/legacy/not_checked/shoes-contrib/basic/class-book.yaml +0 -387
- data/examples/legacy/not_checked/shoes-contrib/good/good-clock.rb +0 -51
- data/examples/legacy/not_checked/shoes-contrib/good/good-follow.rb +0 -26
- data/examples/legacy/not_checked/shoes-contrib/good/good-reminder.rb +0 -174
- data/examples/legacy/not_checked/shoes-contrib/good/good-vjot.rb +0 -56
- data/examples/legacy/not_checked/shoes-contrib/simple/simple-timer.rb +0 -13
- data/examples/legacy/not_checked/shoes-dep-samples/good-clock.rb +0 -51
- data/examples/legacy/not_checked/shoes-dep-samples/good-follow.rb +0 -26
- data/examples/legacy/not_checked/shoes-dep-samples/good-reminder.rb +0 -174
- data/examples/legacy/not_checked/shoes-dep-samples/good-vjot.rb +0 -56
- data/examples/legacy/not_checked/shoes-dep-samples/simple-accordion.rb +0 -75
- data/examples/legacy/not_checked/shoes-dep-samples/simple-anim-shapes.rb +0 -17
- data/examples/legacy/not_checked/shoes-dep-samples/simple-anim-text.rb +0 -13
- data/examples/legacy/not_checked/shoes-dep-samples/simple-arc.rb +0 -23
- data/examples/legacy/not_checked/shoes-dep-samples/simple-bounce.rb +0 -24
- data/examples/legacy/not_checked/shoes-dep-samples/simple-calc.rb +0 -70
- data/examples/legacy/not_checked/shoes-dep-samples/simple-chipmunk.rb +0 -26
- data/examples/legacy/not_checked/shoes-dep-samples/simple-control-sizes.rb +0 -24
- data/examples/legacy/not_checked/shoes-dep-samples/simple-curve.rb +0 -26
- data/examples/legacy/not_checked/shoes-dep-samples/simple-dialogs.rb +0 -29
- data/examples/legacy/not_checked/shoes-dep-samples/simple-draw.rb +0 -13
- data/examples/legacy/not_checked/shoes-dep-samples/simple-editor.rb +0 -28
- data/examples/legacy/not_checked/shoes-dep-samples/simple-form.rb +0 -28
- data/examples/legacy/not_checked/shoes-dep-samples/simple-form.shy +0 -0
- data/examples/legacy/not_checked/shoes-dep-samples/simple-mask.rb +0 -21
- data/examples/legacy/not_checked/shoes-dep-samples/simple-menu.rb +0 -31
- data/examples/legacy/not_checked/shoes-dep-samples/simple-menu1.rb +0 -35
- data/examples/legacy/not_checked/shoes-dep-samples/simple-rubygems.rb +0 -29
- data/examples/legacy/not_checked/shoes-dep-samples/simple-slide.rb +0 -45
- data/examples/legacy/not_checked/shoes-dep-samples/simple-sphere.rb +0 -28
- data/examples/legacy/not_checked/shoes-dep-samples/simple-sqlite3.rb +0 -13
- data/examples/legacy/not_checked/shoes-dep-samples/simple-timer.rb +0 -13
- data/examples/legacy/not_checked/shoes-dep-samples/simple-video.rb +0 -13
- data/examples/legacy/not_checked/simple/anim-text.rb +0 -13
- data/examples/legacy/not_checked/simple/arc.rb +0 -23
- data/examples/legacy/not_checked/simple/bounce.rb +0 -24
- data/examples/legacy/not_checked/simple/chipmunk.rb +0 -26
- data/examples/legacy/not_checked/simple/curve.rb +0 -26
- data/examples/legacy/not_checked/simple/dialogs.rb +0 -29
- data/examples/legacy/not_checked/simple/downloader.rb +0 -40
- data/examples/legacy/not_checked/simple/draw.rb +0 -13
- data/examples/legacy/not_checked/simple/mask.rb +0 -21
- data/examples/legacy/not_checked/simple/slide.rb +0 -45
- data/examples/legacy/not_checked/simple/sphere.rb +0 -28
- data/lib/constants.rb +0 -5
- data/lib/scarpe/app.rb +0 -78
- data/lib/scarpe/document_root.rb +0 -20
- data/lib/scarpe/fill.rb +0 -23
- data/lib/scarpe/flow.rb +0 -19
- data/lib/scarpe/line.rb +0 -25
- data/lib/scarpe/logger.rb +0 -155
- data/lib/scarpe/shape.rb +0 -19
- data/lib/scarpe/spacing.rb +0 -9
- data/lib/scarpe/stack.rb +0 -70
- data/lib/scarpe/text_widget.rb +0 -42
- data/lib/scarpe/unit_test_helpers.rb +0 -163
- data/lib/scarpe/widgets.rb +0 -30
- data/lib/scarpe/wv/fill.rb +0 -30
- data/lib/scarpe/wv/shape_helper.rb +0 -44
- data/scarpe-0.2.0.gem +0 -0
- /data/{lib/scarpe → spikes}/glibui/README.md +0 -0
- /data/{lib/scarpe → spikes}/glibui/alert.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/app.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/background.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/border.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/button.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/dimensions.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/document_root.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/edit_box.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/edit_line.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/flow.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/html.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/image.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/link.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/local_display.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/para.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/spacing.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/stack.rb +0 -0
- /data/{lib/scarpe → spikes}/glibui/text_widget.rb +0 -0
- /data/{lib/scarpe → spikes}/libui/alert.rb +0 -0
- /data/{lib/scarpe → spikes}/libui/button.rb +0 -0
- /data/{lib/scarpe → spikes}/libui/colors.rb +0 -0
- /data/{lib/scarpe → spikes}/libui/core.rb +0 -0
- /data/{lib/scarpe → spikes}/libui/flow.rb +0 -0
- /data/{lib/scarpe → spikes}/libui/libui.rb +0 -0
- /data/{lib/scarpe → spikes}/libui/notepad.md +0 -0
- /data/{lib/scarpe → spikes}/libui/para.rb +0 -0
- /data/{lib/scarpe → spikes}/libui/stack.rb +0 -0
data/lacci/lacci.gemspec
ADDED
|
@@ -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
|
|
@@ -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
|
-
#
|
|
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
|
|
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
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|