diff --git a/CHANGELOG.md b/CHANGELOG.md index b2192cf6d..ced639c84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,11 +23,12 @@ still making big changes. ### Enhancements - Ovals! +- Lots more text methods: del, sub, sup; lots more text styles: underline, strikethrough, strikecolor, align - Features! Shoes.app(feature: [:html, :scarpe]) lets apps declare dependencies on non-classic Shoes! +- Better handling of :left, :top, :width and :height, :margin and :padding on more drawables - The html_class style is a feature to make it easier to do Bootstrap styling on your drawables - Directly run Shoes Specs, including with Niente - We use Minitest assertion DSL rather than our own everywhere now -- Better handling of :left, :top, :width and :height, :margin and :padding on more drawables ### Bugs Fixed @@ -36,6 +37,7 @@ still making big changes. ### Incompatibilities +TextDrawables now draw with very different Calzini (HTML renderer) properties We're deprecating the CatsCradle test DSL in favour of Shoes-Spec. Some error names have changed, with more to come. We've changed the Lacci drawable-create event to include the parent ID. diff --git a/examples/span.rb b/examples/span.rb index cd0bb98f3..1c841190f 100644 --- a/examples/span.rb +++ b/examples/span.rb @@ -1,6 +1,8 @@ Shoes.app :height => 500, :width => 500 do stack :margin => 10 do - para span("TEXT EDITOR", :stroke => "blue", :fill => "green"), " * USE ALT-Q TO QUIT", :stroke => "red" + para span("TEXT EDITOR", :stroke => blue, :fill => green), " * USE ALT-Q TO QUIT", :stroke => red end - span ("text") + para "Various ", del("text"), " in ", sub("various"), " ", sup("styles"), " can be ", ins("hard to read"), "...\n" + + para "A ", span("wide", underline: "single", undercolor: blue), " ", span("variety", underline: "error", undercolor: green), " ", span("of", underline: "double"), " ", span("underlines", underline: "low", undercolor: darkgreen) end diff --git a/lacci/lib/shoes/drawables.rb b/lacci/lib/shoes/drawables.rb index 375c04f99..951dc8195 100644 --- a/lacci/lib/shoes/drawables.rb +++ b/lacci/lib/shoes/drawables.rb @@ -27,6 +27,5 @@ require "shoes/drawables/list_box" require "shoes/drawables/para" require "shoes/drawables/radio" -require "shoes/drawables/span" require "shoes/drawables/video" require "shoes/drawables/progress" diff --git a/lacci/lib/shoes/drawables/edit_box.rb b/lacci/lib/shoes/drawables/edit_box.rb index 4f6f7c022..be562a994 100644 --- a/lacci/lib/shoes/drawables/edit_box.rb +++ b/lacci/lib/shoes/drawables/edit_box.rb @@ -24,7 +24,7 @@ def change(&block) end def append(new_text) - self.text = self.text + new_text + self.text = (self.text || "") + new_text end end end diff --git a/lacci/lib/shoes/drawables/link.rb b/lacci/lib/shoes/drawables/link.rb index 54226001a..f40e09ce6 100644 --- a/lacci/lib/shoes/drawables/link.rb +++ b/lacci/lib/shoes/drawables/link.rb @@ -5,10 +5,10 @@ class Link < Shoes::TextDrawable shoes_styles :text, :click, :has_block shoes_events :click - Shoes::Drawable.drawable_default_styles[Shoes::Link][:click] = "#" + #Shoes::Drawable.drawable_default_styles[Shoes::Link][:click] = "#" - init_args :text - def initialize(text, click: nil, &block) + init_args # Empty by the time it reaches Drawable#initialize + def initialize(*args, **kwargs, &block) @block = block # We can't send a block to the display drawable, but we can send a boolean @has_block = !block.nil? @@ -18,8 +18,6 @@ def initialize(text, click: nil, &block) bind_self_event("click") do @block&.call end - - create_display_drawable end end @@ -27,7 +25,7 @@ def initialize(text, click: nil, &block) # hovered over. The functionality isn't present in Lacci yet. class LinkHover < Link def initialize - raise "This class should never be instantiated! Use link, not link_hover!" + raise "This class should never be instantiated directly! Use link, not link_hover!" end end end diff --git a/lacci/lib/shoes/drawables/para.rb b/lacci/lib/shoes/drawables/para.rb index 701ef7117..fb93c9232 100644 --- a/lacci/lib/shoes/drawables/para.rb +++ b/lacci/lib/shoes/drawables/para.rb @@ -3,7 +3,8 @@ class Shoes class Para < Shoes::Drawable shoes_styles :text_items, :size, :font - shoes_style(:stroke) { |val| Shoes::Colors.to_rgb(val) } + shoes_style(:stroke) { |val, _name| Shoes::Colors.to_rgb(val) } + shoes_style(:fill) { |val, _name| Shoes::Colors.to_rgb(val) } shoes_style(:align) do |val| unless ["left", "center", "right"].include?(val) @@ -51,7 +52,7 @@ def initialize(*args, **kwargs) private def text_children_to_items(text_children) - text_children.map { |arg| arg.is_a?(String) ? arg : arg.linkable_id } + text_children.map { |arg| arg.is_a?(TextDrawable) ? arg.linkable_id : arg.to_s } end public @@ -159,7 +160,5 @@ def caption(*args, **kwargs) def inscription(*args, **kwargs) para(*args, **{ size: :inscription }.merge(kwargs)) end - - alias_method :ins, :inscription end end diff --git a/lacci/lib/shoes/drawables/span.rb b/lacci/lib/shoes/drawables/span.rb deleted file mode 100644 index cca5f001b..000000000 --- a/lacci/lib/shoes/drawables/span.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true - -class Shoes - class Span < Shoes::Drawable - shoes_styles :text, :stroke, :fill, :size, :font, :html_attributes - shoes_events # No Span-specific events yet - - Shoes::Drawable.drawable_default_styles[Shoes::Span][:size] = :span - - init_args - opt_init_args :text, :stroke, :size, :font - def initialize(*args, **html_attributes) - super - - @html_attributes = html_attributes - - create_display_drawable - end - - def replace(text) - @text = text - - # This should signal the display drawable to change - self.text = @text - end - end -end diff --git a/lacci/lib/shoes/drawables/text_drawable.rb b/lacci/lib/shoes/drawables/text_drawable.rb index 36157bed6..844de9d70 100644 --- a/lacci/lib/shoes/drawables/text_drawable.rb +++ b/lacci/lib/shoes/drawables/text_drawable.rb @@ -9,23 +9,89 @@ class Shoes # have methods app, contents, children, parent, # style, to_s, text, text= and replace. # - # We don't currently allow things like em("oh", strong("hi!")), - # so we'll need a rework to match the old interface at - # some point. + # Much of what this does and how is similar to Para. + # It's a very similar API. class TextDrawable < Shoes::Drawable - class << self - # rubocop:disable Lint/MissingSuper - def inherited(subclass) - Shoes::Drawable.drawable_classes ||= [] - Shoes::Drawable.drawable_classes << subclass - - Shoes::Drawable.drawable_default_styles ||= {} - Shoes::Drawable.drawable_default_styles[subclass] = {} + shoes_styles :text_items, :size, :stroke, :strokewidth, :fill, :undercolor, :font + + STRIKETHROUGH_VALUES = [nil, "none", "single"] + shoes_style :strikethrough do |val, _name| + unless STRIKETHROUGH_VALUES.include?(val) + raise Shoes::Errors::InvalidAttributeValueError, "Strikethrough must be one of: #{STRIKETHROUGH_VALUES.inspect}!" + end + val + end + + UNDERLINE_VALUES = [nil, "none", "single", "double", "low", "error"] + shoes_style :underline do |val, _name| + unless UNDERLINE_VALUES.include?(val) + raise Shoes::Errors::InvalidAttributeValueError, "Underline must be one of: #{UNDERLINE_VALUES.inspect}!" end - # rubocop:enable Lint/MissingSuper + val end shoes_events # No TextDrawable-specific events yet + + def initialize(*args, **kwargs) + # Don't pass text_children args to Drawable#initialize + super(*[], **kwargs) + + # Text_children alternates strings and TextDrawables, so we can't just pass + # it as a Shoes style. It won't serialize. + update_text_children(args) + + create_display_drawable + end + + def text_children_to_items(text_children) + text_children.map { |arg| arg.is_a?(TextDrawable) ? arg.linkable_id : arg.to_s } + end + + # Sets the paragraph text to a new value, which can + # include {TextDrawable}s like em(), strong(), etc. + # + # @param children [Array] the arguments can be Strings and/or TextDrawables + # @return [void] + def replace(*children) + update_text_children(children) + end + + # Set the paragraph text to a single String. + # To use bold, italics, etc. use {Para#replace} instead. + # + # @param child [String] the new text to use for this Para + # @return [void] + def text=(*children) + update_text_children(children) + end + + # Return the text, but not the styling, of the para's + # contents. For example, if the contents had strong + # and emphasized text, the bold and emphasized would + # be removed but the text would be returned. + # + # @return [String] the text from this para + def text + @text_children.map(&:to_s).join + end + + # Return the text but not styling from the para. This + # is the same as #text. + # + # @return [String] the text from this para + def to_s + self.text + end + + private + + # Text_children alternates strings and TextDrawables, so we can't just pass + # it as a Shoes style. It won't serialize. + def update_text_children(children) + @text_children = children.flatten + # This should signal the display drawable to change + self.text_items = text_children_to_items(@text_children) + end end class << self @@ -33,37 +99,24 @@ def default_text_drawable_with(element) class_name = element.capitalize drawable_class = Class.new(Shoes::TextDrawable) do - shoes_style :content shoes_events # No specific events init_args # We're going to pass an empty array to super - def initialize(content) - super() - - @content = content - - create_display_drawable - end - - def text - self.content - end - - def to_s - self.content - end - - def text=(new_text) - self.content = new_text - end end Shoes.const_set class_name, drawable_class end end end -# Shoes3 subclasses of cText were: code, del, em, ins, span, strong, sup, sub - Shoes.default_text_drawable_with(:code) +Shoes.default_text_drawable_with(:del) Shoes.default_text_drawable_with(:em) Shoes.default_text_drawable_with(:strong) +Shoes.default_text_drawable_with(:span) +Shoes.default_text_drawable_with(:sub) +Shoes.default_text_drawable_with(:sup) +Shoes.default_text_drawable_with(:ins) # in Shoes3, looks like "ins" is just underline + +# Defaults must come *after* classes are defined + +Shoes::Drawable.drawable_default_styles[Shoes::Ins][:underline] = "single" diff --git a/lib/scarpe/wv.rb b/lib/scarpe/wv.rb index 9f9441194..834b3e2b5 100644 --- a/lib/scarpe/wv.rb +++ b/lib/scarpe/wv.rb @@ -95,7 +95,6 @@ class Scarpe::Webview::Drawable < Shoes::Linkable require_relative "wv/shape" require_relative "wv/text_drawable" -require_relative "wv/span" require_relative "wv/link" require_relative "wv/line" require_relative "wv/rect" diff --git a/lib/scarpe/wv/document_root.rb b/lib/scarpe/wv/document_root.rb index 3bbe739ec..d6aab29b3 100644 --- a/lib/scarpe/wv/document_root.rb +++ b/lib/scarpe/wv/document_root.rb @@ -16,12 +16,12 @@ def initialize(properties) when "font" @fonts << args[0] # Can't just create font_updater and alert_updater on initialize - not everything is set up - @font_updater ||= Scarpe::Webview::WebWrangler::ElementWrangler.new("root-fonts") + @font_updater ||= Scarpe::Webview::WebWrangler::ElementWrangler.new(html_id: "root-fonts") @font_updater.inner_html = font_contents when "alert" bind_ok_event @alerts << args[0] - @alert_updater ||= Scarpe::Webview::WebWrangler::ElementWrangler.new("root-alerts") + @alert_updater ||= Scarpe::Webview::WebWrangler::ElementWrangler.new(html_id: "root-alerts") @alert_updater.inner_html = alert_contents else raise Scarpe::UnknownBuiltinCommandError, "Unexpected builtin command: #{cmd_name.inspect}!" diff --git a/lib/scarpe/wv/drawable.rb b/lib/scarpe/wv/drawable.rb index 1425f0119..dc3a968cd 100644 --- a/lib/scarpe/wv/drawable.rb +++ b/lib/scarpe/wv/drawable.rb @@ -153,7 +153,7 @@ def add_child(child) # # @return [Scarpe::WebWrangler::ElementWrangler] a DOM object manager def html_element - @elt_wrangler ||= Scarpe::Webview::WebWrangler::ElementWrangler.new(html_id) + @elt_wrangler ||= Scarpe::Webview::WebWrangler::ElementWrangler.new(html_id:) end # Return a promise that guarantees all currently-requested changes have completed diff --git a/lib/scarpe/wv/link.rb b/lib/scarpe/wv/link.rb index 4fa41c5fa..96ffe89e2 100644 --- a/lib/scarpe/wv/link.rb +++ b/lib/scarpe/wv/link.rb @@ -10,8 +10,10 @@ def initialize(properties) end end - def element - render "link" + def to_calzini_hash + h = super + h[:tag] = "a" + h end end end diff --git a/lib/scarpe/wv/para.rb b/lib/scarpe/wv/para.rb index 1ecf499e7..f10317094 100644 --- a/lib/scarpe/wv/para.rb +++ b/lib/scarpe/wv/para.rb @@ -61,6 +61,7 @@ def to_html private def child_markup + # The children should be only text strings or TextDrawables. items_to_display_children(@text_items).map do |child| if child.respond_to?(:to_html) child.to_html diff --git a/lib/scarpe/wv/span.rb b/lib/scarpe/wv/span.rb deleted file mode 100644 index 0a8011672..000000000 --- a/lib/scarpe/wv/span.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -module Scarpe::Webview - class Span < TextDrawable - SIZES = { - inscription: 10, - ins: 10, - span: 12, - caption: 14, - tagline: 18, - subtitle: 26, - title: 34, - banner: 48, - }.freeze - private_constant :SIZES - - def initialize(properties) - super - end - - def properties_changed(changes) - text = changes.delete("text") - if text - html_element.inner_html = text - return - end - - # Not deleting, so this will re-render - if changes["size"] && SIZES[@size.to_sym] - @size = @size.to_sym - end - - super - end - - def element(&block) - render("span", &block) - end - - def to_html - element { @text } - end - end -end diff --git a/lib/scarpe/wv/text_drawable.rb b/lib/scarpe/wv/text_drawable.rb index 946a57f48..60a1843d2 100644 --- a/lib/scarpe/wv/text_drawable.rb +++ b/lib/scarpe/wv/text_drawable.rb @@ -1,32 +1,90 @@ # frozen_string_literal: true +# There are different ways to implement these tags. You can change the HTML tag (link, +# code, strong) or set default values (del for strikethrough.) There's no reason the +# Shoes tag name has to match the HTML tag (del, link). This can be a little +# complicated since CSS often sets default values (e.g. del for strikethrough) and +# Scarpe may use those default values or override them. Long term it may be easier +# for us to set up our own CSS for this somehow that does *not* use the HTML-tag +# defaults since the browser can mess with those, and there's no guarantee that +# Webview uses the same default CSS style across all OSes. + module Scarpe::Webview + # This class renders text tags like em, strong, link, etc. class TextDrawable < Drawable - def to_html - # Do not render TextDrawables with individual wrapper divs. - element + # Calzini renders based on properties, mostly Shoes styles. + # To have Calzini render this for us, we convert to the format + # Calzini expects and then let it render. See Webview::Para + # for the specific Calzini call. + def to_calzini_hash + text_array = items_to_display_children(@text_items).map do |item| + if item.respond_to?(:to_calzini_hash) + item.to_calzini_hash + elsif item.is_a?(String) + item + else + # This should normally be filtered out in Lacci, long before we see it + raise "Unrecognized item in TextDrawable! #{item.inspect}" + end + end + + { + items: text_array, + html_id: @linkable_id.to_s, + tag: nil, # have Calzini assign a default unless a subclass overrides this + props: shoes_styles, + } + end + + def element + render("text_drawable", [to_calzini_hash]) + end + + def items_to_display_children(items) + return [] if items.nil? + + items.map do |item| + if item.is_a?(String) + item + else + Scarpe::Webview::DisplayService.instance.query_display_drawable_for(item) + end + end + end + + # Usually we query by ID, but for TextDrawable it has to be by class. + # That's how needs_update!, etc continue to work. + def html_element + @elt_wrangler ||= Scarpe::Webview::WebWrangler::ElementWrangler.new(selector: %{document.getElementsByClassName("id_#{html_id}")}, multi: true) end end class << self - def default_wv_text_drawable_with(element) - webview_class_name = element.capitalize + def default_wv_text_drawable_with_tag(shoes_tag, html_tag = nil) + html_tag ||= shoes_tag + webview_class_name = shoes_tag.capitalize webview_drawable_class = Class.new(Scarpe::Webview::TextDrawable) do - def initialize(properties) - class_name = self.class.name.split("::")[-1] - @html_tag = class_name.delete_prefix("Webview").downcase - super + class << self + attr_accessor :html_tag end - def element - render(@html_tag) { @content.to_s } + def to_calzini_hash + h = super + h[:tag] = self.class.html_tag + h end end Scarpe::Webview.const_set webview_class_name, webview_drawable_class + webview_drawable_class.html_tag = html_tag end end end -Scarpe::Webview.default_wv_text_drawable_with(:code) -Scarpe::Webview.default_wv_text_drawable_with(:em) -Scarpe::Webview.default_wv_text_drawable_with(:strong) +Scarpe::Webview.default_wv_text_drawable_with_tag(:code) +Scarpe::Webview.default_wv_text_drawable_with_tag(:del) +Scarpe::Webview.default_wv_text_drawable_with_tag(:em) +Scarpe::Webview.default_wv_text_drawable_with_tag(:strong) +Scarpe::Webview.default_wv_text_drawable_with_tag(:span) +Scarpe::Webview.default_wv_text_drawable_with_tag(:sub) +Scarpe::Webview.default_wv_text_drawable_with_tag(:sup) +Scarpe::Webview.default_wv_text_drawable_with_tag(:ins, "span") # Styled in Shoes, not CSS diff --git a/lib/scarpe/wv/web_wrangler.rb b/lib/scarpe/wv/web_wrangler.rb index 33e704630..1ad576048 100644 --- a/lib/scarpe/wv/web_wrangler.rb +++ b/lib/scarpe/wv/web_wrangler.rb @@ -747,16 +747,37 @@ def schedule_waiting_changes class ElementWrangler attr_reader :html_id - # Create an ElementWrangler for the given HTML ID + # Create an ElementWrangler for the given HTML ID or selector. + # The caller should provide exactly one of the html_id or selector. # # @param html_id [String] the HTML ID for the DOM element - def initialize(html_id) + def initialize(html_id: nil, selector: nil, multi: false) @webwrangler = ::Scarpe::Webview::DisplayService.instance.wrangler raise Scarpe::MissingWranglerError, "Can't get WebWrangler!" unless @webwrangler - @html_id = html_id + if html_id && !selector + @selector = "document.getElementById('" + html_id + "')" + elsif selector && !html_id + @selector = selector + else + raise ArgumentError, "Must provide exactly one of html_id or selector!" + end + + @multi = multi end + private + + def on_each(fragment) + if @multi + @webwrangler.dom_change("a = Array.from(#{@selector}); a.forEach((item) => item#{fragment}); true") + else + @webwrangler.dom_change(@selector + fragment + ";true") + end + end + + public + # Return a promise that will be fulfilled when all changes scheduled via # this ElementWrangler are verified complete. # @@ -770,7 +791,7 @@ def promise_update # @param new_value [String] the new value # @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete def value=(new_value) - @webwrangler.dom_change("document.getElementById('" + html_id + "').value = `" + new_value + "`; true") + on_each(".value = `" + new_value + "`") end # Update the JS DOM element's inner_text. The given Ruby value will be converted to string and assigned in single-quotes. @@ -778,7 +799,7 @@ def value=(new_value) # @param new_text [String] the new inner_text # @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete def inner_text=(new_text) - @webwrangler.dom_change("document.getElementById('" + html_id + "').innerText = '" + new_text + "'; true") + on_each(".innerText = '" + new_text + "'") end # Update the JS DOM element's inner_html. The given Ruby value will be converted to string and assigned in backquotes. @@ -786,7 +807,7 @@ def inner_text=(new_text) # @param new_html [String] the new inner_html # @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete def inner_html=(new_html) - @webwrangler.dom_change("document.getElementById(\"" + html_id + "\").innerHTML = `" + new_html + "`; true") + on_each(".innerHTML = `" + new_html + "`") end # Update the JS DOM element's outer_html. The given Ruby value will be converted to string and assigned in backquotes. @@ -794,7 +815,7 @@ def inner_html=(new_html) # @param new_html [String] the new outer_html # @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete def outer_html=(new_html) - @webwrangler.dom_change("document.getElementById(\"" + html_id + "\").outerHTML = `" + new_html + "`; true") + on_each(".outerHTML = `" + new_html + "`") end # Update the JS DOM element's attribute. The given Ruby value will be inspected and assigned. @@ -803,7 +824,7 @@ def outer_html=(new_html) # @param value [String] the new attribute value # @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete def set_attribute(attribute, value) - @webwrangler.dom_change("document.getElementById(\"" + html_id + "\").setAttribute(" + attribute.inspect + "," + value.inspect + "); true") + on_each(".setAttribute(" + attribute.inspect + "," + value.inspect + ")") end # Update an attribute of the JS DOM element's style. The given Ruby value will be inspected and assigned. @@ -812,19 +833,19 @@ def set_attribute(attribute, value) # @param value [String] the new style attribute value # @return [Scarpe::Promise] a promise that will be fulfilled when the change is complete def set_style(style_attr, value) - @webwrangler.dom_change("document.getElementById(\"" + html_id + "\").style.#{style_attr} = " + value.inspect + "; true") + on_each(".style.#{style_attr} = " + value.inspect + ";") end # Remove the specified DOM element # # @return [Scarpe::Promise] a promise that wil be fulfilled when the element is removed def remove - @webwrangler.dom_change("document.getElementById('" + html_id + "').remove(); true") + on_each(".remove()") end def toggle_input_button(mark) checked_value = mark ? "true" : "false" - @webwrangler.dom_change("document.getElementById('#{html_id}').checked = #{checked_value};") + on_each(".checked = #{checked_value}") end end end diff --git a/lib/scarpe/wv/webview_local_display.rb b/lib/scarpe/wv/webview_local_display.rb index 285521fef..7a7ec8f00 100644 --- a/lib/scarpe/wv/webview_local_display.rb +++ b/lib/scarpe/wv/webview_local_display.rb @@ -55,7 +55,7 @@ def initialize def create_display_drawable_for(drawable_class_name, drawable_id, properties, parent_id:, is_widget:) existing = query_display_drawable_for(drawable_id, nil_ok: true) if existing - @log.warn("There is already a display drawable for #{drawable_id.inspect}! Returning #{existing.class.name}.") + @log.warn("There is already a Scarpe drawable for #{drawable_id.inspect}! Returning #{existing.class.name} rather than creating a #{drawable_class_name}.") return existing end diff --git a/scarpe-components/lib/scarpe/components/calzini/art_widgets.rb b/scarpe-components/lib/scarpe/components/calzini/art_drawables.rb similarity index 100% rename from scarpe-components/lib/scarpe/components/calzini/art_widgets.rb rename to scarpe-components/lib/scarpe/components/calzini/art_drawables.rb diff --git a/scarpe-components/lib/scarpe/components/calzini/para.rb b/scarpe-components/lib/scarpe/components/calzini/para.rb index 586bc6cc7..321763131 100644 --- a/scarpe-components/lib/scarpe/components/calzini/para.rb +++ b/scarpe-components/lib/scarpe/components/calzini/para.rb @@ -1,17 +1,24 @@ # frozen_string_literal: true module Scarpe::Components::Calzini - # para_element is a bit of a hard one, since it does not-entirely-trivial - # mapping between display objects and IDs. But we don't want Calzini - # messing with the display service or display objects. def para_element(props, &block) + # Align requires an extra wrapping div. + + # Stacking strikethrough with underline requires multiple elements. + # We handle this by making strikethrough part of the main element, + # but using an extra wrapping element for underline. + + tag = props["tag"] || "p" + + para_styles, extra_styles = para_style(props) + HTML.render do |h| - if props["align"] - h.div(id: html_id, style: {"text-align": props["align"], width: "100%"}) do - h.p(style: para_style(props), &block) - end + if extra_styles.empty? + h.send(tag, id: html_id, style: para_styles, &block) else - h.p(id: html_id, style: para_style(props), &block) + h.div(id: html_id, style: extra_styles.merge(width: "100%")) do + h.send(tag, style: para_styles, &block) + end end end end @@ -19,11 +26,57 @@ def para_element(props, &block) private def para_style(props) - drawable_style(props).merge({ - color: rgb_to_hex(props["stroke"]), + ds = drawable_style(props) + s1, s2 = text_specific_styles(props) + [ds.merge(s1), s2] + end + + def text_specific_styles(props) + # Shoes3 allows align: right on TextDrawables like em(), but it does + # nothing. We can ignore it or (maybe in future?) warn if we see it. + + strikethrough = props["strikethrough"] + strikethrough = nil if strikethrough == "" || strikethrough == "none" + s1 = { + "color": rgb_to_hex(props["stroke"]), + "background-color": rgb_to_hex(props["fill"]), "font-size": para_font_size(props), "font-family": props["font"], - }.compact) + "text-decoration-line": strikethrough ? "line-through" : nil, + "text-decoration-color": props["strikecolor"] ? rgb_to_hex(props["strikecolor"]) : nil, + }.compact + + s2 = {} + if props["align"] && props["align"] != "" + s2[:"text-align"] = props["align"] + end + + unless [nil, "none"].include?(props["underline"]) + undercolor = rgb_to_hex props["undercolor"] + s2["text-decoration-color"] = undercolor if undercolor + end + + # [nil, "none", "single", "double", "low", "error"] + case props["underline"] + when nil, "none" + # Do nothing + when "single" + s2["text-decoration-line"] = "underline" + when "double" + s2["text-decoration-line"] = "underline" + s2["text-decoration-style"] = "double" + when "error" + s2["text-decoration-line"] = "underline" + s2["text-decoration-style"] = "wavy" + when "low" + s2["text-decoration-line"] = "underline" + s2["text-underline-offset"] = "0.3rem" + else + # This should normally be unreachable + raise Shoes::Errors::InvalidAttributeValueError, "Unexpected underline type #{props["underline"].inspect}!" + end + + [s1, s2] end def para_font_size(props) @@ -34,4 +87,67 @@ def para_font_size(props) dimensions_length(font_size) end + + public + + # The text element is used to render the equivalent of Shoes cText, + # which includes em, strong, span, link and so on. We use a + # "content" tag for it which alternates plaintext with a hash of + # properties. + def text_drawable_element(prop_array) + out = String.new # Need unfrozen string + + # Each item should be a String or a property Hash + # :items, :html_id, :tag, :props + prop_array.each do |item| + if item.is_a?(String) + out << item.gsub("\n", "<br/>") + else + s, extra = text_drawable_style(item[:props]) + out << HTML.render do |h| + if extra.empty? + h.send( + item[:tag] || "span", + class: "id_#{item[:html_id]}", + style: s, + **text_drawable_attrs(item[:props]) + ) do + text_drawable_element(item[:items]) + end + else + h.span(class: "id_#{item[:html_id]}", style: extra) do + h.send( + item[:tag] || "span", + class: "id_#{item[:html_id]}", + style: s, + **text_drawable_attrs(item[:props]) + ) do + text_drawable_element(item[:items]) + end + end + end + end + end + end + + out + end + + private + + def text_drawable_attrs(props) + { + # These properties will normally only be set by link() + href: props["click"], + onclick: props["has_block"] ? handler_js_code("click") : nil, + }.compact + end + + def text_drawable_style(props) + s, extra_s = text_specific_styles(props) + + # Add hover styles, perhaps with CSS pseudo-class + + [drawable_style(props).merge(s), extra_s] + end end diff --git a/scarpe-components/lib/scarpe/components/calzini/text_widgets.rb b/scarpe-components/lib/scarpe/components/calzini/text_widgets.rb deleted file mode 100644 index 4dc55de00..000000000 --- a/scarpe-components/lib/scarpe/components/calzini/text_widgets.rb +++ /dev/null @@ -1,65 +0,0 @@ -# frozen_string_literal: true - -module Scarpe::Components::Calzini - def link_element(props) - HTML.render do |h| - h.a(**link_attributes(props)) do - props["text"] - end - end - end - - def span_element(props, &block) - HTML.render do |h| - h.span(**span_options(props), &block) - end - end - - def code_element(props, &block) - HTML.render do |h| - h.code(&block) - end - end - - def em_element(props, &block) - HTML.render do |h| - h.em(&block) - end - end - - def strong_element(props, &block) - HTML.render do |h| - h.strong(&block) - end - end - - private - - def link_attributes(props) - { - id: html_id, - href: props["click"], - onclick: (handler_js_code("click") if props["has_block"]), - style: drawable_style(props), - }.compact - end - - def span_style(props) - { - color: props["stroke"], - "font-size": span_font_size(props), - "font-family": props["font"], - }.compact - end - - def span_options(props) - { id: html_id, style: span_style(props) } - end - - def span_font_size(props) - sz = props["size"] - font_size = SIZES.key?(sz.to_s.to_sym) ? SIZES[sz.to_s.to_sym] : sz - - dimensions_length(font_size) - end -end diff --git a/scarpe-components/lib/scarpe/components/html.rb b/scarpe-components/lib/scarpe/components/html.rb index e05643ee2..221713aa0 100644 --- a/scarpe-components/lib/scarpe/components/html.rb +++ b/scarpe-components/lib/scarpe/components/html.rb @@ -20,6 +20,9 @@ class Scarpe::Components::HTML :u, :line, :span, + :sub, + :sup, + :del, :svg, :h1, :h2, diff --git a/scarpe-components/lib/scarpe/components/tiranti.rb b/scarpe-components/lib/scarpe/components/tiranti.rb index d619bea1f..042aab9fc 100644 --- a/scarpe-components/lib/scarpe/components/tiranti.rb +++ b/scarpe-components/lib/scarpe/components/tiranti.rb @@ -145,58 +145,23 @@ def progress_element(props) end end - # para_element is a bit of a hard one, since it does not-entirely-trivial - # mapping between display objects and IDs. But we don't want Calzini - # messing with the display service or display objects. def para_element(props, &block) - tag, opts = para_elt_and_opts(props) - - HTML.render do |h| - h.send(tag, **opts, &block) - end - end - - private - - ELT_AND_SIZE = { - inscription: [:p, 10], - ins: [:p, 10], - para: [:p, 12], - caption: [:p, 14], - tagline: [:p, 18], - subtitle: [:h3, 26], - title: [:h2, 34], - banner: [:h1, 48], - }.freeze - - def para_elt_and_opts(props) - elt, size = para_elt_and_size(props) - size = dimensions_length(size) - - para_style = drawable_style(props).merge({ - color: rgb_to_hex(props["stroke"]), - "font-size": size, - "font-family": props["font"], - }.compact) - - opts = (props["html_attributes"] || {}).merge(id: html_id, style: para_style) - - [elt, opts] - end - - def para_elt_and_size(props) - return [:p, nil] unless props["size"] - - ps = props["size"].to_s.to_sym - if ELT_AND_SIZE.key?(ps) - ELT_AND_SIZE[ps] + ps, _extra = para_style(props) + size = ps[:"font-size"] || "12px" + size_int = size.to_i # Mostly useful if it's something like "12px" + if size.include?("calc") || size.end_with?("%") + # Very big text! + props["tag"] = "h2" + elsif size_int >= 48 + props["tag"] = "h1" + elsif size_int >= 34 + props["tag"] = "h2" + elsif size_int >= 26 + props["tag"] = "h3" else - sz = props["size"].to_i - if sz > 18 - [:h2, sz] - else - [:p, sz] - end + props["tag"] = "p" end + + super end end diff --git a/scarpe-components/test/calzini/test_calzini_para.rb b/scarpe-components/test/calzini/test_calzini_para.rb index ccf7299de..e9c16cae2 100644 --- a/scarpe-components/test/calzini/test_calzini_para.rb +++ b/scarpe-components/test/calzini/test_calzini_para.rb @@ -7,7 +7,6 @@ def setup @calzini = CalziniRenderer.new end - # Note that Calzini doesn't render the text items for itself. def test_para_simple assert_equal %{<p id="elt-1">OK</p>}, @calzini.render("para", {}) { "OK" } @@ -32,11 +31,4 @@ def test_para_with_symbol_banner assert_equal %{<p id="elt-1" style="font-size:48px"></p>}, @calzini.render("para", { "size" => :banner }) end - - # Eventually this should probably need to be marked as a Scarpe extension, here or - # elsewhere. - #def test_para_with_html_attributes - # assert_equal %{<p avocado="true" class="avocado_bearing" id="elt-1"></p>}, - # @calzini.render("para", { "html_attributes" => { "avocado" => true, "class" => "avocado_bearing" } }) - #end end diff --git a/scarpe-components/test/calzini/test_calzini_text_drawables.rb b/scarpe-components/test/calzini/test_calzini_text_drawables.rb index 8df7bb019..12f3582bf 100644 --- a/scarpe-components/test/calzini/test_calzini_text_drawables.rb +++ b/scarpe-components/test/calzini/test_calzini_text_drawables.rb @@ -7,30 +7,100 @@ def setup @calzini = CalziniRenderer.new end - def test_link_simple - assert_equal %{<a id="elt-1" href="https://google.com">click here</a>}, - @calzini.render("link", { "click" => "https://google.com", "text" => "click here" }) + def trim_html_ids(s) + s.gsub(/ class="id_\d+"/, "") end - def test_link_block - assert_equal %{<a id="elt-1" onclick="handle('click')">click here</a>}, - @calzini.render("link", { "has_block" => true, "text" => "click here" }) + def test_text_only_drawable + assert_equal %{this is text}, + @calzini.render("text_drawable", ["this ", "is", " text"]) end - def test_span_simple - assert_equal %{<span id="elt-1" style="color:red;font-size:48px;font-family:Lucida">big red</span>}, - @calzini.render("span", { "stroke" => "red", "size" => :banner, "font" => "Lucida" }) { "big red" } + def test_simple_text_drawable_with_em + assert_equal %{this <em class="id_1">is</em> text}, + @calzini.render("text_drawable", + ["this ", { tag: "em", html_id: "1", items: ["is"], props: {}}, " text"]) end - def test_code_simple - assert_equal %{<code>Hello</code>}, @calzini.render("code", {}) { "Hello" } + # Span doesn't have default properties, so it's good for testing how a property is rendered + def test_simple_text_drawable_with_span_styles + assert_equal %{this <span style="color:#FF00FF;background-color:#0000FF;font-size:13px;font-family:Lucida">is</span> text}, + trim_html_ids(@calzini.render("text_drawable", + ["this ", { + tag: "span", + html_id: "1", + items: ["is"], + props: { + "font" => "Lucida", + "size" => 13, + "stroke" => "#FF00FF", + "fill" => "#0000FF" + } + }, " text"])) end - def test_em_simple - assert_equal %{<em>Hello</em>}, @calzini.render("em", {}) { "Hello" } + def test_link_with_has_block + assert_equal %{this <a onclick="handle('click')">is</a> text}, + trim_html_ids(@calzini.render("text_drawable", + ["this ", { + tag: "a", + html_id: "1", + items: ["is"], + props: { + "has_block" => true, + } + }, " text"])) end - def test_strong_simple - assert_equal %{<strong>Hello</strong>}, @calzini.render("strong", {}) { "Hello" } + def test_link_with_click + assert_equal %{this <a href="#" onclick="handle('click')">is</a> text}, + trim_html_ids(@calzini.render("text_drawable", + ["this ", { + tag: "a", + html_id: "1", + items: ["is"], + props: { + "has_block" => true, + "click" => "#", + } + }, " text"])) + end + + def test_del_tag + assert_equal %{this <del>is</del> text}, + trim_html_ids(@calzini.render("text_drawable", + ["this ", { + tag: "del", + html_id: "1", + items: ["is"], + props: { + } + }, " text"])) + end + + def test_single_strikethrough + assert_equal %{this <span style="text-decoration-line:line-through">is</span> text}, + trim_html_ids(@calzini.render("text_drawable", + ["this ", { + tag: "span", + html_id: "1", + items: ["is"], + props: { + "strikethrough" => "single", + } + }, " text"])) + end + + def test_single_strikethrough_none + assert_equal %{this <span>is</span> text}, + trim_html_ids(@calzini.render("text_drawable", + ["this ", { + tag: "span", + html_id: "1", + items: ["is"], + props: { + "strikethrough" => "none", + } + }, " text"])) end end diff --git a/test/shoes_spec_helper.rb b/test/shoes_spec_helper.rb new file mode 100644 index 000000000..982183645 --- /dev/null +++ b/test/shoes_spec_helper.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# This is intended to be required from inside a Shoes-Spec test, which allows writing test +# helper code for them. + +module TextDrawableHelper + def trim_html_ids(s) + s.gsub(/ class="id_\d+"/, "") + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb index d8b7f9ab7..ab00c90c9 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -47,7 +47,8 @@ def run_scarpe_sspec( expect_assertions_min: nil, expect_assertions_max: nil, expect_result: :success, - display_service: "wv_local" + display_service: "wv_local", + html_renderer: "calzini" ) test_output = File.expand_path(File.join __dir__, "sspec.json") test_method_name = self.name @@ -58,6 +59,7 @@ def run_scarpe_sspec( # For unit testing always supply --debug so we get the most logging cmd = \ "SCARPE_DISPLAY_SERVICE=#{display_service} " + + "SCARPE_HTML_RENDERER=#{html_renderer} " + "SCARPE_LOG_CONFIG=\"#{scarpe_log_config}\" " + "SHOES_MINITEST_EXPORT_FILE=\"#{test_output}\" " + "SHOES_MINITEST_CLASS_NAME=\"#{test_class_name}\" " + diff --git a/test/test_link.rb b/test/test_link.rb deleted file mode 100644 index 1079a7620..000000000 --- a/test/test_link.rb +++ /dev/null @@ -1,46 +0,0 @@ -# frozen_string_literal: true - -require "test_helper" - -class TestWebviewLink < ScarpeTest - def setup - @default_properties = { - "text" => "click here", - "click" => "#", - "has_block" => false, - "shoes_linkable_id" => 1, - } - end - - def with_mocked_binding(&block) - @mocked_disp_service = Minitest::Mock.new - @mocked_app = Minitest::Mock.new - @mocked_disp_service.expect(:app, @mocked_app) - @mocked_app.expect(:bind, nil, [String]) - - Scarpe::Webview::DisplayService.stub(:instance, @mocked_disp_service, &block) - - @mocked_disp_service.verify - @mocked_app.verify - end - - def test_link_with_url - with_mocked_binding do - link = Scarpe::Webview::Link.new(@default_properties.merge("click" => "https://www.google.com")) - - assert_html link.to_html, :a, id: link.html_id, href: "https://www.google.com" do - "click here" - end - end - end - - def test_link_with_block - with_mocked_binding do - link = Scarpe::Webview::Link.new(@default_properties.merge("has_block" => true)) - - assert_html link.to_html, :a, id: link.html_id, href: "#", onclick: link.handler_js_code("click") do - "click here" - end - end - end -end diff --git a/test/test_para.rb b/test/test_para.rb index ac67c8775..21f8a192b 100644 --- a/test/test_para.rb +++ b/test/test_para.rb @@ -172,7 +172,7 @@ def test_para_stacking_text_drawables @p = para "Yo ", [em("EmphaYo "), strong("StrongYo")], em("empha"), ", plain" end SCARPE_APP - assert_includes para().display.to_html, "Yo <em>EmphaYo </em><strong>StrongYo</strong><em>empha</em>, plain" + assert_includes para().display.to_html, "Yo <em class=\"id_3\">EmphaYo </em><strong class=\"id_4\">StrongYo</strong><em class=\"id_5\">empha</em>, plain" TEST_CODE end end diff --git a/test/test_sspec_infrastructure.rb b/test/test_sspec_infrastructure.rb index 5dac276fd..83d8963cd 100644 --- a/test/test_sspec_infrastructure.rb +++ b/test/test_sspec_infrastructure.rb @@ -61,5 +61,4 @@ def test_assertion_fail assert_equal true, false, "This should always fail!" SSPEC end - end diff --git a/test/test_text_drawables.rb b/test/test_text_drawables.rb new file mode 100644 index 000000000..c1bee23ee --- /dev/null +++ b/test/test_text_drawables.rb @@ -0,0 +1,288 @@ +# frozen_string_literal: true + +require "test_helper" + +class TestTextDrawables < ShoesSpecLoggedTest + self.logger_dir = File.expand_path("#{__dir__}/../logger") + + def require_helper + "require " + File.expand_path("#{__dir__}/shoes_spec_helper.rb").inspect + end + + # To test: + # + # * LinkHover + + def test_basic_text_drawables + run_scarpe_sspec_code(<<~SSPEC) + --- + ----------- app code + Shoes.app do + para "This is ", em("emphatically"), " ", strong("strongly"), " made of text!" + end + ----------- test code + #{require_helper} + self.class.include TextDrawableHelper + + assert_equal "This is emphatically strongly made of text!", para.text + assert_includes trim_html_ids(para.display.to_html), "This is <em>emphatically</em> <strong>strongly</strong> made of text!" + SSPEC + end + + def test_para_stroke_and_fill + run_scarpe_sspec_code(<<~SSPEC) + --- + ----------- app code + Shoes.app do + para "This is text", stroke: green, fill: blue + end + ----------- test code + #{require_helper} + self.class.include TextDrawableHelper + + h = trim_html_ids(para.display.to_html) + assert_includes h, "This is text" + assert_includes h, 'color:#008000' + assert_includes h, 'background-color:#0000FF' + SSPEC + end + + def test_styled_text_drawables + run_scarpe_sspec_code(<<~SSPEC) + --- + ----------- app code + Shoes.app do + para "This is ", em("emphatically ", hidden: true), "somewhat hidden!" + end + ----------- test code + #{require_helper} + self.class.include TextDrawableHelper + + assert_equal "This is emphatically somewhat hidden!", para.text + assert_includes trim_html_ids(para.display.to_html), \%{This is <em style="display:none">emphatically </em>somewhat hidden!} + SSPEC + end + + def test_basic_nested_text_drawables_1 + run_scarpe_sspec_code(<<~SSPEC) + --- + ----------- app code + Shoes.app do + para "This is ", em(strong("emphatically strongly")), " made of text!" + end + ----------- test code + #{require_helper} + self.class.include TextDrawableHelper + + assert_equal "This is emphatically strongly made of text!", para.text + assert_includes trim_html_ids(para.display.to_html), "This is <em><strong>emphatically strongly</strong></em> made of text!" + SSPEC + end + + def test_basic_nested_text_drawables_2 + run_scarpe_sspec_code(<<~SSPEC) + --- + ----------- app code + Shoes.app do + para "This is ", em("emphatically and ", strong("empha-strongly")), " made of text!" + end + ----------- test code + #{require_helper} + self.class.include TextDrawableHelper + + assert_equal "This is emphatically and empha-strongly made of text!", para.text + assert_includes trim_html_ids(para.display.to_html), "This is <em>emphatically and <strong>empha-strongly</strong></em> made of text!" + SSPEC + end + + def test_text_drawable_in_multiple_paras + run_scarpe_sspec_code(<<~SSPEC) + --- + ----------- app code + Shoes.app do + t = em("emphatically") + @p1 = para "This is ", t, " ", strong("strongly"), " made of text!" + @p2 = para "And ", t, " a great idea!" + end + ----------- test code + #{require_helper} + self.class.include TextDrawableHelper + + assert_includes trim_html_ids(para("@p1").display.to_html), "This is <em>emphatically</em> <strong>strongly</strong> made of text!" + assert_includes trim_html_ids(para("@p2").display.to_html), "And <em>emphatically</em> a great idea!" + SSPEC + end + + def test_text_drawable_multiple_para_update + run_scarpe_sspec_code(<<~SSPEC) + --- + ----------- app code + Shoes.app do + @t = em("emphatically") + @p1 = para "This is ", @t, " ", strong("strongly"), " made of text!" + @p2 = para "And ", @t, " a great idea!" + + button "Change" do + @t.text = "totally ", strong("unquestionably") + end + end + ----------- test code + #{require_helper} + self.class.include TextDrawableHelper + + button.trigger_click + + # Check to_html updating + assert_includes trim_html_ids(para("@p1").display.to_html), "This is <em>totally <strong>unquestionably</strong></em> <strong>strongly</strong> made of text!" + assert_includes trim_html_ids(para("@p2").display.to_html), "And <em>totally <strong>unquestionably</strong></em> a great idea!" + + # Check .text updating + assert_equal "This is totally unquestionably strongly made of text!", para("@p1").text + assert_equal "And totally unquestionably a great idea!", para("@p2").text + + # Check dom_html updating + h = trim_html_ids(dom_html) # Query once + assert !h.include?("emphatically"), "the word 'emphatically' should have been removed by the update" + assert_includes h, "This is <em>totally <strong>unquestionably</strong></em> <strong>strongly</strong> made of text!" + assert_includes h, "And <em>totally <strong>unquestionably</strong></em> a great idea!" + SSPEC + end + + def test_span + run_scarpe_sspec_code(<<~SSPEC) + --- + ----------- app code + Shoes.app do + para "This is ", em("emphatically"), " made of ", span("text!") + end + ----------- test code + #{require_helper} + self.class.include TextDrawableHelper + + assert_equal "This is emphatically made of text!", para.text + assert_includes trim_html_ids(para.display.to_html), "This is <em>emphatically</em> made of <span>text!</span>" + SSPEC + end + + def test_basic_link + run_scarpe_sspec_code(<<~SSPEC) + --- + ----------- app code + Shoes.app do + para "This is ", link(em("emphatically"), " made of ", span("text!"), click: "http://foo.com") + end + ----------- test code + #{require_helper} + self.class.include TextDrawableHelper + + assert_equal "This is emphatically made of text!", para.text + assert_includes trim_html_ids(para.display.to_html), + \%{This is <a href="http://foo.com"><em>emphatically</em> made of <span>text!</span></a>} + SSPEC + end + + def test_default_para_size + run_scarpe_sspec_code(<<~SSPEC) + --- + ----------- app code + Shoes.app do + style Shoes::Para, size: 18 + para "This is made of text!" + end + ----------- test code + assert_includes para.display.to_html, "font-size:18px" + SSPEC + end + + def test_default_para_size_override + run_scarpe_sspec_code(<<~SSPEC) + --- + ----------- app code + Shoes.app do + style Shoes::Para, size: 14 + para "This is made of text!", size: 19 + end + ----------- test code + assert_includes para.display.to_html, "font-size:19px" + SSPEC + end + + def test_default_em_size + run_scarpe_sspec_code(<<~SSPEC) + --- + ----------- app code + Shoes.app do + style Shoes::Em, size: 21 + para "This is made of ", em("text!") + end + ----------- test code + assert_includes para.display.to_html, "font-size:21px" + SSPEC + end + + def test_default_em_size_override + run_scarpe_sspec_code(<<~SSPEC) + --- + ----------- app code + Shoes.app do + style Shoes::Em, size: 14 + para "This is made of ", em("text!", size: 21) + end + ----------- test code + assert_includes para.display.to_html, "font-size:21px" + SSPEC + end + + def test_size_on_para_and_text_drawable + run_scarpe_sspec_code(<<~SSPEC) + --- + ----------- app code + Shoes.app do + para "This is ", em("emphatically", size: 16), " made of text!", size: 14 + end + ----------- test code + #{require_helper} + self.class.include TextDrawableHelper + + assert_includes trim_html_ids(para.display.to_html), + \%{This is <em style="font-size:16px">emphatically</em> made of text!} + assert_includes trim_html_ids(para.display.to_html), "font-size:14px" + SSPEC + end + + def test_bug_with_confusing_ins_and_inscription + run_scarpe_sspec_code(<<~SSPEC) + --- + ----------- app code + Shoes.app do + para "Various ", del("text"), " in ", sub("various"), " ", sup("styles"), + " can be ", ins("hard to read"), "...\n" + end + ----------- test code + #{require_helper} + self.class.include TextDrawableHelper + + h = trim_html_ids(dom_html) + + assert_includes trim_html_ids(para.display.to_html), + \%{Various <del>text</del> in <sub>various</sub> <sup>styles</sup> can be <span style=\"text-decoration-line:underline\"><span>hard to read</span></span>...} + SSPEC + end + + def test_tiranti_big_text_para_tags + run_scarpe_sspec_code(<<~SSPEC, html_renderer: "tiranti") + --- + ----------- app code + Shoes.app do + para "This is made of text!", size: 26 + end + ----------- test code + #{require_helper} + self.class.include TextDrawableHelper + + assert_equal \%{<h3 id=\"3\" style=\"font-size:26px\">This is made of text!</h3>}, + para.display.to_html + SSPEC + end + +end diff --git a/test/wv/html_fixtures/link.html b/test/wv/html_fixtures/link.html index e8c3e5f8a..1970aa125 100644 --- a/test/wv/html_fixtures/link.html +++ b/test/wv/html_fixtures/link.html @@ -1,6 +1,6 @@ <div id="2" style="display:flex;flex-direction:row;flex-wrap:wrap;align-content:flex-start;justify-content:flex-start;align-items:flex-start;width:100%;height:100%"> <div style="height:100%;width:100%;position:relative"> - <p id="4" style="font-size:12px">'Scarpe' means shoes in Italian. 'Scarpe' also means Shoes in...<a id="3" onclick="scarpeHandler('3-click')">(show more)</a></p> + <p id="4" style="font-size:12px">'Scarpe' means shoes in Italian. 'Scarpe' also means Shoes in...<a class="id_3" onclick="scarpeHandler('3-click')">(show more)</a></p> <div id="root-fonts"></div> <div id="root-alerts"> </div> </div> diff --git a/test/wv/html_fixtures/para_text_widgets.html b/test/wv/html_fixtures/para_text_widgets.html index ef5d33853..49fb4e43a 100644 --- a/test/wv/html_fixtures/para_text_widgets.html +++ b/test/wv/html_fixtures/para_text_widgets.html @@ -1,7 +1,7 @@ <div id="2" style="display:flex;flex-direction:row;flex-wrap:wrap;align-content:flex-start;justify-content:flex-start;align-items:flex-start;width:100%;height:100%"> <div style="height:100%;width:100%;position:relative"> <p id="3" style="font-size:12px">This is simple.</p> - <p id="7" style="font-size:12px">This has <em>emphasis</em> and great <strong>strength</strong> and <code>coolness</code>.</p> + <p id="7" style="font-size:12px">This has <em class="id_4">emphasis</em> and great <strong class="id_5">strength</strong> and <code class="id_6">coolness</code>.</p> <div id="root-fonts"></div> <div id="root-alerts"> </div> </div> diff --git a/test/wv/html_fixtures/shoes_splorer.html b/test/wv/html_fixtures/shoes_splorer.html index 45751777c..929240fad 100644 --- a/test/wv/html_fixtures/shoes_splorer.html +++ b/test/wv/html_fixtures/shoes_splorer.html @@ -1,14 +1,14 @@ <div id="2" style="display:flex;flex-direction:row;flex-wrap:wrap;align-content:flex-start;justify-content:flex-start;align-items:flex-start;width:100%;height:100%"> <div style="height:100%;width:100%;position:relative"> <p id="3" style="font-size:12px">What Shoes methods are available?</p> - <p id="5" style="color:#008000;font-size:12px"><strong> animate[Woot!] </strong></p> - <p id="14" style="color:#FFA500;font-size:12px">o<code>arrow</code> o<code>arc</code> o<code>line</code> o<code>oval</code> o<code>rect</code> o<code>star</code> o<code>shape</code> x<code>mask</code> </p> - <p id="16" style="color:#008000;font-size:12px"><strong> element[Woot!] </strong></p> - <p id="30" style="color:#FFA500;font-size:12px">x<code>mouse</code> o<code>motion</code> x<code>resize</code> o<code>hover</code> o<code>leave</code> o<code>keypress</code> x<code>keyrelease</code> x<code>append</code> x<code>visit</code> x<code>scroll_top</code> x<code>clipboard</code> o<code>download</code> x<code>gutter</code> </p> - <p id="34" style="color:#FFA500;font-size:12px">o<code>image</code> o<code>video</code> x<code>sound</code> </p> - <p id="37" style="color:#FF0000;font-size:12px"><em> xxx</em>setup<em>xxx </em></p> - <p id="48" style="color:#FFA500;font-size:12px">o<code>style</code> o<code>fill</code> o<code>stroke</code> x<code>cap</code> o<code>rotate</code> o<code>strokewidth</code> x<code>transform</code> x<code>translate</code> o<code>nostroke</code> o<code>nofill</code> </p> - <p id="67" style="color:#FFA500;font-size:12px">o<code>banner</code> o<code>title</code> o<code>subtitle</code> o<code>tagline</code> o<code>caption</code> o<code>para</code> o<code>inscription</code> o<code>code</code> x<code>del</code> o<code>em</code> o<code>ins</code> x<code>sub</code> x<code>sup</code> o<code>strong</code> x<code>fg</code> x<code>bg</code> o<code>link</code> o<code>span</code> </p> + <p id="5" style="color:#008000;font-size:12px"><strong class="id_4"> animate[Woot!] </strong></p> + <p id="14" style="color:#FFA500;font-size:12px">o<code class="id_6">arrow</code> o<code class="id_7">arc</code> o<code class="id_8">line</code> o<code class="id_9">oval</code> o<code class="id_10">rect</code> o<code class="id_11">star</code> o<code class="id_12">shape</code> x<code class="id_13">mask</code> </p> + <p id="16" style="color:#008000;font-size:12px"><strong class="id_15"> element[Woot!] </strong></p> + <p id="30" style="color:#FFA500;font-size:12px">x<code class="id_17">mouse</code> o<code class="id_18">motion</code> x<code class="id_19">resize</code> o<code class="id_20">hover</code> o<code class="id_21">leave</code> o<code class="id_22">keypress</code> x<code class="id_23">keyrelease</code> x<code class="id_24">append</code> x<code class="id_25">visit</code> x<code class="id_26">scroll_top</code> x<code class="id_27">clipboard</code> o<code class="id_28">download</code> x<code class="id_29">gutter</code> </p> + <p id="34" style="color:#FFA500;font-size:12px">o<code class="id_31">image</code> o<code class="id_32">video</code> x<code class="id_33">sound</code> </p> + <p id="37" style="color:#FF0000;font-size:12px"><em class="id_35"> xxx</em>setup<em class="id_36">xxx </em></p> + <p id="48" style="color:#FFA500;font-size:12px">o<code class="id_38">style</code> o<code class="id_39">fill</code> o<code class="id_40">stroke</code> x<code class="id_41">cap</code> o<code class="id_42">rotate</code> o<code class="id_43">strokewidth</code> x<code class="id_44">transform</code> x<code class="id_45">translate</code> o<code class="id_46">nostroke</code> o<code class="id_47">nofill</code> </p> + <p id="67" style="color:#FFA500;font-size:12px">o<code class="id_49">banner</code> o<code class="id_50">title</code> o<code class="id_51">subtitle</code> o<code class="id_52">tagline</code> o<code class="id_53">caption</code> o<code class="id_54">para</code> o<code class="id_55">inscription</code> o<code class="id_56">code</code> o<code class="id_57">del</code> o<code class="id_58">em</code> o<code class="id_59">ins</code> o<code class="id_60">sub</code> o<code class="id_61">sup</code> o<code class="id_62">strong</code> x<code class="id_63">fg</code> x<code class="id_64">bg</code> o<code class="id_65">link</code> o<code class="id_66">span</code> </p> <div id="root-fonts"></div> <div id="root-alerts"> </div> </div> diff --git a/test/wv/html_fixtures/simple_slides.html b/test/wv/html_fixtures/simple_slides.html index a292f7857..d2987bfcf 100644 --- a/test/wv/html_fixtures/simple_slides.html +++ b/test/wv/html_fixtures/simple_slides.html @@ -4,7 +4,7 @@ <div style="height:100%;width:100%;position:relative"></div> </div> <button id="4" onclick="scarpeHandler('4-click')" onmouseover="scarpeHandler('4-hover')">Previous Slide</button><button id="5" onclick="scarpeHandler('5-click')" onmouseover="scarpeHandler('5-hover')">Next Slide</button> - <p id="7" style="margin-bottom:10px;font-size:12px"><strong>Slide 1: Welcome to Shoes!</strong></p> + <p id="7" style="margin-bottom:10px;font-size:12px"><strong class="id_6">Slide 1: Welcome to Shoes!</strong></p> <p id="8" style="font-size:12px">Ah, yeah, Shoes!<br> Who said nobody knows Shoes? Well, let me introduce you to a feline expert in the art of Ruby Desktop GUI Libs.<br> <br> diff --git a/test/wv/html_fixtures/span.html b/test/wv/html_fixtures/span.html index 31c96da39..01a17bc42 100644 --- a/test/wv/html_fixtures/span.html +++ b/test/wv/html_fixtures/span.html @@ -1,11 +1,13 @@ <div id="2" style="display:flex;flex-direction:row;flex-wrap:wrap;align-content:flex-start;justify-content:flex-start;align-items:flex-start;width:100%;height:100%"> <div style="height:100%;width:100%;position:relative"> <div id="3" style="display:flex;flex-direction:column;align-content:flex-start;justify-content:flex-start;align-items:flex-start;margin:10px"> - <div style="height:100%;width:100%;position:relative"><span id="4" style="color:blue;font-size:span">TEXT EDITOR</span> - <p id="5" style="color:#FF0000;font-size:12px"><span id="4" style="color:blue;font-size:span">TEXT EDITOR</span> * USE ALT-Q TO QUIT</p> + <div style="height:100%;width:100%;position:relative"> + <p id="5" style="color:#FF0000;font-size:12px"><span class="id_4" style="color:#0000FF;background-color:#008000">TEXT EDITOR</span> * USE ALT-Q TO QUIT</p> </div> </div> - <span id="6" style="font-size:span">text</span> + <p id="10" style="font-size:12px">Various <del class="id_6">text</del> in <sub class="id_7">various</sub> <sup class="id_8">styles</sup> can be <span class="id_9" style="text-decoration-line:underline"><span class="id_9">hard to read</span></span>...<br> + </p> + <p id="15" style="font-size:12px">A <span class="id_11" style="text-decoration-color:#0000FF;text-decoration-line:underline"><span class="id_11">wide</span></span> <span class="id_12" style="text-decoration-color:#008000;text-decoration-line:underline;text-decoration-style:wavy"><span class="id_12">variety</span></span> <span class="id_13" style="text-decoration-line:underline;text-decoration-style:double"><span class="id_13">of</span></span> <span class="id_14" style="text-decoration-color:#006400;text-decoration-line:underline;text-underline-offset:0.3rem"><span class="id_14">underlines</span></span></p> <div id="root-fonts"></div> <div id="root-alerts"> </div> </div> diff --git a/test/wv/html_fixtures/text_sizes.html b/test/wv/html_fixtures/text_sizes.html index 1e6f1bc1d..ba38de8f1 100644 --- a/test/wv/html_fixtures/text_sizes.html +++ b/test/wv/html_fixtures/text_sizes.html @@ -7,7 +7,6 @@ <p id="7" style="font-size:14px">Caption</p> <p id="8" style="font-size:12px">Para</p> <p id="9" style="font-size:10px">Inscription</p> - <p id="10" style="font-size:10px">Inscription via ins</p> <div id="root-fonts"></div> <div id="root-alerts"> </div> </div>