From aa368dbd1c74a27b5c840b464cd314ead39c9240 Mon Sep 17 00:00:00 2001 From: Noah Gibbs Date: Fri, 29 Sep 2023 10:47:31 +0100 Subject: [PATCH 1/2] Add animate and every Shoes calls, plus pre-work for timer, leave, release and keypress. --- examples/animate.rb | 20 +++++++++++ lacci/lib/shoes/app.rb | 15 ++++---- lacci/lib/shoes/widgets/subscription_item.rb | 38 ++++++++++++++++++-- lib/scarpe/wv/subscription_item.rb | 36 ++++++++++++++++++- lib/scarpe/wv/web_wrangler.rb | 2 ++ 5 files changed, 98 insertions(+), 13 deletions(-) create mode 100644 examples/animate.rb diff --git a/examples/animate.rb b/examples/animate.rb new file mode 100644 index 000000000..8c5a0e93d --- /dev/null +++ b/examples/animate.rb @@ -0,0 +1,20 @@ +Shoes.app do + stack do + para "10 fps" + p = para "-" + animate do |frame| + p.replace(frame.to_s) + end + para "20 fps" + p2 = para "-" + animate(20) do |frame| + p2.replace(frame.to_s) + end + para "3spf" + p3 = para "-" + every(3) do |count| + p3.replace(count.to_s) + end + end +end + diff --git a/lacci/lib/shoes/app.rb b/lacci/lib/shoes/app.rb index 69545bebc..659f3d200 100644 --- a/lacci/lib/shoes/app.rb +++ b/lacci/lib/shoes/app.rb @@ -210,16 +210,13 @@ def border(...) current_slot.border(...) end - def motion(&block) - subscription_item(shoes_api_name: "motion", &block) - end - - def hover(&block) - subscription_item(shoes_api_name: "hover", &block) - end + # Event handler objects - def click(&block) - subscription_item(shoes_api_name: "click", &block) + events = [:motion, :hover, :leave, :click, :release, :keypress, :animate, :every, :timer] + events.each do |event| + define_method(event) do |*args, &block| + subscription_item(args:, shoes_api_name: event.to_s, &block) + end end # Draw context methods diff --git a/lacci/lib/shoes/widgets/subscription_item.rb b/lacci/lib/shoes/widgets/subscription_item.rb index 6978001b4..63745e798 100644 --- a/lacci/lib/shoes/widgets/subscription_item.rb +++ b/lacci/lib/shoes/widgets/subscription_item.rb @@ -9,20 +9,40 @@ # # Inheriting from Widget gives these a parent slot and a # linkable_id automatically. +# +# Events not yet implemented: start, finish events for slots - +# start is first draw, finish is widget destroyed class Shoes::SubscriptionItem < Shoes::Widget - display_property :shoes_api_name + display_properties :shoes_api_name, :args - def initialize(shoes_api_name:, &block) + def initialize(args: [], shoes_api_name:, &block) super @callback = block case shoes_api_name + when "animate" + @unsub_id = bind_self_event("animate") do |frame| + @callback.call(frame) + end + when "every" + @unsub_id = bind_self_event("every") do |count| + @callback.call(count) + end + when "timer" + @unsub_id = bind_self_event("timer") do + @callback.call + end when "hover" # Hover passes the Shoes widget as the block param @unsub_id = bind_self_event("hover") do @callback&.call(self) end + when "leave" + # Leave passes the Shoes widget as the block param + @unsub_id = bind_self_event("leave") do + @callback&.call(self) + end when "motion" # Shoes sends back x, y, mods as the args. # Shoes3 uses the strings "control" "shift" and @@ -37,8 +57,20 @@ def initialize(shoes_api_name:, &block) @unsub_id = bind_self_event("click") do |button, x, y, **_kwargs| @callback&.call(button, x, y) end + when "release" + # Click has block params button, left, top + # button is the button number, left and top are coords + @unsub_id = bind_self_event("click") do |button, x, y, **_kwargs| + @callback&.call(button, x, y) + end + when "keypress" + # Keypress passes the key string or symbol to the handler + # Do anything special for serialisation here? + @unsub_id = bind_self_event("click") do |key| + @callback&.call(key) + end else - raise "Unknown Shoes API call #{shoes_api_name.inspect} passed to SubscriptionItem!" + raise "Unknown Shoes event #{shoes_api_name.inspect} passed to SubscriptionItem!" end @unsub_id = bind_self_event(shoes_api_name) do |*args| diff --git a/lib/scarpe/wv/subscription_item.rb b/lib/scarpe/wv/subscription_item.rb index f4a401095..55f9ad9bc 100644 --- a/lib/scarpe/wv/subscription_item.rb +++ b/lib/scarpe/wv/subscription_item.rb @@ -8,6 +8,32 @@ def initialize(properties) bind(@shoes_api_name) do |*args| send_self_event(*args, event_name: @shoes_api_name) end + + @wrangler = Scarpe::Webview::DisplayService.instance.wrangler + + case @shoes_api_name + when "animate" + frame_rate = (@args[0] || 10) + @counter = 0 + @wrangler.periodic_code("animate_#{@shoes_linkable_id}", 1.0 / frame_rate) do + @counter += 1 + send_self_event(@counter, event_name: @shoes_api_name) + end + when "every" + delay = @args[0] + @counter = 0 + @wrangler.periodic_code("every_#{@shoes_linkable_id}", delay) do + @counter += 1 + send_self_event(@counter, event_name: @shoes_api_name) + end + when "timer" + # JS setTimeout? + raise "Implement me!" + when "motion", "hover", "leave", "click", "release", "keypress" + # Wait for set_parent + else + raise Scarpe::UnknownShoesEventAPIError, "Unknown Shoes event API: #{@shoes_api_name}!" + end end def element @@ -37,15 +63,23 @@ def set_parent(new_parent) ) when "hover" new_parent.set_event_callback(self, "onmouseenter", handler_js_code(@shoes_api_name)) + when "leave" + raise "Implement me!" when "click" new_parent.set_event_callback(self, "onclick", handler_js_code(@shoes_api_name, "arguments[0].button", "arguments[0].x", "arguments[0].y")) + when "release" + raise "Implement me!" + when "keypress" + raise "Implement me!" + when "animate", "every", "timer" + # These were handled in initialize(), ignore them here else raise Scarpe::UnknownShoesEventAPIError, "Unknown Shoes event API: #{@shoes_api_name}!" end end def destroy_self - @parent.remove_event_callbacks(self) + @parent&.remove_event_callbacks(self) super end end diff --git a/lib/scarpe/wv/web_wrangler.rb b/lib/scarpe/wv/web_wrangler.rb index b49aac0f7..cd3cd30f2 100644 --- a/lib/scarpe/wv/web_wrangler.rb +++ b/lib/scarpe/wv/web_wrangler.rb @@ -156,6 +156,8 @@ def init_code(name, &block) # so it should be invoked when the WebWrangler is in setup mode, # before the Webview is running. # + # TODO: add a way to stop this loop and unsubscribe. + # # @param name [String] the name of the Javascript init function, if needed # @param interval [Float] the duration between invoking this block # @yield the Ruby block to invoke periodically From 8f830a977085eb9a46dadb1469d899dc39dc5a2a Mon Sep 17 00:00:00 2001 From: Noah Gibbs Date: Fri, 6 Oct 2023 14:06:15 +0100 Subject: [PATCH 2/2] Add mouseup/mouseleave JS events to implement release/leave --- lib/scarpe/wv/subscription_item.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/scarpe/wv/subscription_item.rb b/lib/scarpe/wv/subscription_item.rb index 55f9ad9bc..8855f764e 100644 --- a/lib/scarpe/wv/subscription_item.rb +++ b/lib/scarpe/wv/subscription_item.rb @@ -64,11 +64,11 @@ def set_parent(new_parent) when "hover" new_parent.set_event_callback(self, "onmouseenter", handler_js_code(@shoes_api_name)) when "leave" - raise "Implement me!" + new_parent.set_event_callback(self, "onmouseleave", handler_js_code(@shoes_api_name)) when "click" new_parent.set_event_callback(self, "onclick", handler_js_code(@shoes_api_name, "arguments[0].button", "arguments[0].x", "arguments[0].y")) when "release" - raise "Implement me!" + new_parent.set_event_callback(self, "onmouseup", handler_js_code(@shoes_api_name, "arguments[0].button", "arguments[0].x", "arguments[0].y")) when "keypress" raise "Implement me!" when "animate", "every", "timer"