Class: MetaRuby::GUI::HTML::Page

Inherits:
Qt::Object
  • Object
show all
Defined in:
lib/metaruby/gui/html/page.rb

Overview

A helper class that gives us easy-to-use page elements on a Qt::WebView

Such a page is managed as a list of sections (called Fragment). A new fragment is added or updated with #push

Direct Known Subclasses

ModelBrowser::Page

Defined Under Namespace

Classes: Fragment

Constant Summary collapse

PAGE_TEMPLATE =

The ERB template for a page

See Also:

File.join(RESSOURCES_DIR, "page.rhtml")
PAGE_BODY_TEMPLATE =

The ERB template for a page body

See Also:

File.join(RESSOURCES_DIR, "page_body.rhtml")
FRAGMENT_TEMPLATE =

The ERB template for a page fragment

See Also:

File.join(RESSOURCES_DIR, "fragment.rhtml")
LIST_TEMPLATE =

The ERB template for a list

See Also:

File.join(RESSOURCES_DIR, "list.rhtml")
ASSETS =

Assets (CSS, javascript) that are included in every page

%w{page.css jquery.min.js jquery.selectfilter.js}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(page) ⇒ Page

Creates a new Page object

Parameters:



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/metaruby/gui/html/page.rb', line 65

def initialize(page)
    super()
    @page = page
    @head = Array.new
    @scripts = Array.new
    @fragments = []
    @templates = Hash.new
    @auto_id = 0

    if defined?(Qt::WebPage) && page.kind_of?(Qt::WebPage)
        page.link_delegation_policy = Qt::WebPage::DelegateAllLinks
        Qt::Object.connect(page, SIGNAL('linkClicked(const QUrl&)'), self, SLOT('pageLinkClicked(const QUrl&)'))
    end
    @object_uris = Hash.new
end

Instance Attribute Details

#exception_rendering#render (readonly)

Object used to render exceptions in #push_exception

It is set by #enable_exception_rendering

