Skip to content

Commit

Permalink
Merge pull request #292 from scarpe-team/mouse_motion_spike
Browse files Browse the repository at this point in the history
Spike for events, including mouse motion
  • Loading branch information
noahgibbs authored Jul 7, 2023
2 parents e84e321 + 0507201 commit 19bc0f7
Show file tree
Hide file tree
Showing 19 changed files with 259 additions and 47 deletions.
20 changes: 20 additions & 0 deletions examples/motion_events.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Shoes.app width: 600, height: 600 do
stack do
para "Events and Menus"
flow do
@btn1 = button "button 1", width: 75 do
@eb.append "button 1 clicked\n"
end
click do
@eb.append "flow click\n"
end
hover do
@eb.append "flow hover\n"
end
end
@eb = edit_box width: 500, height: 350
end
motion do |x, y, mods|
@eb.append "motion #{x},#{y} #{mods} "
end
end
12 changes: 12 additions & 0 deletions lib/scarpe/app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -169,5 +169,17 @@ 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

def click(&block)
subscription_item(shoes_api_name: "click", &block)
end

alias_method :info, :puts
end
4 changes: 4 additions & 0 deletions lib/scarpe/display_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ def send_shoes_event(*args, event_name:, target: nil, **kwargs)
def bind_shoes_event(event_name:, target: nil, &handler)
DisplayService.subscribe_to_event(event_name, target, &handler)
end

def unsub_shoes_event(unsub_id)
DisplayService.unsub_from_events(unsub_id)
end
end

# These methods are an interface to DisplayService objects.
Expand Down
6 changes: 5 additions & 1 deletion lib/scarpe/document_root.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
# frozen_string_literal: true

class Scarpe
class DocumentRoot < Scarpe::Slot
class DocumentRoot < Scarpe::Flow
def initialize
@height = "100%"
@width = @margin = @padding = nil
@options = {}

super

create_display_widget
Expand Down
6 changes: 5 additions & 1 deletion lib/scarpe/edit_box.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class EditBox < Scarpe::Widget
display_properties :text, :height, :width

def initialize(text = nil, height: nil, width: nil, &block)
@text = text.nil? ? block&.call : text || ""
@text = (text.nil? ? block&.call : text) || ""

super

Expand All @@ -20,5 +20,9 @@ def initialize(text = nil, height: nil, width: nil, &block)
def change(&block)
@callback = block
end

def append(new_text)
self.text = self.text + new_text
end
end
end
4 changes: 3 additions & 1 deletion lib/scarpe/flow.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ class Scarpe
class Flow < Scarpe::Slot
display_properties :width, :height, :margin, :padding

def initialize(width: nil, height: "100%", margin: nil, padding: nil, &block)
def initialize(width: nil, height: nil, margin: nil, padding: nil, **options, &block)
@options = options

super

# Create the display-side widget *before* instance_eval, which will add child widgets with their display widgets
Expand Down
1 change: 1 addition & 0 deletions lib/scarpe/slot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
class Scarpe::Slot < Scarpe::Widget
include Scarpe::Background
include Scarpe::Border
include Scarpe::Spacing
end
2 changes: 1 addition & 1 deletion lib/scarpe/spacing.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
class Scarpe
module Spacing
def self.included(includer)
includer.display_properties :margin, :padding
includer.display_properties :margin, :padding, :margin_top, :margin_left, :margin_right, :margin_bottom, :options
end
end
end
8 changes: 3 additions & 5 deletions lib/scarpe/stack.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@

class Scarpe
class Stack < Scarpe::Slot
include Scarpe::Spacing
# TODO: sort out various margin and padding properties, including putting stuff into spacing
display_properties :width, :height, :scroll

display_properties :width, :height, :margin, :padding, :scroll, :margin_top, :margin_left, :margin_right, :margin_bottom, :options

