scarpe-wasm 0.1.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 +7 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +21 -0
- data/README.md +39 -0
- data/Rakefile +12 -0
- data/lib/scarpe/wasm/alert.rb +65 -0
- data/lib/scarpe/wasm/app.rb +107 -0
- data/lib/scarpe/wasm/arc.rb +54 -0
- data/lib/scarpe/wasm/background.rb +27 -0
- data/lib/scarpe/wasm/border.rb +24 -0
- data/lib/scarpe/wasm/button.rb +50 -0
- data/lib/scarpe/wasm/check.rb +29 -0
- data/lib/scarpe/wasm/control_interface.rb +147 -0
- data/lib/scarpe/wasm/control_interface_test.rb +234 -0
- data/lib/scarpe/wasm/dimensions.rb +22 -0
- data/lib/scarpe/wasm/document_root.rb +8 -0
- data/lib/scarpe/wasm/edit_box.rb +44 -0
- data/lib/scarpe/wasm/edit_line.rb +43 -0
- data/lib/scarpe/wasm/flow.rb +24 -0
- data/lib/scarpe/wasm/font.rb +36 -0
- data/lib/scarpe/wasm/html.rb +108 -0
- data/lib/scarpe/wasm/image.rb +41 -0
- data/lib/scarpe/wasm/line.rb +35 -0
- data/lib/scarpe/wasm/link.rb +29 -0
- data/lib/scarpe/wasm/list_box.rb +50 -0
- data/lib/scarpe/wasm/para.rb +90 -0
- data/lib/scarpe/wasm/radio.rb +34 -0
- data/lib/scarpe/wasm/shape.rb +68 -0
- data/lib/scarpe/wasm/slot.rb +81 -0
- data/lib/scarpe/wasm/spacing.rb +41 -0
- data/lib/scarpe/wasm/span.rb +66 -0
- data/lib/scarpe/wasm/stack.rb +24 -0
- data/lib/scarpe/wasm/star.rb +61 -0
- data/lib/scarpe/wasm/subscription_item.rb +50 -0
- data/lib/scarpe/wasm/text_widget.rb +30 -0
- data/lib/scarpe/wasm/version.rb +7 -0
- data/lib/scarpe/wasm/video.rb +42 -0
- data/lib/scarpe/wasm/wasm_calls.rb +118 -0
- data/lib/scarpe/wasm/wasm_local_display.rb +94 -0
- data/lib/scarpe/wasm/web_wrangler.rb +679 -0
- data/lib/scarpe/wasm/webview_relay_display.rb +220 -0
- data/lib/scarpe/wasm/widget.rb +228 -0
- data/lib/scarpe/wasm/wv_display_worker.rb +75 -0
- data/lib/scarpe/wasm.rb +46 -0
- data/scarpe-wasm.gemspec +39 -0
- data/sig/scarpe/wasm.rbs +6 -0
- metadata +92 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "scarpe/base64"
|
4
|
+
|
5
|
+
class Scarpe
|
6
|
+
class WASMImage < WASMWidget
|
7
|
+
include Base64
|
8
|
+
def initialize(properties)
|
9
|
+
super
|
10
|
+
|
11
|
+
@url = valid_url?(@url) ? @url : "data:image/png;base64,#{encode_file_to_base64(@url)}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def element
|
15
|
+
if @click
|
16
|
+
HTML.render do |h|
|
17
|
+
h.a(id: html_id, href: @click) { h.img(id: html_id, src: @url, style:) }
|
18
|
+
end
|
19
|
+
else
|
20
|
+
HTML.render do |h|
|
21
|
+
h.img(id: html_id, src: @url, style:)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def style
|
29
|
+
styles = {}
|
30
|
+
|
31
|
+
styles[:width] = Dimensions.length(@width) if @width
|
32
|
+
styles[:height] = Dimensions.length(@height) if @height
|
33
|
+
|
34
|
+
styles[:top] = Dimensions.length(@top) if @top
|
35
|
+
styles[:left] = Dimensions.length(@left) if @left
|
36
|
+
styles[:position] = "absolute" if @top || @left
|
37
|
+
|
38
|
+
styles
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Scarpe
|
4
|
+
class WASMLine < Scarpe::WASMWidget
|
5
|
+
def initialize(properties)
|
6
|
+
super(properties)
|
7
|
+
end
|
8
|
+
|
9
|
+
def element
|
10
|
+
HTML.render do |h|
|
11
|
+
h.div(id: html_id, style: style) do
|
12
|
+
h.svg(width: @x2, height: @y2) do
|
13
|
+
h.line(x1: @left, y1: @top, x2: @x2, y2: @y2, style: line_style)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def style
|
22
|
+
{
|
23
|
+
left: "#{@left}px",
|
24
|
+
top: "#{@top}px",
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def line_style
|
29
|
+
{
|
30
|
+
stroke: @draw_context["stroke"],
|
31
|
+
"stroke-width": "4",
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Scarpe
|
4
|
+
class WASMLink < WASMWidget
|
5
|
+
def initialize(properties)
|
6
|
+
super
|
7
|
+
|
8
|
+
bind("click") do
|
9
|
+
send_self_event(event_name: "click")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def element
|
14
|
+
HTML.render do |h|
|
15
|
+
h.a(**attributes) do
|
16
|
+
@text
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def attributes
|
22
|
+
{
|
23
|
+
id: html_id,
|
24
|
+
href: @click,
|
25
|
+
onclick: (handler_js_code("click") if @has_block),
|
26
|
+
}.compact
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Scarpe
|
4
|
+
class WASMListBox < Scarpe::WASMWidget
|
5
|
+
attr_reader :selected_item, :items, :height, :width
|
6
|
+
|
7
|
+
def initialize(properties)
|
8
|
+
super(properties)
|
9
|
+
|
10
|
+
# The JS handler sends a "change" event, which we forward to the Shoes widget tree
|
11
|
+
bind("change") do |new_item|
|
12
|
+
send_self_event(new_item, event_name: "change")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def properties_changed(changes)
|
17
|
+
selected = changes.delete("selected_item")
|
18
|
+
if selected
|
19
|
+
html_element.value = selected
|
20
|
+
end
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
24
|
+
def element
|
25
|
+
onchange = handler_js_code("change", "this.options[this.selectedIndex].value")
|
26
|
+
|
27
|
+
select_attrs = { id: html_id, onchange: onchange, style: style }
|
28
|
+
option_attrs = { value: nil, selected: false }
|
29
|
+
|
30
|
+
HTML.render do |h|
|
31
|
+
h.select(**select_attrs) do
|
32
|
+
items.each do |item|
|
33
|
+
h.option(**option_attrs, value: item, selected: (item == selected_item)) { item }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def style
|
42
|
+
styles = {}
|
43
|
+
|
44
|
+
styles[:height] = Dimensions.length(height) if height
|
45
|
+
styles[:width] = Dimensions.length(width) if width
|
46
|
+
|
47
|
+
styles.compact
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Scarpe
|
4
|
+
class WASMPara < WASMWidget
|
5
|
+
SIZES = {
|
6
|
+
inscription: 10,
|
7
|
+
ins: 10,
|
8
|
+
para: 12,
|
9
|
+
caption: 14,
|
10
|
+
tagline: 18,
|
11
|
+
subtitle: 26,
|
12
|
+
title: 34,
|
13
|
+
banner: 48,
|
14
|
+
}.freeze
|
15
|
+
private_constant :SIZES
|
16
|
+
|
17
|
+
def initialize(properties)
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
def properties_changed(changes)
|
22
|
+
items = changes.delete("text_items")
|
23
|
+
if items
|
24
|
+
html_element.inner_html = to_html
|
25
|
+
return
|
26
|
+
end
|
27
|
+
|
28
|
+
# Not deleting, so this will re-render
|
29
|
+
if changes["size"] && SIZES[@size.to_sym]
|
30
|
+
@size = @size.to_sym
|
31
|
+
end
|
32
|
+
|
33
|
+
super
|
34
|
+
end
|
35
|
+
|
36
|
+
def items_to_display_children(items)
|
37
|
+
return [] if items.nil?
|
38
|
+
|
39
|
+
items.map do |item|
|
40
|
+
if item.is_a?(String)
|
41
|
+
item
|
42
|
+
else
|
43
|
+
WASMDisplayService.instance.query_display_widget_for(item)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def element(&block)
|
49
|
+
HTML.render do |h|
|
50
|
+
h.p(**options, &block)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_html
|
55
|
+
@children ||= []
|
56
|
+
|
57
|
+
element { child_markup }
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def child_markup
|
63
|
+
items_to_display_children(@text_items).map do |child|
|
64
|
+
if child.respond_to?(:to_html)
|
65
|
+
child.to_html
|
66
|
+
else
|
67
|
+
child.gsub("\n", "<br>")
|
68
|
+
end
|
69
|
+
end.join
|
70
|
+
end
|
71
|
+
|
72
|
+
def options
|
73
|
+
@html_attributes.merge(id: html_id, style: style)
|
74
|
+
end
|
75
|
+
|
76
|
+
def style
|
77
|
+
{
|
78
|
+
"color" => rgb_to_hex(@stroke),
|
79
|
+
"font-size" => font_size,
|
80
|
+
"font-family" => @font,
|
81
|
+
}.compact
|
82
|
+
end
|
83
|
+
|
84
|
+
def font_size
|
85
|
+
font_size = @size.is_a?(Symbol) ? SIZES[@size] : @size
|
86
|
+
|
87
|
+
Dimensions.length(font_size)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Scarpe
|
4
|
+
class WASMRadio < Scarpe::WASMWidget
|
5
|
+
attr_reader :text
|
6
|
+
|
7
|
+
def initialize(properties)
|
8
|
+
super
|
9
|
+
|
10
|
+
bind("click") do
|
11
|
+
send_self_event(event_name: "click", target: shoes_linkable_id)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def properties_changed(changes)
|
16
|
+
items = changes.delete("checked")
|
17
|
+
html_element.toggle_input_button(items)
|
18
|
+
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
def element
|
23
|
+
HTML.render do |h|
|
24
|
+
h.input(type: :radio, id: html_id, onclick: handler_js_code("click"), name: group_name, value: "hmm #{text}", checked: @checked)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def group_name
|
31
|
+
@group || @parent
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Scarpe
|
4
|
+
# Should inherit from Slot?
|
5
|
+
class WASMShape < Scarpe::WASMWidget
|
6
|
+
def initialize(properties)
|
7
|
+
super(properties)
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_html
|
11
|
+
@children ||= []
|
12
|
+
child_markup = @children.map(&:to_html).join
|
13
|
+
|
14
|
+
color = @draw_context["fill"] || "black"
|
15
|
+
self_markup = HTML.render do |h|
|
16
|
+
h.div(id: html_id, style: style) do
|
17
|
+
h.svg(width: "400", height: "500") do
|
18
|
+
h.path(d: path_from_shape_commands, style: "fill:#{color};stroke-width:2;")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Put child markup first for backward compatibility, but I'm pretty sure this is wrong.
|
24
|
+
child_markup + self_markup
|
25
|
+
end
|
26
|
+
|
27
|
+
def element(&block)
|
28
|
+
color = @draw_context["fill"] || "black"
|
29
|
+
HTML.render do |h|
|
30
|
+
h.div(id: html_id, style: style) do
|
31
|
+
h.svg(width: "400", height: "500") do
|
32
|
+
h.path(d: path_from_shape_commands, style: "fill:#{color};stroke-width:2;")
|
33
|
+
end
|
34
|
+
block.call(h) if block_given?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# We have a set of Shoes shape commands, but we need SVG objects like paths.
|
42
|
+
def path_from_shape_commands
|
43
|
+
current_path = ""
|
44
|
+
|
45
|
+
@shape_commands.each do |cmd, *args|
|
46
|
+
case cmd
|
47
|
+
when "move_to"
|
48
|
+
x, y = *args
|
49
|
+
current_path += "M #{x} #{y} "
|
50
|
+
when "line_to"
|
51
|
+
x, y = *args
|
52
|
+
current_path += "L #{x} #{y} "
|
53
|
+
else
|
54
|
+
raise "Unknown shape command! #{cmd.inspect}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
current_path
|
59
|
+
end
|
60
|
+
|
61
|
+
def style
|
62
|
+
{
|
63
|
+
width: "400",
|
64
|
+
height: "900",
|
65
|
+
}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Scarpe
|
4
|
+
class WASMSlot < Scarpe::WASMWidget
|
5
|
+
include Scarpe::WASMBackground
|
6
|
+
include Scarpe::WASMBorder
|
7
|
+
include Scarpe::WASMSpacing
|
8
|
+
|
9
|
+
def initialize(properties)
|
10
|
+
@event_callbacks = {}
|
11
|
+
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
def element(&block)
|
16
|
+
HTML.render do |h|
|
17
|
+
h.div(attributes.merge(id: html_id, style: style), &block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def set_event_callback(obj, event_name, js_code)
|
22
|
+
event_name = event_name.to_s
|
23
|
+
@event_callbacks[event_name] ||= {}
|
24
|
+
if @event_callbacks[event_name][obj]
|
25
|
+
raise "Can't have two callbacks on the same event, from the same object, on the same parent!"
|
26
|
+
end
|
27
|
+
|
28
|
+
@event_callbacks[event_name][obj] = js_code
|
29
|
+
|
30
|
+
update_dom_event(event_name)
|
31
|
+
end
|
32
|
+
|
33
|
+
def remove_event_callback(obj, event_name)
|
34
|
+
event_name = event_name.to_s
|
35
|
+
@event_callbacks[event_name] ||= {}
|
36
|
+
@event_callbacks[event_name].delete(obj)
|
37
|
+
|
38
|
+
update_dom_event(event_name)
|
39
|
+
end
|
40
|
+
|
41
|
+
def remove_event_callbacks(obj)
|
42
|
+
changed = []
|
43
|
+
|
44
|
+
@event_callbacks.each do |event_name, items|
|
45
|
+
changed << event_name if items.delete(obj)
|
46
|
+
end
|
47
|
+
|
48
|
+
changed.each { |event_name| update_dom_event(event_name) }
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
def update_dom_event(event_name)
|
54
|
+
html_element.set_attribute(event_name, @event_callbacks[event_name].values.join(";"))
|
55
|
+
end
|
56
|
+
|
57
|
+
def attributes
|
58
|
+
attr = {}
|
59
|
+
|
60
|
+
@event_callbacks.each do |event_name, handlers|
|
61
|
+
attr[event_name] = handlers.values.join(";")
|
62
|
+
end
|
63
|
+
|
64
|
+
attr
|
65
|
+
end
|
66
|
+
|
67
|
+
def style
|
68
|
+
styles = super
|
69
|
+
|
70
|
+
styles["margin-top"] = @margin_top if @margin_top
|
71
|
+
styles["margin-bottom"] = @margin_bottom if @margin_bottom
|
72
|
+
styles["margin-left"] = @margin_left if @margin_left
|
73
|
+
styles["margin-right"] = @margin_right if @margin_right
|
74
|
+
|
75
|
+
styles[:width] = Dimensions.length(@width) if @width
|
76
|
+
styles[:height] = Dimensions.length(@height) if @height
|
77
|
+
|
78
|
+
styles
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Scarpe
|
4
|
+
module WASMSpacing
|
5
|
+
SPACING_DIRECTIONS = [:left, :right, :top, :bottom]
|
6
|
+
|
7
|
+
def style
|
8
|
+
styles = defined?(super) ? super : {}
|
9
|
+
|
10
|
+
extract_spacing_styles_for(:margin, styles, @margin)
|
11
|
+
extract_spacing_styles_for(:padding, styles, @padding)
|
12
|
+
|
13
|
+
styles
|
14
|
+
end
|
15
|
+
|
16
|
+
def extract_spacing_styles_for(attribute, styles, values)
|
17
|
+
values ||= spacing_values_from_options(attribute)
|
18
|
+
|
19
|
+
case values
|
20
|
+
when Hash
|
21
|
+
values.each do |direction, value|
|
22
|
+
styles["#{attribute}-#{direction}"] = Dimensions.length(value)
|
23
|
+
end
|
24
|
+
when Array
|
25
|
+
SPACING_DIRECTIONS.zip(values).to_h.compact.each do |direction, value|
|
26
|
+
styles["#{attribute}-#{direction}"] = Dimensions.length(value)
|
27
|
+
end
|
28
|
+
else
|
29
|
+
styles[attribute] = Dimensions.length(values)
|
30
|
+
end
|
31
|
+
|
32
|
+
styles.compact!
|
33
|
+
end
|
34
|
+
|
35
|
+
def spacing_values_from_options(attribute)
|
36
|
+
SPACING_DIRECTIONS.map do |direction|
|
37
|
+
@options.delete("#{attribute}_#{direction}".to_sym)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Scarpe
|
4
|
+
class WASMSpan < Scarpe::WASMWidget
|
5
|
+
SIZES = {
|
6
|
+
inscription: 10,
|
7
|
+
ins: 10,
|
8
|
+
span: 12,
|
9
|
+
caption: 14,
|
10
|
+
tagline: 18,
|
11
|
+
subtitle: 26,
|
12
|
+
title: 34,
|
13
|
+
banner: 48,
|
14
|
+
}.freeze
|
15
|
+
private_constant :SIZES
|
16
|
+
|
17
|
+
def initialize(properties)
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
def properties_changed(changes)
|
22
|
+
text = changes.delete("text")
|
23
|
+
if text
|
24
|
+
html_element.inner_html = text
|
25
|
+
return
|
26
|
+
end
|
27
|
+
|
28
|
+
# Not deleting, so this will re-render
|
29
|
+
if changes["size"] && SIZES[@size.to_sym]
|
30
|
+
@size = @size.to_sym
|
31
|
+
end
|
32
|
+
|
33
|
+
super
|
34
|
+
end
|
35
|
+
|
36
|
+
def element(&block)
|
37
|
+
HTML.render do |h|
|
38
|
+
h.span(**options, &block)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_html
|
43
|
+
element { @text }
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def options
|
49
|
+
@html_attributes.merge(id: html_id, style: style)
|
50
|
+
end
|
51
|
+
|
52
|
+
def style
|
53
|
+
{
|
54
|
+
"color" => @stroke,
|
55
|
+
"font-size" => font_size,
|
56
|
+
"font-family" => @font,
|
57
|
+
}.compact
|
58
|
+
end
|
59
|
+
|
60
|
+
def font_size
|
61
|
+
font_size = @size.is_a?(Symbol) ? SIZES[@size] : @size
|
62
|
+
|
63
|
+
Dimensions.length(font_size)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Scarpe
|
4
|
+
class WASMStack < Scarpe::WASMSlot
|
5
|
+
def get_style
|
6
|
+
style
|
7
|
+
end
|
8
|
+
|
9
|
+
protected
|
10
|
+
|
11
|
+
def style
|
12
|
+
styles = super
|
13
|
+
|
14
|
+
styles[:display] = "flex"
|
15
|
+
styles["flex-direction"] = "column"
|
16
|
+
styles["align-content"] = "flex-start"
|
17
|
+
styles["justify-content"] = "flex-start"
|
18
|
+
styles["align-items"] = "flex-start"
|
19
|
+
styles["overflow"] = "auto" if @scroll
|
20
|
+
|
21
|
+
styles
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Scarpe
|
4
|
+
class WASMStar < Scarpe::WASMWidget
|
5
|
+
def initialize(properties)
|
6
|
+
super(properties)
|
7
|
+
end
|
8
|
+
|
9
|
+
def element(&block)
|
10
|
+
fill = @draw_context["fill"]
|
11
|
+
stroke = @draw_context["stroke"]
|
12
|
+
fill = "black" if fill == ""
|
13
|
+
stroke = "black" if stroke == ""
|
14
|
+
HTML.render do |h|
|
15
|
+
h.div(id: html_id, style: style) do
|
16
|
+
h.svg(width: @outer, height: @outer, style: "fill:#{fill};") do
|
17
|
+
h.polygon(points: star_points, style: "stroke:#{stroke};stroke-width:2")
|
18
|
+
end
|
19
|
+
block.call(h) if block_given?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def style
|
27
|
+
{
|
28
|
+
width: Dimensions.length(@width),
|
29
|
+
height: Dimensions.length(@height),
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def star_points
|
34
|
+
get_star_points.join(",")
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_star_points
|
38
|
+
angle = 2 * Math::PI / @points
|
39
|
+
coordinates = []
|
40
|
+
|
41
|
+
@points.times do |i|
|
42
|
+
outer_angle = i * angle
|
43
|
+
inner_angle = outer_angle + angle / 2
|
44
|
+
|
45
|
+
coordinates.concat(get_coordinates(outer_angle, inner_angle))
|
46
|
+
end
|
47
|
+
|
48
|
+
coordinates
|
49
|
+
end
|
50
|
+
|
51
|
+
def get_coordinates(outer_angle, inner_angle)
|
52
|
+
outer_x = @outer / 2 + Math.cos(outer_angle) * @outer / 2
|
53
|
+
outer_y = @outer / 2 + Math.sin(outer_angle) * @outer / 2
|
54
|
+
|
55
|
+
inner_x = @outer / 2 + Math.cos(inner_angle) * @inner / 2
|
56
|
+
inner_y = @outer / 2 + Math.sin(inner_angle) * @inner / 2
|
57
|
+
|
58
|
+
[outer_x, outer_y, inner_x, inner_y]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Scarpe::WASMSubscriptionItem < Scarpe::WASMWidget
|
4
|
+
def initialize(properties)
|
5
|
+
super
|
6
|
+
|
7
|
+
bind(@shoes_api_name) do |*args|
|
8
|
+
send_self_event(*args, event_name: @shoes_api_name)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def element
|
13
|
+
""
|
14
|
+
end
|
15
|
+
|
16
|
+
# This will get called once we know the parent, which is useful for events
|
17
|
+
# like hover, where our subscription is likely to depend on what our parent is.
|
18
|
+
def set_parent(new_parent)
|
19
|
+
super
|
20
|
+
|
21
|
+
case @shoes_api_name
|
22
|
+
when "motion"
|
23
|
+
# TODO: what do we do for whole-screen mousemove outside the window?
|
24
|
+
# Those should be set on body, which right now doesn't have a widget.
|
25
|
+
# TODO: figure out how to handle alt and meta keys - does Shoes3 recognise those?
|
26
|
+
new_parent.set_event_callback(
|
27
|
+
self,
|
28
|
+
"onmousemove",
|
29
|
+
handler_js_code(
|
30
|
+
@shoes_api_name,
|
31
|
+
"arguments[0].x",
|
32
|
+
"arguments[0].y",
|
33
|
+
"arguments[0].ctrlKey",
|
34
|
+
"arguments[0].shiftKey",
|
35
|
+
),
|
36
|
+
)
|
37
|
+
when "hover"
|
38
|
+
new_parent.set_event_callback(self, "onmouseenter", handler_js_code(@shoes_api_name))
|
39
|
+
when "click"
|
40
|
+
new_parent.set_event_callback(self, "onclick", handler_js_code(@shoes_api_name, "arguments[0].button", "arguments[0].x", "arguments[0].y"))
|
41
|
+
else
|
42
|
+
raise "Unknown Shoes event API: #{@shoes_api_name}!"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def destroy_self
|
47
|
+
@parent.remove_event_callbacks(self)
|
48
|
+
super
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Scarpe
|
4
|
+
class WASMTextWidget < Scarpe::WASMWidget
|
5
|
+
end
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def default_wasm_text_widget_with(element)
|
9
|
+
wasm_class_name = "WASM#{element.capitalize}"
|
10
|
+
wasm_widget_class = Class.new(Scarpe::WASMTextWidget) do
|
11
|
+
def initialize(properties)
|
12
|
+
class_name = self.class.name.split("::")[-1]
|
13
|
+
@html_tag = class_name.delete_prefix("WASM").downcase
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def element
|
18
|
+
HTML.render do |h|
|
19
|
+
h.send(@html_tag) { @content.to_s }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
Scarpe.const_set wasm_class_name, wasm_widget_class
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
Scarpe.default_wasm_text_widget_with(:code)
|
29
|
+
Scarpe.default_wasm_text_widget_with(:em)
|
30
|
+
Scarpe.default_wasm_text_widget_with(:strong)
|