Returns:

  • (#render)


60
61
62
# File 'lib/metaruby/gui/html/page.rb', line 60

def exception_rendering
  @exception_rendering
end

#fragmentsArray<Fragment> (readonly)

List of fragments

Returns:



38
39
40
# File 'lib/metaruby/gui/html/page.rb', line 38

def fragments
  @fragments
end

#headArray<String> (readonly)

Content to be rendered in the page head

Returns:

  • (Array<String>)


48
49
50
# File 'lib/metaruby/gui/html/page.rb', line 48

def head
  @head
end

#object_urisObject

Static mapping of objects to URIs

See Also:

  • #uri_fo


43
44
45
# File 'lib/metaruby/gui/html/page.rb', line 43

def object_uris
  @object_uris
end

#pageQt::WebPage, HTMLPage (readonly)

The underlying page rendering object

Returns:



33
34
35
# File 'lib/metaruby/gui/html/page.rb', line 33

def page
  @page
end

#page_nameString?

The content of the <title> tag

Returns:

  • (String, nil)


24
25
26
# File 'lib/metaruby/gui/html/page.rb', line 24

def page_name
  @page_name
end

#scriptsArray<String> (readonly)

Scripts to be loaded in the page

Returns:

  • (Array<String>)


53
54
55
# File 'lib/metaruby/gui/html/page.rb', line 53

def scripts
  @scripts
end

#titleString?

The content of a toplevel <h1> tag

Returns:

  • (String, nil)


28
29
30
# File 'lib/metaruby/gui/html/page.rb', line 28

def title
  @title
end

Class Method Details

.copy_assets_to(target_dir, assets = ASSETS) ⇒ Object

Copy the assets to a target directory

This can be used to create self-contained HTML pages using the Page class, by providing a different ressource dir to e.g. #html or #html_body and copying the assets to it.



208
209
210
211
212
213
# File 'lib/metaruby/gui/html/page.rb', line 208

def self.copy_assets_to(target_dir, assets = ASSETS)
    FileUtils.mkdir_p target_dir
    assets.each do |file|
        FileUtils.cp File.join(RESSOURCES_DIR, file), target_dir
    end
end

.main_doc(text) ⇒ String

Converts the given text from markdown to HTML and generates the necessary <div> context.

Returns:

  • (String)

    the HTML snippet that should be used to render the given text as main documentation



176
177
178
# File 'lib/metaruby/gui/html/page.rb', line 176

def self.main_doc(text)
    "<div class=\"doc-main\">#{Kramdown::Document.new(text).to_html}</div>"
end

.to_html(object, renderer, ressource_dir: RESSOURCES_DIR, **options) ⇒ String

Renders an object into a HTML page

Parameters:

  • object (Object)
  • renderer (#render)

    the object that renders into the page. The object must accept a MetaRuby::GUI::HTML::Page at initialization and its #render method gets called passing the object and rendering options

Returns:

  • (String)


467
468
469
470
# File 'lib/metaruby/gui/html/page.rb', line 467

def self.to_html(object, renderer, ressource_dir: RESSOURCES_DIR, **options)
    to_html_page(object, renderer, **options).
        html(ressource_dir: ressource_dir)
end

.to_html_body(object, renderer, ressource_dir: RESSOURCES_DIR, **options) ⇒ String

Renders an object into a HTML body

Parameters:

  • object (Object)
  • renderer (#render)

    the object that renders into the page. The object must accept a MetaRuby::GUI::HTML::Page at initialization and its #render method gets called passing the object and rendering options

Returns:

  • (String)


476
477
478
479
# File 'lib/metaruby/gui/html/page.rb', line 476

def self.to_html_body(object, renderer, ressource_dir: RESSOURCES_DIR, **options)
    to_html_page(object, renderer, **options).
        html_body(ressource_dir: ressource_dir)
end

.to_html_page(object, renderer, **options) ⇒ Page

Renders an object into a HTML page

Parameters:

  • object (Object)
  • renderer (#render)

    the object that renders into the page. The object must accept a MetaRuby::GUI::HTML::Page at initialization and its #render method gets called passing the object and rendering options

Returns:



456
457
458
459
460
461
# File 'lib/metaruby/gui/html/page.rb', line 456

def self.to_html_page(object, renderer, **options)
    webpage = HTMLPage.new
    page = new(webpage)
    renderer.new(page).render(object, **options)
    page
end

Instance Method Details

#add_script(html) ⇒ Object

Add content to #scripts

Parameters:

  • html (String)


124
125
126
# File 'lib/metaruby/gui/html/page.rb', line 124

def add_script(html)
    scripts << html
end

#add_to_head(html) ⇒ Object

Add content to #head

Parameters:

  • html (String)


117
118
119
# File 'lib/metaruby/gui/html/page.rb', line 117

def add_to_head(html)
    head << html
end

#add_to_setup(obj) ⇒ Object

Add content to the page setup (head and scripts)

Parameters:

  • obj (#head, #scripts)

    the object defining the content to be added

See Also:



109
110
111
112
# File 'lib/metaruby/gui/html/page.rb', line 109

def add_to_setup(obj)
    add_to_head(obj.head)
    add_script(obj.scripts)
end

#auto_idObject

Automatic generation of a fragment ID



393
394
395
# File 'lib/metaruby/gui/html/page.rb', line 393

def auto_id
    "metaruby-html-page-fragment-#{@auto_id += 1}"
end

#clearObject

Removes all existing displays



244
245
246
247
# File 'lib/metaruby/gui/html/page.rb', line 244

def clear
    page.main_frame.html = ""
    fragments.clear
end

#enable_exception_rendering(renderer = ExceptionRendering.new(self)) ⇒ Object

Enable rendering of exceptions using the given renderer

Parameters:



400
401
402
403
# File 'lib/metaruby/gui/html/page.rb', line 400

def enable_exception_rendering(renderer = ExceptionRendering.new(self))
    add_to_setup(renderer)
    @exception_rendering = renderer
end

#find_button_by_url(url) ⇒ Button?

Find a button from its URI

Returns:



287
288
289
290
291
292
293
294
295
# File 'lib/metaruby/gui/html/page.rb', line 287

def find_button_by_url(url)
    id = url.path
    fragments.each do |fragment|
        if result = fragment.buttons.find { |b| b.id == id }
            return result
        end
    end
    nil
end

#find_first_element(selector) ⇒ Object



297
298
299
# File 'lib/metaruby/gui/html/page.rb', line 297

def find_first_element(selector)
    page.main_frame.find_first_element(selector)
end

#html(ressource_dir: RESSOURCES_DIR) ⇒ Object

Generate the HTML

Parameters:

  • ressource_dir (String) (defaults to: RESSOURCES_DIR)

    the path to the ressource directory that #path_in_resource should use



264
265
266
# File 'lib/metaruby/gui/html/page.rb', line 264

def html(ressource_dir: RESSOURCES_DIR)
    load_template(PAGE_TEMPLATE).result(binding)
end

#html_body(ressource_dir: RESSOURCES_DIR) ⇒ Object

Generate the body of the HTML document

Parameters:

  • ressource_dir (String) (defaults to: RESSOURCES_DIR)

    the path to the ressource directory that #path_in_resource should use



272
273
274
# File 'lib/metaruby/gui/html/page.rb', line 272

def html_body(ressource_dir: RESSOURCES_DIR)
    load_template(PAGE_BODY_TEMPLATE).result(binding)
end

#html_fragment(fragment, ressource_dir: RESSOURCES_DIR) ⇒ Object

Generate the HTML of a fragment

Parameters:

  • ressource_dir (String) (defaults to: RESSOURCES_DIR)

    the path to the ressource directory that #path_in_resource should use



280
281
282
# File 'lib/metaruby/gui/html/page.rb', line 280

def html_fragment(fragment, ressource_dir: RESSOURCES_DIR)
    load_template(FRAGMENT_TEMPLATE).result(binding)
end

Helper that generates a HTML link to a given object

The object URI is resolved using #uri_for. If there is no known link to the object, it is returned as text

Parameters:

  • object (Object)

    the object to create a link to

  • text (String) (defaults to: nil)

    the link text. Defaults to object#name

Returns:

  • (String)


154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/metaruby/gui/html/page.rb', line 154

def link_to(object, text = nil, **args)
    text = HTML.escape_html(text || object.name || "<anonymous>")
    if uri = uri_for(object)
        if uri !~ /^\w+:\/\//
            if uri[0, 1] != '/'
                uri = "/#{uri}"
            end
            uri = Qt::Url.new("link://metaruby#{uri}")
        else
            uri = Qt::Url.new(uri)
        end
        args.each { |k, v| uri.add_query_item(k.to_s, v.to_s) }
        "<a href=\"#{uri.to_string}\">#{text}</a>"
    else text
    end
end

#load_javascript(file) ⇒ Object

Load a javascript file in the head



141
142
143
144
# File 'lib/metaruby/gui/html/page.rb', line 141

def load_javascript(file)
    add_to_head(
        "<script type=\"text/javascript\" src=\"#{path_in_resource(file)}\"></script>")
end

#load_template(*path) ⇒ ERB

Lazy loads a template

Returns:

  • (ERB)


218
219
220
221
222
223
# File 'lib/metaruby/gui/html/page.rb', line 218

def load_template(*path)
    path = File.join(*path)
    @templates[path] ||= ERB.new(File.read(path))
    @templates[path].filename = path
    @templates[path]
end

#main_doc(text) ⇒ Object



180
181
182
# File 'lib/metaruby/gui/html/page.rb', line 180

def main_doc(text)
    self.class.main_doc(text)
end

#pageLinkClicked(url) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Slot that catches the page’s link-clicked signal and dispatches into the buttonClicked signal (for buttons), fileClicked for files and linkClicked for links



306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
# File 'lib/metaruby/gui/html/page.rb', line 306

def pageLinkClicked(url)
    if url.scheme == 'btn' && url.host == 'metaruby'
        if btn = find_button_by_url(url)
            new_state = if url.fragment == 'on' then true
                        else false
                        end

            btn.state = new_state
            new_text = btn.text
            element = find_first_element("a##{btn.html_id}")
            element.replace(btn.render)

            emit buttonClicked(btn.id, new_state)
        else
            MetaRuby.warn "invalid button URI #{url.to_string}: could not find corresponding handler (known buttons are #{fragments.flat_map { |f| f.buttons.map { |btn| btn.id.to_string } }.sort.join(", ")})"
        end
    elsif url.scheme == 'link' && url.host == 'metaruby'
        emit linkClicked(url)
    elsif url.scheme == "file"
        emit fileOpenClicked(url)
    else
        MetaRuby.warn "MetaRuby::GUI::HTML::Page: ignored link #{url.toString}"
    end
end

#path_in_resource(path) ⇒ String

Resolves a relative path to a path in the underlying application’s resource folder

Returns:

  • (String)


132
133
134
135
136
137
138
# File 'lib/metaruby/gui/html/page.rb', line 132

def path_in_resource(path)
    if Pathname.new(path).absolute?
        path
    else
        File.join('${RESOURCE_DIR}', path)
    end
end

#push(title, html, id: auto_id, **view_options) ⇒ Object

Adds a fragment to this page, with the given title and HTML content

The added fragment is enclosed in a div block to allow for dynamic replacement

Parameters:

  • view_options (Hash)

    a customizable set of options

Options Hash (**view_options):

  • id (String)

    the ID of the fragment. If given, and if an existing fragment with the same ID exists, the new fragment replaces the existing one, and the view is updated accordingly.



373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'lib/metaruby/gui/html/page.rb', line 373

def push(title, html, id: auto_id, **view_options)
    if id
        # Check whether we should replace the existing content or
        # push it new
        fragment = fragments.find do |fragment|
            fragment.id == id
        end
        if fragment
            fragment.html = html
            element = find_first_element("div##{fragment.id}")
            element.replace(html_fragment(fragment))
            return
        end
    end

    fragments << Fragment.new(title, html, id: id, **view_options)
    update_html
end

#push_exception(title, e, id: auto_id, **options) ⇒ Object

Push a fragment that represents the given exception

#enable_exception_rendering must have been called first

Parameters:

  • title (String)

    the fragment title

  • e (Exception)

    the exception to render

  • id (String) (defaults to: auto_id)

    the fragment ID

  • options (Hash)

    additional options passed to #push



413
414
415
416
# File 'lib/metaruby/gui/html/page.rb', line 413

def push_exception(title, e, id: auto_id, **options)
    html = exception_rendering.render(e, nil, id)
    push(title, html, id: id, **options)
end

#render_item(name, value = nil) ⇒ Object

Create an item for the rendering in tables



419
420
421
422
423
424
425
# File 'lib/metaruby/gui/html/page.rb', line 419

def render_item(name, value = nil)
    if value
        "<li><b>#{name}</b>: #{value}</li>"
    else
        "<li>#{name}</li>"
    end
end

#render_list(title, items, filter: false, id: nil, **push_options) ⇒ Object

Render a list of objects into HTML and push it to this page

Parameters:

  • title (String, nil)

    the section’s title. If nil, no new section is created

  • items (Array<Object>, Array<(Object,Hash)>)

    the list items, one item per line. If a hash is provided, it is used as HTML attributes for the lines

  • filter (Boolean) (defaults to: false)

    only render the items with the given ‘id’

  • id (String) (defaults to: nil)

    the id to filter if filter: is true

  • push_options (Hash)

    options that are passed to #push. The id: option is added to it.



438
439
440
441
442
443
444
# File 'lib/metaruby/gui/html/page.rb', line 438

def render_list(title, items, filter: false, id: nil, **push_options)
    if filter && !id
        raise ArgumentError, ":filter is true, but no :id has been given"
    end
    html = load_template(LIST_TEMPLATE).result(binding)
    push(title, html, push_options.merge(id: id))
end

#restoreObject

Restore the page at the state it was at the last call to #save



340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
# File 'lib/metaruby/gui/html/page.rb', line 340

def restore
    return if !@saved_state

    fragments_by_id = Hash.new
    @saved_state.each do |fragment|
        fragments_by_id[fragment.id] = fragment
    end

    # Delete all fragments that are not in the saved state
    fragments.delete_if do |fragment|
        element = find_first_element("div##{fragment.id}")
        if old_fragment = fragments_by_id[fragment.id]
            if old_fragment.html != fragment.html
                element.replace(old_fragment.html)
            end
        else
            element.replace("")
            true
        end
    end
end

#saveObject

Save the current state of the page, so that it can be restored by calling #restore



335
336
337
# File 'lib/metaruby/gui/html/page.rb', line 335

def save
    @saved_state = fragments.map(&:dup)
end

#scale_attribute(node, name, scale) ⇒ Object



249
250
251
252
253
# File 'lib/metaruby/gui/html/page.rb', line 249

def scale_attribute(node, name, scale)
    node.attributes[name] = node.attributes[name].gsub /[\d\.]+/ do |n|
        (Float(n) * scale).to_s
    end
end

#update_htmlObject

Generate the HTML and update the underlying #page



256
257
258
# File 'lib/metaruby/gui/html/page.rb', line 256

def update_html
    page.main_frame.html = html
end

#uri_for(object) ⇒ Object

Generate a URI for an object

The method must either return a string that is a URI representing the object, or nil if there is none. The choice of the URI is application-specific, used by the application to recognize links

The default application returns a file:/// URI for a Pathname object, and then uses #object_uris

See Also:



235
236
237
238
239
240
241
# File 'lib/metaruby/gui/html/page.rb', line 235

def uri_for(object)
    if object.kind_of?(Pathname)
        "file://#{object.expand_path}"
    else
        object_uris[object]
    end
end