scarpe 0.2.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +7 -1
- data/.yardopts +1 -0
- data/CHANGELOG.md +52 -6
- data/Gemfile +2 -1
- data/Gemfile.lock +20 -6
- data/LICENSE.txt +7 -1
- data/README.md +100 -21
- data/Rakefile +68 -1
- data/examples/Edit_box_Styles.rb +8 -0
- data/examples/Kerning.rb +7 -0
- data/examples/animate.rb +20 -0
- data/examples/arrow.rb +10 -0
- data/examples/border.rb +11 -0
- data/examples/btn_tooltip.rb +7 -0
- data/examples/button_style_changed.rb +7 -0
- data/examples/button_styles_default.rb +6 -0
- data/examples/check.rb +2 -0
- data/examples/download_and_show_image.rb +3 -0
- data/examples/flags/finland.rb +15 -0
- data/examples/flags/italy.rb +11 -0
- data/examples/flags/mauritius.rb +14 -0
- data/examples/font_family.rb +17 -0
- data/examples/font_shorthand.rb +9 -0
- data/examples/gen.rb +12 -8
- data/examples/highlander.rb +3 -3
- data/examples/legacy/README.md +6 -0
- data/examples/legacy/not_checked/shoes-contrib/basic/shoes-notes.rb +1 -1
- data/examples/legacy/not_checked/shoes-manual/append.rb +10 -0
- data/examples/legacy/not_checked/shoes-manual/background_change.rb +12 -0
- data/examples/legacy/not_checked/shoes-manual/background_pattern.rb +5 -0
- data/examples/legacy/not_checked/shoes-manual/basic_app.rb +8 -0
- data/examples/legacy/not_checked/shoes-manual/border.rb +9 -0
- data/examples/legacy/not_checked/shoes-manual/builtins/FONTS.rb +5 -0
- data/examples/legacy/not_checked/shoes-manual/builtins/ask.rb +2 -0
- data/examples/legacy/not_checked/shoes-manual/builtins/ask_color.rb +5 -0
- data/examples/legacy/not_checked/shoes-manual/builtins/ask_open_file.rb +5 -0
- data/examples/legacy/not_checked/shoes-manual/builtins/ask_save_folder.rb +2 -0
- data/examples/legacy/not_checked/shoes-manual/builtins/confirm.rb +4 -0
- data/examples/legacy/not_checked/shoes-manual/builtins/debug.rb +2 -0
- data/examples/legacy/not_checked/shoes-manual/builtins/info.rb +3 -0
- data/examples/legacy/not_checked/shoes-manual/button.rb +9 -0
- data/examples/legacy/not_checked/shoes-manual/clear.rb +7 -0
- data/examples/legacy/not_checked/shoes-manual/custom_header.rb +13 -0
- data/examples/legacy/not_checked/shoes-manual/displace.rb +14 -0
- data/examples/legacy/not_checked/shoes-manual/edit_box.rb +8 -0
- data/examples/legacy/not_checked/shoes-manual/fill_pattern.rb +5 -0
- data/examples/legacy/not_checked/shoes-manual/fonts.rb +7 -0
- data/examples/legacy/not_checked/shoes-manual/gutter.rb +6 -0
- data/examples/legacy/not_checked/shoes-manual/image_web.rb +4 -0
- data/examples/legacy/not_checked/shoes-manual/keypress.rb +7 -0
- data/examples/legacy/not_checked/shoes-manual/list_box.rb +10 -0
- data/examples/legacy/not_checked/shoes-manual/motion.rb +10 -0
- data/examples/legacy/not_checked/shoes-manual/mouse.rb +8 -0
- data/examples/legacy/not_checked/shoes-manual/move.rb +14 -0
- data/examples/legacy/not_checked/shoes-manual/nested_ovals.rb +8 -0
- data/examples/legacy/not_checked/shoes-manual/oval.rb +7 -0
- data/examples/legacy/not_checked/shoes-manual/ovals.rb +6 -0
- data/examples/legacy/not_checked/shoes-manual/ovals_image.rb +8 -0
- data/examples/legacy/not_checked/shoes-manual/prepend.rb +7 -0
- data/examples/legacy/not_checked/shoes-manual/progress_bar.rb +10 -0
- data/examples/legacy/not_checked/shoes-manual/radio.rb +18 -0
- data/examples/legacy/not_checked/shoes-manual/radio_alternative_1.rb +7 -0
- data/examples/legacy/not_checked/shoes-manual/radio_alternative_2.rb +9 -0
- data/examples/legacy/not_checked/shoes-manual/rotate_rectangle.rb +6 -0
- data/examples/legacy/not_checked/shoes-manual/shape.rb +11 -0
- data/examples/legacy/not_checked/shoes-manual/static/avatar.png +0 -0
- data/examples/legacy/not_checked/shoes-manual/stroke.rb +5 -0
- data/examples/legacy/not_checked/shoes-manual/style.rb +3 -0
- data/examples/legacy/not_checked/shoes-manual/style_alternative_1.rb +4 -0
- data/examples/legacy/not_checked/shoes-manual/style_alternative_2.rb +5 -0
- data/examples/legacy/not_checked/shoes-manual/style_length.rb +5 -0
- data/examples/legacy/not_checked/shoes-manual/timer.rb +6 -0
- data/examples/legacy/not_checked/shoes-manual/trigger_window.rb +8 -0
- data/examples/legacy/not_checked/shoes-manual/window_owner.rb +8 -0
- data/examples/legacy/not_checked/simple/anim-shapes.rb +1 -1
- data/examples/legacy/not_checked/speedometer_app.rb +55 -0
- data/examples/legacy/working/shoes_manual/alert_button.rb +2 -0
- data/examples/legacy/working/shoes_manual/animate.rb +7 -0
- data/examples/legacy/working/shoes_manual/background_para.rb +4 -0
- data/examples/legacy/working/shoes_manual/button_alternative.rb +7 -0
- data/examples/legacy/working/shoes_manual/checkbox.rb +17 -0
- data/examples/legacy/working/shoes_manual/download.rb +12 -0
- data/examples/legacy/working/shoes_manual/edit_box.rb +6 -0
- data/examples/legacy/working/shoes_manual/editline.rb +7 -0
- data/examples/legacy/working/shoes_manual/fixed_height.rb +8 -0
- data/examples/legacy/working/shoes_manual/fixed_width.rb +12 -0
- data/examples/legacy/working/shoes_manual/image.rb +5 -0
- data/examples/legacy/working/shoes_manual/instance_variable_check.rb +10 -0
- data/examples/legacy/working/shoes_manual/message.rb +18 -0
- data/examples/legacy/working/shoes_manual/rectangle.rb +6 -0
- data/examples/legacy/working/shoes_manual/save_download.rb +12 -0
- data/examples/legacy/working/shoes_manual/self_check.rb +10 -0
- data/examples/legacy/working/shoes_manual/stack.rb +7 -0
- data/examples/legacy/working/shoes_manual/style_info.rb +8 -0
- data/examples/legacy/working/shoes_manual/utf8_support.rb +8 -0
- data/examples/legacy/working/shoes_manual/width.rb +4 -0
- data/examples/legacy/working/simple/image-icon.rb +3 -0
- data/examples/legacy/{not_checked → working}/simple/image.rb +1 -1
- data/examples/list_box_choose.rb +17 -0
- data/examples/local_assets/local_file_server.rb +82 -0
- data/examples/local_assets/multi_image.rb +5 -0
- data/examples/local_assets/sample.gif +0 -0
- data/examples/local_assets/sample.mp4 +0 -0
- data/examples/local_assets/small.png +0 -0
- data/examples/local_fonts.rb +5 -2
- data/examples/local_images.rb +2 -3
- data/examples/margin.rb +13 -0
- data/examples/margin_check.rb +27 -0
- data/examples/oval-with-kwargs.rb +3 -0
- data/examples/oval.rb +26 -0
- data/examples/para/para_text.rb +14 -0
- data/examples/para_font_styles.rb +17 -0
- data/examples/para_font_variant.rb +6 -0
- data/examples/para_fontweight.rb +13 -0
- data/examples/parse_xl_funnies.rb +3 -0
- data/examples/progress.rb +31 -0
- data/examples/radio/radio_groups.rb +2 -2
- data/examples/rect.rb +4 -0
- data/examples/rotate_shapes.rb +17 -0
- data/examples/scarpe_ext.rb +3 -0
- data/examples/shapes/star.rb +1 -3
- data/examples/simpler-menu.rb +21 -0
- data/examples/spacing.rb +1 -1
- data/examples/span.rb +4 -2
- data/exe/scarpe +2 -1
- data/lacci/Gemfile +2 -0
- data/lacci/Gemfile.lock +8 -1
- data/lacci/lacci.gemspec +2 -2
- data/lacci/lib/lacci/scarpe_cli.rb +2 -2
- data/lacci/lib/lacci/scarpe_core.rb +2 -1
- data/lacci/lib/lacci/version.rb +1 -1
- data/lacci/lib/scarpe/niente/app.rb +23 -0
- data/lacci/lib/scarpe/niente/display_service.rb +66 -0
- data/lacci/lib/scarpe/niente/drawable.rb +59 -0
- data/lacci/lib/scarpe/niente/shoes_spec.rb +93 -0
- data/lacci/lib/scarpe/niente.rb +32 -0
- data/lacci/lib/shoes/app.rb +111 -72
- data/lacci/lib/shoes/background.rb +2 -2
- data/lacci/lib/shoes/border.rb +2 -2
- data/lacci/lib/shoes/builtins.rb +63 -0
- data/lacci/lib/shoes/changelog.rb +52 -0
- data/lacci/lib/shoes/colors.rb +3 -1
- data/lacci/lib/shoes/constants.rb +41 -2
- data/lacci/lib/shoes/display_service.rb +80 -18
- data/lacci/lib/shoes/download.rb +2 -2
- data/lacci/lib/shoes/drawable.rb +654 -0
- data/lacci/lib/shoes/drawables/arc.rb +27 -0
- data/lacci/lib/shoes/drawables/arrow.rb +21 -0
- data/lacci/lib/shoes/drawables/border.rb +28 -0
- data/lacci/lib/shoes/drawables/button.rb +57 -0
- data/lacci/lib/shoes/drawables/check.rb +33 -0
- data/lacci/lib/shoes/drawables/document_root.rb +20 -0
- data/lacci/lib/shoes/{widgets → drawables}/edit_box.rb +9 -8
- data/lacci/lib/shoes/{widgets → drawables}/edit_line.rb +8 -7
- data/lacci/lib/shoes/drawables/flow.rb +20 -0
- data/lacci/lib/shoes/drawables/font_helper.rb +62 -0
- data/lacci/lib/shoes/{widgets → drawables}/image.rb +7 -7
- data/lacci/lib/shoes/drawables/line.rb +17 -0
- data/lacci/lib/shoes/drawables/link.rb +31 -0
- data/lacci/lib/shoes/drawables/list_box.rb +59 -0
- data/lacci/lib/shoes/drawables/oval.rb +48 -0
- data/lacci/lib/shoes/drawables/para.rb +206 -0
- data/lacci/lib/shoes/drawables/progress.rb +15 -0
- data/lacci/lib/shoes/drawables/radio.rb +35 -0
- data/lacci/lib/shoes/drawables/rect.rb +18 -0
- data/lacci/lib/shoes/{widgets → drawables}/shape.rb +8 -8
- data/lacci/lib/shoes/drawables/slot.rb +178 -0
- data/lacci/lib/shoes/drawables/stack.rb +21 -0
- data/lacci/lib/shoes/drawables/star.rb +28 -0
- data/lacci/lib/shoes/drawables/subscription_item.rb +93 -0
- data/lacci/lib/shoes/drawables/text_drawable.rb +122 -0
- data/lacci/lib/shoes/drawables/video.rb +17 -0
- data/lacci/lib/shoes/drawables/widget.rb +74 -0
- data/lacci/lib/shoes/drawables.rb +32 -0
- data/lacci/lib/shoes/errors.rb +38 -0
- data/lacci/lib/shoes/log.rb +2 -2
- data/lacci/lib/shoes/margin_helper.rb +79 -0
- data/lacci/lib/shoes/ruby_extensions.rb +15 -0
- data/lacci/lib/shoes-spec.rb +93 -0
- data/lacci/lib/shoes.rb +31 -10
- data/lacci/test/.gitignore +1 -0
- data/lacci/test/test_draw_context.rb +167 -0
- data/lacci/test/test_font_helper.rb +57 -0
- data/lacci/test/test_helper.rb +81 -0
- data/lacci/test/test_lacci.rb +99 -3
- data/lacci/test/test_margin_helper.rb +82 -0
- data/lacci/test/test_niente_test_infra.rb +26 -0
- data/lacci/test/test_oval.rb +82 -0
- data/lacci/test/test_parenting.rb +140 -0
- data/lacci/test/test_shoes_errors.rb +49 -0
- data/lacci/test/test_text_drawables.rb +23 -0
- data/lib/scarpe/assets.rb +18 -0
- data/lib/scarpe/cats_cradle.rb +84 -103
- data/lib/scarpe/errors.rb +77 -0
- data/lib/scarpe/shoes_spec.rb +160 -0
- data/lib/scarpe/version.rb +2 -2
- data/lib/scarpe/wv/app.rb +21 -20
- data/lib/scarpe/wv/arc.rb +4 -51
- data/lib/scarpe/wv/arrow.rb +9 -0
- data/lib/scarpe/wv/border.rb +9 -18
- data/lib/scarpe/wv/button.rb +7 -35
- data/lib/scarpe/wv/check.rb +3 -5
- data/lib/scarpe/wv/control_interface.rb +20 -30
- data/lib/scarpe/wv/document_root.rb +81 -4
- data/lib/scarpe/wv/{widget.rb → drawable.rb} +71 -82
- data/lib/scarpe/wv/edit_box.rb +8 -18
- data/lib/scarpe/wv/edit_line.rb +9 -20
- data/lib/scarpe/wv/flow.rb +2 -18
- data/lib/scarpe/wv/image.rb +7 -30
- data/lib/scarpe/wv/line.rb +3 -25
- data/lib/scarpe/wv/link.rb +6 -17
- data/lib/scarpe/wv/list_box.rb +6 -29
- data/lib/scarpe/wv/oval.rb +13 -0
- data/lib/scarpe/wv/para.rb +12 -30
- data/lib/scarpe/wv/progress.rb +19 -0
- data/lib/scarpe/wv/radio.rb +9 -10
- data/lib/scarpe/wv/rect.rb +13 -0
- data/lib/scarpe/wv/scarpe_extensions.rb +8 -0
- data/lib/scarpe/wv/shape.rb +13 -13
- data/lib/scarpe/wv/slot.rb +8 -25
- data/lib/scarpe/wv/stack.rb +2 -18
- data/lib/scarpe/wv/star.rb +3 -53
- data/lib/scarpe/wv/subscription_item.rb +38 -4
- data/lib/scarpe/wv/text_drawable.rb +90 -0
- data/lib/scarpe/wv/video.rb +15 -15
- data/lib/scarpe/wv/web_wrangler.rb +320 -328
- data/lib/scarpe/wv/webview_local_display.rb +52 -33
- data/lib/scarpe/wv/webview_relay_display.rb +12 -12
- data/lib/scarpe/wv/webview_relay_util.rb +7 -10
- data/lib/scarpe/wv/wv_display_worker.rb +2 -2
- data/lib/scarpe/wv.rb +52 -12
- data/lib/scarpe/wv_local.rb +1 -1
- data/lib/scarpe/wv_relay.rb +1 -1
- data/lib/scarpe.rb +1 -0
- data/logger/debug_web_wrangler.json +1 -1
- data/logger/scarpe_wv_test.json +1 -1
- data/scarpe-components/Gemfile +4 -1
- data/scarpe-components/Gemfile.lock +85 -0
- data/scarpe-components/README.md +2 -2
- data/scarpe-components/assets/bootstrap-themes/bootstrap-cerulean.css +12229 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-cosmo.css +11810 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-cyborg.css +12210 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-darkly.css +12153 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-flatly.css +12126 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-icons.min.css +5 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-journal.css +12099 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-litera.css +12211 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-lumen.css +12369 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-lux.css +11928 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-materia.css +13184 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-minty.css +12177 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-morph.css +12750 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-pulse.css +11890 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-quartz.css +12622 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-sandstone.css +12201 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-simplex.css +12186 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-sketchy.css +12451 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-slate.css +12492 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-solar.css +12149 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-spacelab.css +12266 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-superhero.css +12216 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-united.css +12077 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-vapor.css +12549 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-yeti.css +12325 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap-zephyr.css +12283 -0
- data/scarpe-components/assets/bootstrap-themes/bootstrap.bundle.min.js +7 -0
- data/scarpe-components/lib/scarpe/components/asset_server.rb +219 -0
- data/scarpe-components/lib/scarpe/components/base64.rb +23 -5
- data/scarpe-components/lib/scarpe/components/calzini/alert.rb +49 -0
- data/scarpe-components/lib/scarpe/components/calzini/art_drawables.rb +227 -0
- data/scarpe-components/lib/scarpe/components/calzini/border.rb +38 -0
- data/scarpe-components/lib/scarpe/components/calzini/button.rb +37 -0
- data/scarpe-components/lib/scarpe/components/calzini/misc.rb +136 -0
- data/scarpe-components/lib/scarpe/components/calzini/para.rb +237 -0
- data/scarpe-components/lib/scarpe/components/calzini/slots.rb +109 -0
- data/scarpe-components/lib/scarpe/components/calzini.rb +236 -0
- data/scarpe-components/lib/scarpe/components/errors.rb +24 -0
- data/scarpe-components/lib/scarpe/components/file_helpers.rb +1 -0
- data/scarpe-components/lib/scarpe/components/html.rb +134 -0
- data/scarpe-components/lib/scarpe/components/minitest_export_reporter.rb +83 -0
- data/scarpe-components/lib/scarpe/components/minitest_import_runnable.rb +98 -0
- data/scarpe-components/lib/scarpe/components/minitest_result.rb +127 -0
- data/scarpe-components/lib/scarpe/components/modular_logger.rb +5 -5
- data/scarpe-components/lib/scarpe/components/print_logger.rb +22 -3
- data/scarpe-components/lib/scarpe/components/process_helpers.rb +37 -0
- data/scarpe-components/lib/scarpe/components/promises.rb +14 -14
- data/scarpe-components/lib/scarpe/components/segmented_file_loader.rb +36 -17
- data/scarpe-components/lib/scarpe/components/string_helpers.rb +10 -0
- data/scarpe-components/lib/scarpe/components/tiranti.rb +167 -0
- data/scarpe-components/lib/scarpe/components/unit_test_helpers.rb +48 -6
- data/scarpe-components/lib/scarpe/components/version.rb +2 -2
- data/scarpe-components/test/assets/big-image.png +0 -0
- data/scarpe-components/test/assets/big-stylesheet.css +497 -0
- data/scarpe-components/test/assets/little-image.png +0 -0
- data/scarpe-components/test/assets/little-stylesheet.css +1 -0
- data/scarpe-components/test/calzini/test_calzini_alert.rb +30 -0
- data/scarpe-components/test/calzini/test_calzini_art_drawables.rb +105 -0
- data/scarpe-components/test/calzini/test_calzini_button.rb +54 -0
- data/scarpe-components/test/calzini/test_calzini_misc.rb +115 -0
- data/scarpe-components/test/calzini/test_calzini_para.rb +34 -0
- data/scarpe-components/test/calzini/test_calzini_slots.rb +85 -0
- data/scarpe-components/test/calzini/test_calzini_text_drawables.rb +106 -0
- data/scarpe-components/test/calzini/test_various.rb +133 -0
- data/scarpe-components/test/mtr_data/exception.json +1 -0
- data/scarpe-components/test/mtr_data/fail_with_message.json +1 -0
- data/scarpe-components/test/mtr_data/skipped_no_message.json +1 -0
- data/scarpe-components/test/mtr_data/skipped_w_msg.json +1 -0
- data/scarpe-components/test/mtr_data/succeed_2_asserts.json +1 -0
- data/scarpe-components/test/test_asset_server.rb +72 -0
- data/scarpe-components/test/test_components.rb +31 -2
- data/scarpe-components/test/test_dimensions.rb +26 -0
- data/scarpe-components/test/test_helper.rb +20 -1
- data/scarpe-components/test/test_html.rb +65 -0
- data/scarpe-components/test/test_minitest_result.rb +68 -0
- data/scarpe-components/test/test_promises.rb +5 -4
- data/scarpe-components/test/test_segmented_app_files.rb +10 -6
- data/scarpegen.rb +14 -14
- data/sig/scarpe.rbs +1 -1
- data/tasks/check_html_fixtures.rb +140 -0
- data/tasks/regenerate_html_fixtures.rb +104 -0
- data/templates/basic_class_template.erb +13 -14
- data/templates/class_template_with_event_bind.erb +4 -4
- data/templates/class_template_with_shapes.erb +8 -28
- data/templates/example_template.erb +1 -1
- data/templates/module_template.erb +4 -4
- data/templates/webview_template.erb +3 -2
- metadata +286 -80
- data/examples/legacy/not_checked/shoes-contrib/elements/image-icon.rb +0 -3
- data/lacci/lib/shoes/spacing.rb +0 -9
- data/lacci/lib/shoes/widget.rb +0 -218
- data/lacci/lib/shoes/widgets/alert.rb +0 -19
- data/lacci/lib/shoes/widgets/arc.rb +0 -51
- data/lacci/lib/shoes/widgets/button.rb +0 -35
- data/lacci/lib/shoes/widgets/check.rb +0 -28
- data/lacci/lib/shoes/widgets/document_root.rb +0 -20
- data/lacci/lib/shoes/widgets/flow.rb +0 -22
- data/lacci/lib/shoes/widgets/font.rb +0 -14
- data/lacci/lib/shoes/widgets/line.rb +0 -18
- data/lacci/lib/shoes/widgets/link.rb +0 -25
- data/lacci/lib/shoes/widgets/list_box.rb +0 -25
- data/lacci/lib/shoes/widgets/para.rb +0 -68
- data/lacci/lib/shoes/widgets/radio.rb +0 -35
- data/lacci/lib/shoes/widgets/slot.rb +0 -75
- data/lacci/lib/shoes/widgets/span.rb +0 -26
- data/lacci/lib/shoes/widgets/stack.rb +0 -24
- data/lacci/lib/shoes/widgets/star.rb +0 -44
- data/lacci/lib/shoes/widgets/subscription_item.rb +0 -60
- data/lacci/lib/shoes/widgets/text_widget.rb +0 -51
- data/lacci/lib/shoes/widgets/video.rb +0 -15
- data/lacci/lib/shoes/widgets.rb +0 -29
- data/lib/scarpe/evented_assertions.rb +0 -88
- data/lib/scarpe/wv/alert.rb +0 -66
- data/lib/scarpe/wv/background.rb +0 -27
- data/lib/scarpe/wv/control_interface_test.rb +0 -238
- data/lib/scarpe/wv/dimensions.rb +0 -22
- data/lib/scarpe/wv/font.rb +0 -36
- data/lib/scarpe/wv/html.rb +0 -108
- data/lib/scarpe/wv/spacing.rb +0 -41
- data/lib/scarpe/wv/span.rb +0 -68
- data/lib/scarpe/wv/text_widget.rb +0 -30
- /data/examples/legacy/not_checked/{expert → shoes-contrib/basic}/definr.rb +0 -0
- /data/examples/legacy/not_checked/{expert → shoes-contrib/basic}/funnies.rb +0 -0
- /data/examples/legacy/not_checked/shoes-contrib/{elements → basic}/list_box-select-class.rb +0 -0
- /data/examples/legacy/{not_checked → working}/shoes3-tests/editline/editline.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/basic → working/simple}/basic-edit-box.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/elements → working/simple}/basic-fps.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/elements → working/simple}/border-cat.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/elements → working/simple}/check-mate.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/manipulation → working/simple}/clear-slot.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/basic → working/simple}/clock.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/basic → working/simple}/gradient-shoes.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/basic → working/simple}/list_box-shape-report.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/elements → working/simple}/list_box.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/elements → working/simple}/phat-button.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib → working}/simple/simple-calc.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/position → working/simple}/stack-width.rb +0 -0
- /data/examples/legacy/{not_checked/shoes-contrib/elements → working/simple}/width-introspec.rb +0 -0
@@ -8,9 +8,9 @@ require "cgi"
|
|
8
8
|
# After creation, it starts in setup mode, and you can
|
9
9
|
# use setup-mode callbacks.
|
10
10
|
|
11
|
-
|
11
|
+
module Scarpe::Webview
|
12
12
|
# The Scarpe WebWrangler, for Webview, manages a lot of Webviews quirks. It provides
|
13
|
-
# a simpler underlying abstraction for DOMWrangler and the Webview
|
13
|
+
# a simpler underlying abstraction for DOMWrangler and the Webview drawables.
|
14
14
|
# Webview can be picky - if you send it too many messages, it can crash. If the
|
15
15
|
# messages you send it are too large, it can crash. If you don't return control
|
16
16
|
# to its event loop, it can crash. It doesn't save references to all event handlers,
|
@@ -51,26 +51,6 @@ class Scarpe
|
|
51
51
|
# A reference to the control_interface that manages internal Scarpe Webview events.
|
52
52
|
attr_reader :control_interface
|
53
53
|
|
54
|
-
# This error indicates a problem when running ConfirmedEval
|
55
|
-
class JSEvalError < Scarpe::Error
|
56
|
-
def initialize(data)
|
57
|
-
@data = data
|
58
|
-
super(data[:msg] || (self.class.name + "!"))
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
# An error running the supplied JS code string in confirmed_eval
|
63
|
-
class JSRuntimeError < JSEvalError
|
64
|
-
end
|
65
|
-
|
66
|
-
# The code timed out for some reason
|
67
|
-
class JSTimeoutError < JSEvalError
|
68
|
-
end
|
69
|
-
|
70
|
-
# We got weird or nonsensical results that seem like an error on WebWrangler's part
|
71
|
-
class InternalError < JSEvalError
|
72
|
-
end
|
73
|
-
|
74
54
|
# This is the JS function name for eval results (internal-only)
|
75
55
|
EVAL_RESULT = "scarpeAsyncEvalResult"
|
76
56
|
|
@@ -85,7 +65,7 @@ class Scarpe
|
|
85
65
|
# @param resizable [Boolean] whether the window should be resizable by the user
|
86
66
|
# @param heartbeat [Float] time between heartbeats in seconds
|
87
67
|
def initialize(title:, width:, height:, resizable: false, heartbeat: 0.1)
|
88
|
-
log_init("
|
68
|
+
log_init("Webview::WebWrangler")
|
89
69
|
|
90
70
|
@log.debug("Creating WebWrangler...")
|
91
71
|
|
@@ -149,7 +129,7 @@ class Scarpe
|
|
149
129
|
# @param name [String] the Javascript name for the new function
|
150
130
|
# @yield The Ruby block to be invoked when JS calls the function
|
151
131
|
def bind(name, &block)
|
152
|
-
raise "App is running, javascript binding no longer works because it uses WebView init!" if @is_running
|
132
|
+
raise Scarpe::JSBindingError, "App is running, javascript binding no longer works because it uses WebView init!" if @is_running
|
153
133
|
|
154
134
|
@webview.bind(name, &block)
|
155
135
|
end
|
@@ -160,7 +140,7 @@ class Scarpe
|
|
160
140
|
# @param name [String] the Javascript name for the init function
|
161
141
|
# @yield The Ruby block to be invoked when Webview runs
|
162
142
|
def init_code(name, &block)
|
163
|
-
raise "App is running, javascript init no longer works!" if @is_running
|
143
|
+
raise Scarpe::JSInitError, "App is running, javascript init no longer works!" if @is_running
|
164
144
|
|
165
145
|
# Save a reference to the init string so that it doesn't get GC'd
|
166
146
|
code_str = "#{name}();"
|
@@ -176,6 +156,8 @@ class Scarpe
|
|
176
156
|
# so it should be invoked when the WebWrangler is in setup mode,
|
177
157
|
# before the Webview is running.
|
178
158
|
#
|
159
|
+
# TODO: add a way to stop this loop and unsubscribe.
|
160
|
+
#
|
179
161
|
# @param name [String] the name of the Javascript init function, if needed
|
180
162
|
# @param interval [Float] the duration between invoking this block
|
181
163
|
# @yield the Ruby block to invoke periodically
|
@@ -188,7 +170,7 @@ class Scarpe
|
|
188
170
|
# new window. But will there ever be a new page/window? Can we just
|
189
171
|
# use eval instead of init to set up a periodic handler and call it
|
190
172
|
# good?
|
191
|
-
raise "App is running, can't set up new periodic handlers with init!"
|
173
|
+
raise Scarpe::PeriodicHandlerSetupError, "App is running, can't set up new periodic handlers with init!"
|
192
174
|
end
|
193
175
|
|
194
176
|
js_interval = (interval.to_f * 1_000.0).to_i
|
@@ -214,7 +196,7 @@ class Scarpe
|
|
214
196
|
# @param code [String] the Javascript code to attempt to execute
|
215
197
|
# @return [void]
|
216
198
|
def js_eventually(code)
|
217
|
-
raise "WebWrangler isn't running, eval doesn't work!" unless @is_running
|
199
|
+
raise Scarpe::WebWranglerNotRunningError, "WebWrangler isn't running, eval doesn't work!" unless @is_running
|
218
200
|
|
219
201
|
@log.warn "Deprecated: please do NOT use js_eventually, it's basically never what you want!" unless ENV["CI"]
|
220
202
|
|
@@ -242,10 +224,10 @@ class Scarpe
|
|
242
224
|
#
|
243
225
|
# @param code [String] the Javascript code to execute
|
244
226
|
# @param timeout [Float] how long to allow before raising a timeout exception
|
245
|
-
# @param wait_for [Array<Promise>] promises that must complete successfully before this JS is scheduled
|
227
|
+
# @param wait_for [Array<Scarpe::Promise>] promises that must complete successfully before this JS is scheduled
|
246
228
|
def eval_js_async(code, timeout: EVAL_DEFAULT_TIMEOUT, wait_for: [])
|
247
229
|
unless @is_running
|
248
|
-
raise "WebWrangler isn't running, so evaluating JS won't work!"
|
230
|
+
raise Scarpe::WebWranglerNotRunningError, "WebWrangler isn't running, so evaluating JS won't work!"
|
249
231
|
end
|
250
232
|
|
251
233
|
this_eval_serial = @eval_counter
|
@@ -279,6 +261,7 @@ class Scarpe
|
|
279
261
|
@log.debug("Scheduled JS: (#{this_eval_serial})\n#{wrapped_code}")
|
280
262
|
else
|
281
263
|
# We're mid-shutdown. No more scheduling things.
|
264
|
+
@log.warn "Mid-shutdown JS eval. Not scheduling JS!"
|
282
265
|
end
|
283
266
|
end
|
284
267
|
|
@@ -320,7 +303,7 @@ class Scarpe
|
|
320
303
|
def receive_eval_result(r_type, id, val)
|
321
304
|
entry = @pending_evals.delete(id)
|
322
305
|
unless entry
|
323
|
-
raise "Received an eval result for a nonexistent ID #{id.inspect}!"
|
306
|
+
raise Scarpe::NonexistentEvalResultError, "Received an eval result for a nonexistent ID #{id.inspect}!"
|
324
307
|
end
|
325
308
|
|
326
309
|
@log.debug("Got JS value: #{r_type} / #{id} / #{val.inspect}")
|
@@ -331,13 +314,13 @@ class Scarpe
|
|
331
314
|
when "success"
|
332
315
|
promise.fulfilled!(val)
|
333
316
|
when "error"
|
334
|
-
promise.rejected! JSRuntimeError.new(
|
317
|
+
promise.rejected! Scarpe::JSRuntimeError.new(
|
335
318
|
msg: "JS runtime error: #{val.inspect}!",
|
336
319
|
code: entry[:code],
|
337
320
|
ret_value: val,
|
338
321
|
)
|
339
322
|
else
|
340
|
-
promise.rejected!
|
323
|
+
promise.rejected! Scarpe::JSInternalError.new(
|
341
324
|
msg: "JS eval internal error! r_type: #{r_type.inspect}",
|
342
325
|
code: entry[:code],
|
343
326
|
ret_value: val,
|
@@ -373,13 +356,15 @@ class Scarpe
|
|
373
356
|
timed_out_ids.each do |id|
|
374
357
|
@log.error "Timing out JS eval! #{@pending_evals[id][:code]}"
|
375
358
|
entry = @pending_evals.delete(id)
|
376
|
-
err = JSTimeoutError.new(msg: "JS timeout error!", code: entry[:code], ret_value: nil)
|
359
|
+
err = Scarpe::JSTimeoutError.new(msg: "JS timeout error!", code: entry[:code], ret_value: nil)
|
377
360
|
entry[:promise].rejected!(err)
|
378
361
|
end
|
379
362
|
end
|
380
363
|
|
381
364
|
public
|
382
365
|
|
366
|
+
attr_writer :empty_page
|
367
|
+
|
383
368
|
# After setup, we call run to go to "running" mode.
|
384
369
|
# No more setup callbacks should be called, only running callbacks.
|
385
370
|
def run
|
@@ -387,14 +372,18 @@ class Scarpe
|
|
387
372
|
|
388
373
|
# From webview:
|
389
374
|
# 0 - Width and height are default size
|
390
|
-
# 1 - Width and height are minimum
|
391
|
-
# 2 - Width and height are maximum
|
375
|
+
# 1 - Width and height are minimum bounds
|
376
|
+
# 2 - Width and height are maximum bounds
|
392
377
|
# 3 - Window size can not be changed by a user
|
393
378
|
hint = @resizable ? 0 : 3
|
394
379
|
|
395
380
|
@webview.set_title(@title)
|
396
381
|
@webview.set_size(@width, @height, hint)
|
397
|
-
@
|
382
|
+
unless @empty_page
|
383
|
+
raise Scarpe::EmptyPageNotSetError, "No empty page markup was set!"
|
384
|
+
end
|
385
|
+
|
386
|
+
@webview.navigate("data:text/html, #{CGI.escape @empty_page}")
|
398
387
|
|
399
388
|
monkey_patch_console(@webview)
|
400
389
|
|
@@ -440,29 +429,7 @@ class Scarpe
|
|
440
429
|
end
|
441
430
|
|
442
431
|
def empty
|
443
|
-
|
444
|
-
<html>
|
445
|
-
<head id='head-wvroot'>
|
446
|
-
<style id='style-wvroot'>
|
447
|
-
/** Style resets **/
|
448
|
-
body {
|
449
|
-
font-family: arial, Helvetica, sans-serif;
|
450
|
-
margin: 0;
|
451
|
-
height: 100%;
|
452
|
-
overflow: hidden;
|
453
|
-
}
|
454
|
-
p {
|
455
|
-
margin: 0;
|
456
|
-
}
|
457
|
-
</style>
|
458
|
-
</head>
|
459
|
-
<body id='body-wvroot'>
|
460
|
-
<div id='wrapper-wvroot'></div>
|
461
|
-
</body>
|
462
|
-
</html>
|
463
|
-
HTML
|
464
|
-
|
465
|
-
CGI.escape(html)
|
432
|
+
Scarpe::Components::Calzini.empty_page_element
|
466
433
|
end
|
467
434
|
|
468
435
|
public
|
@@ -535,325 +502,350 @@ class Scarpe
|
|
535
502
|
end
|
536
503
|
end
|
537
504
|
|
538
|
-
class Scarpe
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
include Shoes::Log
|
560
|
-
|
561
|
-
# Changes that have not yet been executed
|
562
|
-
attr_reader :waiting_changes
|
505
|
+
class Scarpe::Webview::WebWrangler
|
506
|
+
# Leaving DOM changes as "meh, async, we'll see when it happens" is terrible for testing.
|
507
|
+
# Instead, we need to track whether particular changes have committed yet or not.
|
508
|
+
# So we add a single gateway for all DOM changes, and we make sure its work is done
|
509
|
+
# before we consider a redraw complete.
|
510
|
+
#
|
511
|
+
# DOMWrangler batches up changes into fewer RPC calls. It's fine to have a redraw
|
512
|
+
# "in flight" and have changes waiting to catch the next bus. But we don't want more
|
513
|
+
# than one in flight, since it seems like having too many pending RPC requests can
|
514
|
+
# crash Webview. So we allow one redraw scheduled and one redraw promise waiting,
|
515
|
+
# at maximum.
|
516
|
+
#
|
517
|
+
# A WebWrangler will create and wrap a DOMWrangler, serving as the interface
|
518
|
+
# for all DOM operations.
|
519
|
+
#
|
520
|
+
# A batch of DOMWrangler changes may be removed if a full update is scheduled. That
|
521
|
+
# update is considered to replace the previous incremental changes. Any changes that
|
522
|
+
# need to execute even if a full update happens should be scheduled through
|
523
|
+
# WebWrangler#eval_js_async, not DOMWrangler.
|
524
|
+
class DOMWrangler
|
525
|
+
include Shoes::Log
|
563
526
|
|
564
|
-
|
565
|
-
|
527
|
+
# Changes that have not yet been executed
|
528
|
+
attr_reader :waiting_changes
|
529
|
+
|
530
|
+
# A Scarpe::Promise for JS that has been scheduled to execute but is not yet verified complete
|
531
|
+
attr_reader :pending_redraw_promise
|
532
|
+
|
533
|
+
# A Scarpe::Promise for waiting changes - it will be fulfilled when all waiting changes
|
534
|
+
# have been verified complete, or when a full redraw that removed them has been
|
535
|
+
# verified complete. If many small changes are scheduled, the same promise will be
|
536
|
+
# returned for many of them.
|
537
|
+
attr_reader :waiting_redraw_promise
|
538
|
+
|
539
|
+
# Create a DOMWrangler that is paired with a WebWrangler. The WebWrangler is
|
540
|
+
# treated as an underlying abstraction for reliable JS evaluation.
|
541
|
+
def initialize(web_wrangler)
|
542
|
+
log_init("Webview::WebWrangler::DOMWrangler")
|
543
|
+
|
544
|
+
@wrangler = web_wrangler
|
545
|
+
|
546
|
+
@waiting_changes = []
|
547
|
+
@pending_redraw_promise = nil
|
548
|
+
@waiting_redraw_promise = nil
|
549
|
+
|
550
|
+
@fully_up_to_date_promise = nil
|
551
|
+
|
552
|
+
# Initially we're waiting for a full replacement to happen.
|
553
|
+
# It's possible to request updates/changes before we have
|
554
|
+
# a DOM in place and before Webview is running. If we do
|
555
|
+
# that, we should discard those updates.
|
556
|
+
@first_draw_requested = false
|
557
|
+
|
558
|
+
@redraw_handlers = []
|
559
|
+
|
560
|
+
# The "fully up to date" logic is complicated and not
|
561
|
+
# as well tested as I'd like. This makes it far less
|
562
|
+
# likely that the event simply won't fire.
|
563
|
+
# With more comprehensive testing, this should be
|
564
|
+
# removable.
|
565
|
+
web_wrangler.periodic_code("scarpeDOMWranglerHeartbeat") do
|
566
|
+
if @fully_up_to_date_promise && fully_updated?
|
567
|
+
@log.info("Fulfilling up-to-date promise on heartbeat")
|
568
|
+
@fully_up_to_date_promise.fulfilled!
|
569
|
+
@fully_up_to_date_promise = nil
|
570
|
+
end
|
571
|
+
end
|
572
|
+
end
|
566
573
|
|
567
|
-
|
568
|
-
#
|
569
|
-
|
570
|
-
# returned for many of them.
|
571
|
-
attr_reader :waiting_redraw_promise
|
574
|
+
def request_change(js_code)
|
575
|
+
# No updates until there's something to update
|
576
|
+
return unless @first_draw_requested
|
572
577
|
|
573
|
-
|
574
|
-
# treated as an underlying abstraction for reliable JS evaluation.
|
575
|
-
def initialize(web_wrangler)
|
576
|
-
log_init("WV::WebWrangler::DOMWrangler")
|
578
|
+
@waiting_changes << js_code
|
577
579
|
|
578
|
-
|
580
|
+
promise_redraw
|
581
|
+
end
|
579
582
|
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
@fully_up_to_date_promise = nil
|
585
|
-
|
586
|
-
# Initially we're waiting for a full replacement to happen.
|
587
|
-
# It's possible to request updates/changes before we have
|
588
|
-
# a DOM in place and before Webview is running. If we do
|
589
|
-
# that, we should discard those updates.
|
590
|
-
@first_draw_requested = false
|
591
|
-
|
592
|
-
@redraw_handlers = []
|
593
|
-
|
594
|
-
# The "fully up to date" logic is complicated and not
|
595
|
-
# as well tested as I'd like. This makes it far less
|
596
|
-
# likely that the event simply won't fire.
|
597
|
-
# With more comprehensive testing, this should be
|
598
|
-
# removable.
|
599
|
-
web_wrangler.periodic_code("scarpeDOMWranglerHeartbeat") do
|
600
|
-
if @fully_up_to_date_promise && fully_updated?
|
601
|
-
@log.info("Fulfilling up-to-date promise on heartbeat")
|
602
|
-
@fully_up_to_date_promise.fulfilled!
|
603
|
-
@fully_up_to_date_promise = nil
|
604
|
-
end
|
605
|
-
end
|
606
|
-
end
|
583
|
+
def self.replacement_code(html_text)
|
584
|
+
"document.getElementById('wrapper-wvroot').innerHTML = `#{html_text}`; true"
|
585
|
+
end
|
607
586
|
|
608
|
-
|
609
|
-
|
610
|
-
|
587
|
+
def request_replace(html_text)
|
588
|
+
# Replace other pending changes, they're not needed any more
|
589
|
+
@waiting_changes = [DOMWrangler.replacement_code(html_text)]
|
590
|
+
@first_draw_requested = true
|
611
591
|
|
612
|
-
|
592
|
+
@log.debug("Requesting DOM replacement...")
|
593
|
+
promise_redraw
|
594
|
+
end
|
613
595
|
|
614
|
-
|
615
|
-
|
596
|
+
def on_every_redraw(&block)
|
597
|
+
@redraw_handlers << block
|
598
|
+
end
|
616
599
|
|
617
|
-
|
618
|
-
|
600
|
+
# promise_redraw returns a Scarpe::Promise which will be fulfilled after all current
|
601
|
+
# pending or waiting changes have completed. This may require creating a new
|
602
|
+
# promise.
|
603
|
+
#
|
604
|
+
# What are the states of redraw?
|
605
|
+
# "empty" - no waiting promise, no pending-redraw promise, no pending changes
|
606
|
+
# "pending only" - no waiting promise, but we have a pending redraw with some changes; it hasn't committed yet
|
607
|
+
# "pending and waiting" - we have a waiting promise for our unscheduled changes; we can add more unscheduled
|
608
|
+
# changes since we haven't scheduled them yet.
|
609
|
+
#
|
610
|
+
# This is often called after adding a new waiting change or replacing them, so the state may have just changed.
|
611
|
+
# It can also be called when no changes have been made and no updates need to happen.
|
612
|
+
def promise_redraw
|
613
|
+
if fully_updated?
|
614
|
+
# No changes to make, nothing in-process or waiting, so just return a pre-fulfilled promise
|
615
|
+
@log.debug("Requesting redraw but there are no pending changes or promises, return pre-fulfilled")
|
616
|
+
return ::Scarpe::Promise.fulfilled
|
619
617
|
end
|
620
618
|
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
@
|
625
|
-
|
626
|
-
@log.debug("Requesting DOM replacement...")
|
627
|
-
promise_redraw
|
619
|
+
# Already have a redraw requested *and* one on deck? Then all current changes will have committed
|
620
|
+
# when we (eventually) fulfill the waiting_redraw_promise.
|
621
|
+
if @waiting_redraw_promise
|
622
|
+
@log.debug("Promising eventual redraw of #{@waiting_changes.size} waiting unscheduled changes.")
|
623
|
+
return @waiting_redraw_promise
|
628
624
|
end
|
629
625
|
|
630
|
-
|
631
|
-
|
626
|
+
if @waiting_changes.empty?
|
627
|
+
# There's no waiting_redraw_promise. There are no waiting changes. But we're not fully updated.
|
628
|
+
# So there must be a redraw in flight, and we don't need to schedule a new waiting_redraw_promise.
|
629
|
+
@log.debug("Returning in-flight redraw promise")
|
630
|
+
return @pending_redraw_promise
|
632
631
|
end
|
633
632
|
|
634
|
-
#
|
635
|
-
# pending or waiting changes have completed. This may require creating a new
|
636
|
-
# promise.
|
637
|
-
#
|
638
|
-
# What are the states of redraw?
|
639
|
-
# "empty" - no waiting promise, no pending-redraw promise, no pending changes
|
640
|
-
# "pending only" - no waiting promise, but we have a pending redraw with some changes; it hasn't committed yet
|
641
|
-
# "pending and waiting" - we have a waiting promise for our unscheduled changes; we can add more unscheduled
|
642
|
-
# changes since we haven't scheduled them yet.
|
643
|
-
#
|
644
|
-
# This is often called after adding a new waiting change or replacing them, so the state may have just changed.
|
645
|
-
# It can also be called when no changes have been made and no updates need to happen.
|
646
|
-
def promise_redraw
|
647
|
-
if fully_updated?
|
648
|
-
# No changes to make, nothing in-process or waiting, so just return a pre-fulfilled promise
|
649
|
-
@log.debug("Requesting redraw but there are no pending changes or promises, return pre-fulfilled")
|
650
|
-
return Promise.fulfilled
|
651
|
-
end
|
633
|
+
@log.debug("Requesting redraw with #{@waiting_changes.size} waiting changes and no waiting promise - need to schedule something!")
|
652
634
|
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
return @pending_redraw_promise
|
665
|
-
end
|
635
|
+
# We have at least one waiting change, possibly newly-added. We have no waiting_redraw_promise.
|
636
|
+
# Do we already have a redraw in-flight?
|
637
|
+
if @pending_redraw_promise
|
638
|
+
# Yes we do. Schedule a new waiting promise. When it turns into the pending_redraw_promise it will
|
639
|
+
# grab all waiting changes. In the mean time, it sits here and waits.
|
640
|
+
#
|
641
|
+
# We *could* do a fancy promise thing and have it update @waiting_changes for itself, etc, when it
|
642
|
+
# schedules itself. But we should always be calling promise_redraw or having a redraw fulfilled (see below)
|
643
|
+
# when these things change. I'd rather keep the logic in this method. It's easier to reason through
|
644
|
+
# all the cases.
|
645
|
+
@waiting_redraw_promise = ::Scarpe::Promise.new
|
666
646
|
|
667
|
-
@log.debug("
|
668
|
-
|
669
|
-
|
670
|
-
# Do we already have a redraw in-flight?
|
671
|
-
if @pending_redraw_promise
|
672
|
-
# Yes we do. Schedule a new waiting promise. When it turns into the pending_redraw_promise it will
|
673
|
-
# grab all waiting changes. In the mean time, it sits here and waits.
|
674
|
-
#
|
675
|
-
# We *could* do a fancy promise thing and have it update @waiting_changes for itself, etc, when it
|
676
|
-
# schedules itself. But we should always be calling promise_redraw or having a redraw fulfilled (see below)
|
677
|
-
# when these things change. I'd rather keep the logic in this method. It's easier to reason through
|
678
|
-
# all the cases.
|
679
|
-
@waiting_redraw_promise = Promise.new
|
680
|
-
|
681
|
-
@log.debug("Creating a new waiting promise since a pending promise is already in place")
|
682
|
-
return @waiting_redraw_promise
|
683
|
-
end
|
647
|
+
@log.debug("Creating a new waiting promise since a pending promise is already in place")
|
648
|
+
return @waiting_redraw_promise
|
649
|
+
end
|
684
650
|
|
685
|
-
|
686
|
-
|
651
|
+
# We have no redraw in-flight and no pre-existing waiting line. The new change(s) are presumably right
|
652
|
+
# after things were fully up-to-date. We can schedule them for immediate redraw.
|
687
653
|
|
688
|
-
|
689
|
-
|
690
|
-
|
654
|
+
@log.debug("Requesting redraw with #{@waiting_changes.size} waiting changes - scheduling a new redraw for them!")
|
655
|
+
promise = schedule_waiting_changes # This clears the waiting changes
|
656
|
+
@pending_redraw_promise = promise
|
691
657
|
|
692
|
-
|
693
|
-
|
694
|
-
|
658
|
+
promise.on_fulfilled do
|
659
|
+
@redraw_handlers.each(&:call)
|
660
|
+
@pending_redraw_promise = nil
|
695
661
|
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
662
|
+
if @waiting_redraw_promise
|
663
|
+
# While this redraw was in flight, more waiting changes got added and we made a promise
|
664
|
+
# about when they'd complete. Now they get scheduled, and we'll fulfill the waiting
|
665
|
+
# promise when that redraw finishes. Clear the old waiting promise. We'll add a new one
|
666
|
+
# when/if more changes are scheduled during this redraw.
|
667
|
+
old_waiting_promise = @waiting_redraw_promise
|
668
|
+
@waiting_redraw_promise = nil
|
703
669
|
|
704
|
-
|
670
|
+
@log.debug "Fulfilled redraw with #{@waiting_changes.size} waiting changes - scheduling a new redraw for them!"
|
705
671
|
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
end
|
719
|
-
else
|
720
|
-
@log.error "WHOAH, WHAT? My logic must be wrong, because there's " +
|
721
|
-
"no waiting promise, but waiting changes!"
|
672
|
+
new_promise = promise_redraw
|
673
|
+
new_promise.on_fulfilled { old_waiting_promise.fulfilled! }
|
674
|
+
else
|
675
|
+
# The in-flight redraw completed, and there's still no waiting promise. Good! That means
|
676
|
+
# we should be fully up-to-date.
|
677
|
+
@log.debug "Fulfilled redraw with no waiting changes - marking us as up to date!"
|
678
|
+
if @waiting_changes.empty?
|
679
|
+
# We're fully up to date! Fulfill the promise. Now we don't need it again until somebody asks
|
680
|
+
# us for another.
|
681
|
+
if @fully_up_to_date_promise
|
682
|
+
@fully_up_to_date_promise.fulfilled!
|
683
|
+
@fully_up_to_date_promise = nil
|
722
684
|
end
|
685
|
+
else
|
686
|
+
@log.error "WHOAH, WHAT? My logic must be wrong, because there's " +
|
687
|
+
"no waiting promise, but waiting changes!"
|
723
688
|
end
|
689
|
+
end
|
724
690
|
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
raise "JS Redraw failed! Bailing!"
|
691
|
+
@log.debug("Redraw is now fully up-to-date") if fully_updated?
|
692
|
+
end.on_rejected do
|
693
|
+
@log.error "Could not complete JS redraw! #{promise.reason.full_message}"
|
694
|
+
@log.debug("REDRAW FULLY UP TO DATE BUT JS FAILED") if fully_updated?
|
731
695
|
|
732
|
-
|
733
|
-
end
|
734
|
-
end
|
696
|
+
raise Scarpe::JSRedrawError, "JS Redraw failed! Bailing!"
|
735
697
|
|
736
|
-
|
737
|
-
@pending_redraw_promise.nil? && @waiting_redraw_promise.nil? && @waiting_changes.empty?
|
698
|
+
# Later we should figure out how to handle this. Clear the promises and queues and request another redraw?
|
738
699
|
end
|
700
|
+
end
|
739
701
|
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
# No changes to make, nothing in-process or waiting, so just return a pre-fulfilled promise
|
744
|
-
return Promise.fulfilled
|
745
|
-
end
|
702
|
+
def fully_updated?
|
703
|
+
@pending_redraw_promise.nil? && @waiting_redraw_promise.nil? && @waiting_changes.empty?
|
704
|
+
end
|
746
705
|
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
706
|
+
# Return a promise which will be fulfilled when the DOM is fully up-to-date
|
707
|
+
def promise_fully_updated
|
708
|
+
if fully_updated?
|
709
|
+
# No changes to make, nothing in-process or waiting, so just return a pre-fulfilled promise
|
710
|
+
return ::Scarpe::Promise.fulfilled
|
711
|
+
end
|
751
712
|
|
752
|
-
|
753
|
-
|
713
|
+
# Do we already have a promise for this? Return it. Everybody can share one.
|
714
|
+
if @fully_up_to_date_promise
|
715
|
+
return @fully_up_to_date_promise
|
754
716
|
end
|
755
717
|
|
756
|
-
|
718
|
+
# We're not fully updated, so we need a promise. Create it, return it.
|
719
|
+
@fully_up_to_date_promise = ::Scarpe::Promise.new
|
720
|
+
end
|
721
|
+
|
722
|
+
private
|
757
723
|
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
724
|
+
# Put together the waiting changes into a new in-flight redraw request.
|
725
|
+
# Return it as a promise.
|
726
|
+
def schedule_waiting_changes
|
727
|
+
return if @waiting_changes.empty?
|
762
728
|
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
end
|
729
|
+
js_code = @waiting_changes.join(";")
|
730
|
+
@waiting_changes = [] # They're not waiting any more!
|
731
|
+
@wrangler.eval_js_async(js_code)
|
767
732
|
end
|
768
733
|
end
|
769
|
-
end
|
770
734
|
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
735
|
+
# An ElementWrangler provides a way for a Drawable to manipulate is DOM element(s)
|
736
|
+
# via their HTML IDs. The most straightforward Drawables can have a single HTML ID
|
737
|
+
# and use a single ElementWrangler to make any needed changes.
|
738
|
+
#
|
739
|
+
# For now we don't need an ElementWrangler to add DOM elements, just to manipulate them
|
740
|
+
# after initial render. New DOM objects for Drawables are normally added via full
|
741
|
+
# redraws rather than incremental updates.
|
742
|
+
#
|
743
|
+
# Any changes made via ElementWrangler may be cancelled if a full redraw occurs,
|
744
|
+
# since it is assumed that small DOM manipulations are no longer needed. If a
|
745
|
+
# change would need to be made even if a full redraw occurred, it should be
|
746
|
+
# scheduled via WebWrangler#eval_js_async, not via an ElementWrangler.
|
747
|
+
class ElementWrangler
|
748
|
+
attr_reader :html_id
|
749
|
+
|
750
|
+
# Create an ElementWrangler for the given HTML ID or selector.
|
751
|
+
# The caller should provide exactly one of the html_id or selector.
|
780
752
|
#
|
781
|
-
#
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
@webwrangler = WebviewDisplayService.instance.wrangler
|
793
|
-
@html_id = html_id
|
753
|
+
# @param html_id [String] the HTML ID for the DOM element
|
754
|
+
def initialize(html_id: nil, selector: nil, multi: false)
|
755
|
+
@webwrangler = ::Scarpe::Webview::DisplayService.instance.wrangler
|
756
|
+
raise Scarpe::MissingWranglerError, "Can't get WebWrangler!" unless @webwrangler
|
757
|
+
|
758
|
+
if html_id && !selector
|
759
|
+
@selector = "document.getElementById('" + html_id + "')"
|
760
|
+
elsif selector && !html_id
|
761
|
+
@selector = selector
|
762
|
+
else
|
763
|
+
raise ArgumentError, "Must provide exactly one of html_id or selector!"
|
794
764
|
end
|
795
765
|
|
796
|
-
|
797
|
-
|
798
|
-
#
|
799
|
-
# @return [Scarpe::Promise] a promise that will be fulfilled when scheduled changes are complete
|
800
|
-
def promise_update
|
801
|
-
@webwrangler.dom_promise_redraw
|
802
|
-
end
|
766
|
+
@multi = multi
|
767
|
+
end
|
803
768
|
|
804
|
-
|
805
|
-
#
|
806
|
-
# @param new_value [String] the new value
|
807
|
-
# @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete
|
808
|
-
def value=(new_value)
|
809
|
-
@webwrangler.dom_change("document.getElementById('" + html_id + "').value = `" + new_value + "`; true")
|
810
|
-
end
|
769
|
+
private
|
811
770
|
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
@webwrangler.dom_change("document.getElementById('" + html_id + "').innerText = '" + new_text + "'; true")
|
771
|
+
def on_each(fragment)
|
772
|
+
if @multi
|
773
|
+
@webwrangler.dom_change("a = Array.from(#{@selector}); a.forEach((item) => item#{fragment}); true")
|
774
|
+
else
|
775
|
+
@webwrangler.dom_change(@selector + fragment + ";true")
|
818
776
|
end
|
777
|
+
end
|
819
778
|
|
820
|
-
|
821
|
-
#
|
822
|
-
# @param new_html [String] the new inner_html
|
823
|
-
# @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete
|
824
|
-
def inner_html=(new_html)
|
825
|
-
@webwrangler.dom_change("document.getElementById(\"" + html_id + "\").innerHTML = `" + new_html + "`; true")
|
826
|
-
end
|
779
|
+
public
|
827
780
|
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
end
|
781
|
+
# Return a promise that will be fulfilled when all changes scheduled via
|
782
|
+
# this ElementWrangler are verified complete.
|
783
|
+
#
|
784
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when scheduled changes are complete
|
785
|
+
def promise_update
|
786
|
+
@webwrangler.dom_promise_redraw
|
787
|
+
end
|
836
788
|
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
end
|
789
|
+
# Update the JS DOM element's value. The given Ruby value will be converted to string and assigned in backquotes.
|
790
|
+
#
|
791
|
+
# @param new_value [String] the new value
|
792
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete
|
793
|
+
def value=(new_value)
|
794
|
+
on_each(".value = `" + new_value + "`")
|
795
|
+
end
|
845
796
|
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
797
|
+
# Update the JS DOM element's inner_text. The given Ruby value will be converted to string and assigned in single-quotes.
|
798
|
+
#
|
799
|
+
# @param new_text [String] the new inner_text
|
800
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete
|
801
|
+
def inner_text=(new_text)
|
802
|
+
on_each(".innerText = '" + new_text + "'")
|
803
|
+
end
|
852
804
|
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
805
|
+
# Update the JS DOM element's inner_html. The given Ruby value will be converted to string and assigned in backquotes.
|
806
|
+
#
|
807
|
+
# @param new_html [String] the new inner_html
|
808
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete
|
809
|
+
def inner_html=(new_html)
|
810
|
+
on_each(".innerHTML = `" + new_html + "`")
|
811
|
+
end
|
812
|
+
|
813
|
+
# Update the JS DOM element's outer_html. The given Ruby value will be converted to string and assigned in backquotes.
|
814
|
+
#
|
815
|
+
# @param new_html [String] the new outer_html
|
816
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete
|
817
|
+
def outer_html=(new_html)
|
818
|
+
on_each(".outerHTML = `" + new_html + "`")
|
819
|
+
end
|
820
|
+
|
821
|
+
# Update the JS DOM element's attribute. The given Ruby value will be inspected and assigned.
|
822
|
+
#
|
823
|
+
# @param attribute [String] the attribute name
|
824
|
+
# @param value [String] the new attribute value
|
825
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete
|
826
|
+
def set_attribute(attribute, value)
|
827
|
+
on_each(".setAttribute(" + attribute.inspect + "," + value.inspect + ")")
|
828
|
+
end
|
829
|
+
|
830
|
+
# Update an attribute of the JS DOM element's style. The given Ruby value will be inspected and assigned.
|
831
|
+
#
|
832
|
+
# @param style_attr [String] the style attribute name
|
833
|
+
# @param value [String] the new style attribute value
|
834
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete
|
835
|
+
def set_style(style_attr, value)
|
836
|
+
on_each(".style.#{style_attr} = " + value.inspect + ";")
|
837
|
+
end
|
838
|
+
|
839
|
+
# Remove the specified DOM element
|
840
|
+
#
|
841
|
+
# @return [Scarpe::Promise] a promise that wil be fulfilled when the element is removed
|
842
|
+
def remove
|
843
|
+
on_each(".remove()")
|
844
|
+
end
|
845
|
+
|
846
|
+
def toggle_input_button(mark)
|
847
|
+
checked_value = mark ? "true" : "false"
|
848
|
+
on_each(".checked = #{checked_value}")
|
857
849
|
end
|
858
850
|
end
|
859
851
|
end
|