def initialize(width: nil, height: "100%", margin: nil, padding: nil, scroll: false, margin_top: nil, margin_bottom: nil, margin_left: nil,
def initialize(width: nil, height: nil, margin: nil, padding: nil, scroll: false, margin_top: nil, margin_bottom: nil, margin_left: nil,
margin_right: nil, **options, &block)

# TODO: what are these options? Are they guaranteed serializable?
@options = options

super
Expand Down
60 changes: 60 additions & 0 deletions lib/scarpe/subscription_item.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# frozen_string_literal: true

# Certain Shoes calls like motion and keydown are basically an
# event subscription, with no other visible presence. However,
# they have a place in the widget tree and can be deleted.
#
# Depending on the display library they may not have any
# direct visual (or similar) presence there either.
#
# Inheriting from Widget gives these a parent slot and a
# linkable_id automatically.
class SubscriptionItem < Scarpe::Widget
display_property :shoes_api_name

def initialize(shoes_api_name:, &block)
super

@callback = block

case shoes_api_name
when "hover"
# Hover passes the Shoes widget as the block param
@unsub_id = bind_self_event("hover") do
@callback&.call(self)
end
when "motion"
# Shoes sends back x, y, mods as the args.
# Shoes3 uses the strings "control" "shift" and
# "control_shift" as the mods arg.
@unsub_id = bind_self_event("motion") do |x, y, ctrl_key, shift_key, **_kwargs|
mods = [ctrl_key ? "control" : nil, shift_key ? "shift" : nil].compact.join("_")
@callback&.call(x, y, mods)
end
when "click"
# 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
else
raise "Unknown Shoes API call #{shoes_api_name.inspect} passed to SubscriptionItem!"
end

@unsub_id = bind_self_event(shoes_api_name) do |*args|
@callback&.call(*args)
end

# This won't create a visible display widget, but will turn into
# an invisible widget and a stream of events.
create_display_widget
end

def destroy
# TODO: we need a better way to do this automatically. See https://github.com/scarpe-team/scarpe/issues/291
unsub_shoes_event(@unsub_id) if @unsub_id
@unsub_id = nil

super
end
end
3 changes: 2 additions & 1 deletion lib/scarpe/widgets.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@
require_relative "fill"

require_relative "slot"
require_relative "document_root"
require_relative "para"
require_relative "stack"
require_relative "flow"
require_relative "document_root"
require_relative "download"
require_relative "subscription_item"
require_relative "button"
require_relative "image"
require_relative "edit_box"
Expand Down
2 changes: 2 additions & 0 deletions lib/scarpe/wv.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@

require_relative "wv/app"
require_relative "wv/para"
require_relative "wv/slot"
require_relative "wv/stack"
require_relative "wv/flow"
require_relative "wv/document_root"
require_relative "wv/subscription_item"
require_relative "wv/button"
require_relative "wv/image"
require_relative "wv/edit_box"
Expand Down
15 changes: 2 additions & 13 deletions lib/scarpe/wv/flow.rb
Original file line number Diff line number Diff line change
@@ -1,30 +1,19 @@
# frozen_string_literal: true

class Scarpe
class WebviewFlow < Scarpe::WebviewWidget
include Scarpe::WebviewBackground
include Scarpe::WebviewBorder

class WebviewFlow < Scarpe::WebviewSlot
def initialize(properties)
super
end

def element(&block)
HTML.render do |h|
h.div(id: html_id, style:, &block)
end
end

private
protected

def style
styles = super

styles[:display] = "flex"
styles["flex-direction"] = "row"
styles["flex-wrap"] = "wrap"
styles[:width] = Dimensions.length(@width) if @width
styles[:height] = Dimensions.length(@height) if @height

styles
end
Expand Down
1 change: 1 addition & 0 deletions lib/scarpe/wv/shape.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require_relative "shape_helper"

class Scarpe
# Should inherit from Slot?
class WebviewShape < Scarpe::WebviewWidget
include ShapeHelper

Expand Down
81 changes: 81 additions & 0 deletions lib/scarpe/wv/slot.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# frozen_string_literal: true

class Scarpe
class WebviewSlot < Scarpe::WebviewWidget
include Scarpe::WebviewBackground
include Scarpe::WebviewBorder
include Scarpe::WebviewSpacing

def initialize(properties)
@event_callbacks = {}

super
end

def element(&block)
HTML.render do |h|
h.div(attributes.merge(id: html_id, style: style), &block)
end
end

def set_event_callback(obj, event_name, js_code)
event_name = event_name.to_s
@event_callbacks[event_name] ||= {}
if @event_callbacks[event_name][obj]
raise "Can't have two callbacks on the same event, from the same object, on the same parent!"
end

@event_callbacks[event_name][obj] = js_code

update_dom_event(event_name)
end

def remove_event_callback(obj, event_name)
event_name = event_name.to_s
@event_callbacks[event_name] ||= {}
@event_callbacks[event_name].delete(obj)

update_dom_event(event_name)
end

def remove_event_callbacks(obj)
changed = []

@event_callbacks.each do |event_name, items|
changed << event_name if items.delete(obj)
end

changed.each { |event_name| update_dom_event(event_name) }
end

protected

def update_dom_event(event_name)
html_element.set_attribute(event_name, @event_callbacks[event_name].values.join(";"))
end

def attributes
attr = {}

@event_callbacks.each do |event_name, handlers|
attr[event_name] = handlers.values.join(";")
end

attr
end

def style
styles = super

styles["margin-top"] = @margin_top if @margin_top
styles["margin-bottom"] = @margin_bottom if @margin_bottom
styles["margin-left"] = @margin_left if @margin_left
styles["margin-right"] = @margin_right if @margin_right

styles[:width] = Dimensions.length(@width) if @width
styles[:height] = Dimensions.length(@height) if @height

styles
end
end
end
2 changes: 1 addition & 1 deletion lib/scarpe/wv/spacing.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module WebviewSpacing
SPACING_DIRECTIONS = [:left, :right, :top, :bottom]

def style
styles = (super if defined?(super)) || {}
styles = defined?(super) ? super : {}

extract_spacing_styles_for(:margin, styles, @margin)
extract_spacing_styles_for(:padding, styles, @padding)
Expand Down
25 changes: 2 additions & 23 deletions lib/scarpe/wv/stack.rb
Original file line number Diff line number Diff line change
@@ -1,39 +1,18 @@
# frozen_string_literal: true

class Scarpe
class WebviewStack < Scarpe::WebviewWidget
include Scarpe::WebviewBackground
include Scarpe::WebviewBorder
include Scarpe::WebviewSpacing

def initialize(properties)
super
end

def element(&block)
HTML.render do |h|
h.div(id: html_id, style: style, &block)
end
end

class WebviewStack < Scarpe::WebviewSlot
def get_style
style
end

private
protected

def style
styles = super

styles["margin-top"] = @margin_top if @margin_top
styles["margin-bottom"] = @margin_bottom if @margin_bottom
styles["margin-left"] = @margin_left if @margin_left
styles["margin-right"] = @margin_right if @margin_right

styles[:display] = "flex"
styles["flex-direction"] = "column"
styles[:width] = Dimensions.length(@width) if @width
styles[:height] = Dimensions.length(@height) if @height
styles["overflow"] = "auto" if @scroll

styles
Expand Down
Loading

0 comments on commit 19bc0f7

Please sign in to comment.