Class: Scarpe::WebviewWidget
- Inherits:
-
Shoes::Linkable
- Object
- Shoes::Linkable
- Scarpe::WebviewWidget
- Includes:
- Shoes::Log
- Defined in:
- lib/scarpe/wv/widget.rb
Overview
The WebviewWidget parent class helps connect a Webview widget with its Shoes equivalent, render itself to the Webview DOM, handle Javascript events and generally keep things working in Webview.
Direct Known Subclasses
WebviewAlert, WebviewApp, WebviewArc, WebviewButton, WebviewCheck, WebviewEditBox, WebviewEditLine, WebviewFont, WebviewImage, WebviewLine, WebviewLink, WebviewListBox, WebviewPara, WebviewRadio, WebviewShape, WebviewSlot, WebviewSpan, WebviewStar, WebviewSubscriptionItem, WebviewTextWidget, WebviewVideo
Constant Summary
Constants included from Shoes::Log
Shoes::Log::DEFAULT_COMPONENT, Shoes::Log::DEFAULT_DEBUG_LOG_CONFIG, Shoes::Log::DEFAULT_LOG_CONFIG
Instance Attribute Summary collapse
-
#children ⇒ Object
readonly
An array of WebviewWidget children (possibly empty) of this widget.
-
#parent ⇒ Object
readonly
The WebviewWidget parent of this widget.
-
#shoes_linkable_id ⇒ Object
readonly
The Shoes ID corresponding to the Shoes widget for this Webview widget.
Attributes inherited from Shoes::Linkable
Class Method Summary collapse
-
.display_class_for(scarpe_class_name) ⇒ Object
Return the corresponding Webview class for a particular Shoes class name.
Instance Method Summary collapse
-
#add_child(child) ⇒ Object
protected
Do not call directly, use set_parent.
-
#bind(event) { ... } ⇒ Object
This binds a Scarpe JS callback, handled via a single dispatch point in the app.
-
#destroy_self ⇒ Scarpe::Promise
Removes the element from both the Ruby Widget tree and the HTML DOM.
-
#handler_js_code(handler_function_name, *args) ⇒ String
Generate JS code to trigger a specific event name on this widget with the supplies arguments.
-
#html_element ⇒ Scarpe::WebWrangler::ElementWrangler
This gets a mini-webview for just this element and its children, if any.
-
#html_id ⇒ String
Get the object's HTML ID.
-
#initialize(properties) ⇒ WebviewWidget
constructor
Set instance variables for the display properties of this widget.
-
#inspect ⇒ Object
A shorter inspect text for prettier irb output.
-
#needs_update! ⇒ void
Request a full redraw of all widgets.
-
#promise_update ⇒ Scarpe::Promise
Return a promise that guarantees all currently-requested changes have completed.
-
#properties_changed(changes) ⇒ Object
Properties_changed will be called automatically when properties change.
-
#remove_child(child) ⇒ Object
protected
Do not call directly, use set_parent.
-
#rgb_to_hex(color) ⇒ Object
protected
Convert an [r, g, b, a] array to an HTML hex color code Arrays support alpha.
-
#set_parent(new_parent) ⇒ Object
Give this widget a new parent, including managing the appropriate child lists for parent widgets.
-
#style ⇒ Object
protected
CSS styles.
-
#to_html ⇒ String
to_html is intended to get the HTML DOM rendering of this object and its children.
Methods included from Shoes::Log
configure_logger, #log_init, logger
Methods inherited from Shoes::Linkable
#bind_shoes_event, #send_self_event, #send_shoes_event, #unsub_shoes_event
Constructor Details
#initialize(properties) ⇒ WebviewWidget
Set instance variables for the display properties of this widget. Bind Shoes events for changes of parent widget and changes of property values.
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/scarpe/wv/widget.rb', line 39 def initialize(properties) log_init("WV::Widget") # Call method, which looks up the parent @shoes_linkable_id = properties["shoes_linkable_id"] || properties[:shoes_linkable_id] unless @shoes_linkable_id raise "Could not find property shoes_linkable_id in #{properties.inspect}!" end # Set the display properties properties.each do |k, v| next if k == "shoes_linkable_id" instance_variable_set("@" + k.to_s, v) end # The parent field is *almost* simple enough that a typed display property would handle it. bind_shoes_event(event_name: "parent", target: shoes_linkable_id) do |new_parent_id| display_parent = WebviewDisplayService.instance.(new_parent_id) if @parent != display_parent set_parent(display_parent) end end # When Shoes widgets change properties, we get a change notification here bind_shoes_event(event_name: "prop_change", target: shoes_linkable_id) do |prop_changes| prop_changes.each do |k, v| instance_variable_set("@" + k, v) end properties_changed(prop_changes) end bind_shoes_event(event_name: "destroy", target: shoes_linkable_id) do destroy_self end super(linkable_id: @shoes_linkable_id) end |
Instance Attribute Details
#children ⇒ Object (readonly)
An array of WebviewWidget children (possibly empty) of this widget
35 36 37 |
# File 'lib/scarpe/wv/widget.rb', line 35 def children @children end |
#parent ⇒ Object (readonly)
The WebviewWidget parent of this widget
32 33 34 |
# File 'lib/scarpe/wv/widget.rb', line 32 def parent @parent end |
#shoes_linkable_id ⇒ Object (readonly)
The Shoes ID corresponding to the Shoes widget for this Webview widget
29 30 31 |
# File 'lib/scarpe/wv/widget.rb', line 29 def shoes_linkable_id @shoes_linkable_id end |
Class Method Details
.display_class_for(scarpe_class_name) ⇒ Object
Return the corresponding Webview class for a particular Shoes class name
12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# File 'lib/scarpe/wv/widget.rb', line 12 def display_class_for(scarpe_class_name) scarpe_class = Shoes.const_get(scarpe_class_name) unless scarpe_class.ancestors.include?(Shoes::Linkable) raise "Scarpe Webview can only get display classes for Shoes " + "linkable widgets, not #{scarpe_class_name.inspect}!" end klass = Scarpe.const_get("Webview" + scarpe_class_name.split("::")[-1]) if klass.nil? raise "Couldn't find corresponding Scarpe Webview class for #{scarpe_class_name.inspect}!" end klass end |
Instance Method Details
#add_child(child) ⇒ Object (protected)
Do not call directly, use set_parent
127 128 129 130 131 132 133 |
# File 'lib/scarpe/wv/widget.rb', line 127 def add_child(child) @children ||= [] @children << child # If we add a child, we should redraw ourselves needs_update! end |
#bind(event) { ... } ⇒ Object
This binds a Scarpe JS callback, handled via a single dispatch point in the app
212 213 214 215 216 |
# File 'lib/scarpe/wv/widget.rb', line 212 def bind(event, &block) raise("Widget has no linkable_id! #{inspect}") unless linkable_id WebviewDisplayService.instance.app.bind("#{linkable_id}-#{event}", &block) end |
#destroy_self ⇒ Scarpe::Promise
Removes the element from both the Ruby Widget tree and the HTML DOM. Return a promise for when that HTML change will be visible.
222 223 224 225 |
# File 'lib/scarpe/wv/widget.rb', line 222 def destroy_self @parent&.remove_child(self) html_element.remove end |
#handler_js_code(handler_function_name, *args) ⇒ String
Generate JS code to trigger a specific event name on this widget with the supplies arguments.
243 244 245 246 247 248 |
# File 'lib/scarpe/wv/widget.rb', line 243 def handler_js_code(handler_function_name, *args) raise("Widget has no linkable_id! #{inspect}") unless linkable_id js_args = ["'#{linkable_id}-#{handler_function_name}'", *args].join(", ") "scarpeHandler(#{js_args})" end |
#html_element ⇒ Scarpe::WebWrangler::ElementWrangler
This gets a mini-webview for just this element and its children, if any. It is normally called by the widget itself to do its DOM management.
176 177 178 |
# File 'lib/scarpe/wv/widget.rb', line 176 def html_element @elt_wrangler ||= Scarpe::WebWrangler::ElementWrangler.new(html_id) end |
#html_id ⇒ String
Get the object's HTML ID
190 191 192 |
# File 'lib/scarpe/wv/widget.rb', line 190 def html_id object_id.to_s end |
#inspect ⇒ Object
A shorter inspect text for prettier irb output
110 111 112 |
# File 'lib/scarpe/wv/widget.rb', line 110 def inspect "#<#{self.class}:#{self.object_id} @shoes_linkable_id=#{@shoes_linkable_id} @parent=#{@parent.inspect} @children=#{@children.inspect}>" end |
#needs_update! ⇒ void
This method returns an undefined value.
Request a full redraw of all widgets.
It's really hard to do dirty-tracking here because the redraws are fully asynchronous. And so we can't easily cancel one "in flight," and we can't easily pick up the latest changes... And we probably don't want to, because we may be halfway through a batch.
234 235 236 |
# File 'lib/scarpe/wv/widget.rb', line 234 def needs_update! WebviewDisplayService.instance.app.request_redraw! end |
#promise_update ⇒ Scarpe::Promise
Return a promise that guarantees all currently-requested changes have completed
183 184 185 |
# File 'lib/scarpe/wv/widget.rb', line 183 def promise_update html_element.promise_update end |
#properties_changed(changes) ⇒ Object
Properties_changed will be called automatically when properties change. The widget should delete any changes from the Hash that it knows how to incrementally handle, and pass the rest to super. If any changes go entirely un-handled, a full redraw will be scheduled. This exists to be overridden by children watching for changes.
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/scarpe/wv/widget.rb', line 85 def properties_changed(changes) # If a widget does something really nonstandard with its html_id or element, it will # need to override to prevent this from happening. That's easy enough, though. if changes.key?("hidden") hidden = changes.delete("hidden") if hidden html_element.set_style("display", "none") else new_style = style # Get current display CSS property, which may vary by subclass disp = new_style[:display] html_element.set_style("display", disp || "block") end end needs_update! unless changes.empty? end |
#remove_child(child) ⇒ Object (protected)
Do not call directly, use set_parent
117 118 119 120 121 122 123 124 |
# File 'lib/scarpe/wv/widget.rb', line 117 def remove_child(child) @children ||= [] unless @children.include?(child) @log.error("remove_child: no such child(#{child.inspect}) for"\ " parent(#{parent.inspect})!") end @children.delete(child) end |
#rgb_to_hex(color) ⇒ Object (protected)
Convert an [r, g, b, a] array to an HTML hex color code Arrays support alpha. HTML hex does not. So premultiply.
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/scarpe/wv/widget.rb', line 137 def rgb_to_hex(color) return color if color.nil? r, g, b, a = *color if r.is_a?(Float) a ||= 1.0 r_float = r * a g_float = g * a b_float = b * a else a ||= 255 a_float = (a / 255.0) r_float = (r.to_f / 255.0) * a_float g_float = (g.to_f / 255.0) * a_float b_float = (b.to_f / 255.0) * a_float end r_int = (r_float * 255.0).to_i.clamp(0, 255) g_int = (g_float * 255.0).to_i.clamp(0, 255) b_int = (b_float * 255.0).to_i.clamp(0, 255) "#%0.2X%0.2X%0.2X" % [r_int, g_int, b_int] end |
#set_parent(new_parent) ⇒ Object
Give this widget a new parent, including managing the appropriate child lists for parent widgets.
103 104 105 106 107 |
# File 'lib/scarpe/wv/widget.rb', line 103 def set_parent(new_parent) @parent&.remove_child(self) new_parent&.add_child(self) @parent = new_parent end |
#style ⇒ Object (protected)
CSS styles
162 163 164 165 166 167 168 |
# File 'lib/scarpe/wv/widget.rb', line 162 def style styles = {} if @hidden styles[:display] = "none" end styles end |
#to_html ⇒ String
to_html is intended to get the HTML DOM rendering of this object and its children. Calling it should be side-effect-free and NOT update the webview.
198 199 200 201 202 203 204 205 206 |
# File 'lib/scarpe/wv/widget.rb', line 198 def to_html @children ||= [] child_markup = @children.map(&:to_html).join if respond_to?(:element) element { child_markup } else child_markup end end |