HtmlDialog là lớp cho tạo và tương tác với hộp thoại HTML bằng ruby.
Tạo hộp thoại HtmlDialog
Ví dụ tạo hộp thoại đơn giản:
dialog = UI::HtmlDialog.new(
  {
    :dialog_title => "Ví dụ HTML Dialog",   # tiêu đề hộp thoại
    :preferences_key => "xuanthulab.net",   #
    :scrollable => true,                    # cho phép cuộn
    :resizable => true,                     # cho phép thay đổi cỡ
    :width => 600,                          # chiều rộng, cao
    :height => 400,
    :left => 100,                           # vị trí, tọa độ góc trên trái
    :top => 100,
    :min_width => 50,
    :min_height => 50,
    :max_width =>1000,                      # chiều rộng, cao lớn nhất
    :max_height => 1000,
    # kiểu hộp thoại UI::HtmlDialog::STYLE_UTILITY, UI::HtmlDialog::STYLE_WINDOW
    :style => UI::HtmlDialog::STYLE_DIALOG
  })
  # thiết lập Url
  dialog.set_url("https://xuanthulab.net")
  # thiết lập HTML của hộp thoại nếu muốn
  # dialog.set_html("<h1>xuanthulab.net</h1>")
dialog.show
Sửa đổi HtmlDialog
Sau khi có đối tượng HtmlDialog được tạo, có các phương thức để chỉnh sửa đổi tượng được tạo ra:
| set_html(html_string) | Phương thức để nạp nội dung HTML vào hộp thoại | 
| set_position(left, top) ⇒ true | Thiết lập vị trí | 
| set_size(width, height) ⇒ true | Thiết lập cỡ hộp thoại | 
| set_url(url) ⇒ nil | Nạp HTML từ URL | 
| #set_file(filename) ⇒ nil | Nạp HTML từ file | 
Ví dụ có nội dung html lưu trong file file1.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Ví dụ HTML</title>
</head>
<body style="background-color: #00bcd4;">
    <h1>Xin chào, đây là nội dung HTML</h1>
</body>
</html>
Sử dụng nạp vào hộp thoại
dialog = UI::HtmlDialog.new(
{
  :dialog_title => "Ví dụ HTML Dialog",
  :preferences_key => "xuanthulab.net",
  :scrollable => true,
  :resizable => true,
  :width => 300,
  :height => 200,
  :left => 100,
  :top => 100,
  :min_width => 50,
  :min_height => 50,
  :max_width =>500,
  :max_height => 300,
  :style => UI::HtmlDialog::STYLE_DIALOG
})
dialog.set_file(__dir__ + '/file1.html')
dialog.show
 
JavaScript, CSS trong HtmlDialog
Các nội dung HTMl trong HtmlDialog có thể tích hợp CSS, JavaScript. Học về CSS tại CSS, học JavaScript tại JavaScript
Ví dụ có HTML có tích hợp CSS, JavaScript trong file2.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Ví dụ HTML</title>
  <style>
      #body {
        background-color:bisque;
        color: #333;
      }
  </style>
  <script>
       var i = 0;
       function addcontent()
       {
           i++;
           alert("Sẽ thêm nội dung HTML");
           let bd = document.getElementById('body');
           var n = document.createElement("p");
           n.textContent = "Nội dung chèn vào " + i;
           bd.prepend(n);
       }
  </script>
</head>
<body id="body">
    <h1>Xin chào, đây là nội dung HTML</h1>
    <input type="button" value="Thêm nội dung" onclick="addcontent();" />
</body>
</html>
Sau đó nạp nội dung này vào HtmlDialog
 
Tương tác qua lại JavaScript và SketchUp Ruby
Chạy JavaScript từ Ruby
Có thể thi hành một đoạn JavaScript lưu trong một chuỗi (không thẻ script) bằng cách gọi execute_script(script),
ví dụ sau, khi nhấn chọn face hoặc cung tròn thì nó sẽ thi hành một script để thiết lập nội dung hộp thoại
# 28.ruby-call-js.rb
# load "/Users/xuanthulab/Desktop/learn-ruby/sketchup/28.ruby-call-js.rb"
require 'sketchup.rb'
require 'extensions.rb'
class SelObserver < Sketchup::SelectionObserver
  @entityselect = nil
  # @param [Sketchup::Entity] e
  def showinfomation(e)
    @entityselect = e
    puts e.class.to_s
    s = ''
    # @type [Sketchup::ArcCurve]
    curve = nil
    curve = e.curve if e.class.to_s == 'Sketchup::Edge'
    if curve
      #  curve.edges.each {|x| puts x}
      puts curve.class
      if curve.class.to_s == 'Sketchup::ArcCurve'
         if  (curve.end_angle - curve.start_angle).abs  == 2 * Math::PI
            s += "<h1>Hinh tròn</h1>"
         else
            s += "<h1>Cung tròn</h1>"
         end
      end
      s += "<h2>Phân đoạn: #{curve.edges.length}</h2>"
      s += "<h2>Chiều dài: #{curve.length.to_mm}</h2>"
    else
      # Face
      if e.class.to_s == 'Sketchup::Face'
        # @type [Sketchup::Face]
        face = e
        s += "<h1>Mặt - Face</h1>"
        s += "<h2>Pháp tuyến: #{face.normal.to_s}</h2>"
        s += "<h2>Diện tích: #{face.area.to_mm.to_mm}</h2>"
        s += "<h2>Phân đoạn: #{face.edges.length}</h2>"
      end
    end
    s = 'Chọn cung tròn hoặc mặt!' if s == ''
    # Đưa vào Hộp thoại
    $dialoginfo.execute_script("setBodyContent('#{s}')")
  end
  def onSelectionBulkChange(selection)
      first_ent = selection[0]
      if ! (first_ent.equal? @entityselect)
        showinfomation first_ent
      end
  end
end
sel = Sketchup.active_model.selection
if defined?($selob)
  puts "Remove Observer"
  sel.remove_observer $selob
end
$selob = SelObserver.new
sel.add_observer $selob
$dialoginfo = UI::HtmlDialog.new(
  {
    :dialog_title => "Ví dụ HTML Dialog",
    :preferences_key => "xuanthulab.net",
    :scrollable => true,
    :resizable => true,
    :width => 300,
    :height => 200,
    :left => 100,
    :top => 100,
    :min_width => 50,
    :min_height => 50,
    :max_width =>500,
    :max_height => 300,
    :style => UI::HtmlDialog::STYLE_DIALOG
  })
  $dialoginfo.set_file(__dir__ + '/28.ruby-call-js.html')
  $dialoginfo.show
28.ruby-call-js.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
      #body {
        background-color:bisque;
        color: #333;
      }
  </style>
  <script>
       function setBodyContent(s)
       {
          bd = document.getElementById('body');
          bd.innerHTML = s;
       }
  </script>
</head>
<body id="body">
    <h1>Xin chào, đây là nội dung HTML</h1>
</body>
</html>
 
Chạy Ruby từ JavaScript
Cú pháp để khai báo một callback (phương thức) Ruby - mà có thể gọi được từ JavaScript thì sử dụng add_action_callback của HtmlDialog như sau:
add_action_callback(callback_name) {|action_context, param1, param2, pram3 ... }
Lúc này trong Js có thể gọi callback_name(param1, param2, pram3, ...),
chú ý phần khai báo có action_context hiện không được dùng đến, với cú pháp cụ thể:
sketchup.callback_name(param1, param2, pram3, {
  onCompleted: function() {
    console.log('Ruby side done.');
  }
});
Ví dụ khai báo phương thức callback create_face tạo mặt từ bốn điểm nhập vào:
# 29.js_call_ruby.rb
# load "/Users/xuanthulab/Desktop/learn-ruby/sketchup/29.js_call_ruby.rb"
require 'sketchup.rb'
require 'extensions.rb'
 if defined? $dialoginfo
    $dialoginfo.close
 end
$dialoginfo = UI::HtmlDialog.new(
  {
    :dialog_title => "Ví dụ HTML Dialog",
    :preferences_key => "xuanthulab.net",
    :scrollable => true,
    :resizable => true,
    :width => 300,
    :height => 400,
    :left => 100,
    :top => 100,
    :min_width => 50,
    :min_height => 50,
    :max_width =>500,
    :max_height => 400,
    :style => UI::HtmlDialog::STYLE_DIALOG
  })
  $dialoginfo.set_file(__dir__ + '/29.js_call_ruby.html')
  $dialoginfo.add_action_callback("create_face") {|action_context, points|
    # @type [Array] points
    # points.each {
    #    |e|
    #    # @type [Hash] e
    #  }
    pt1 = Geom::Point3d.new(Float(points[0]['x'].strip).mm, Float(points[0]['y'].strip).mm, Float(points[0]['z'].strip).mm)
    pt2 = Geom::Point3d.new(Float(points[1]['x'].strip).mm, Float(points[1]['y'].strip).mm, Float(points[1]['z'].strip).mm)
    pt3 = Geom::Point3d.new(Float(points[2]['x'].strip).mm, Float(points[2]['y'].strip).mm, Float(points[2]['z'].strip).mm)
    pt4 = Geom::Point3d.new(Float(points[3]['x'].strip).mm, Float(points[3]['y'].strip).mm, Float(points[3]['z'].strip).mm)
    vec1 = pt2 - pt1
    vec2 = pt2 - pt3
    plane = [pt1, vec1 * vec2]
    if pt4.on_plane? plane
      Sketchup.active_model.entities.add_face pt1, pt2, pt3, pt4
    else
      puts "Invalid input. Points must lie on the same plane."
    end
  }
  $dialoginfo.show
Nội dung file HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
  <script>
       function send()
       {
        let points = Array();
        for (let index = 1; index <= 4; index++) {
            let point = {}
            point["x"] = document.getElementById('x' + index).value
            point["y"] = document.getElementById('y' + index).value
            point["z"] = document.getElementById('z' + index).value
            points.push(point)
        }
        sketchup.create_face(points, {
            onCompleted: function(ob) {
            }
          });
       }
  </script>
</head>
<body>
    <div class="container pt-3">
       <div class="card w-100">
          <div class="card-body">
            <p><strong>Tạo mặt từ các điểm</strong></p>
            <!-- Location of the first vertex -->
                Điểm 1:
                <input type="text" id="x1" value="0.0" size="10" maxlength="6"/> <input type="text" id="y1" value="0.0" size="10" maxlength="6"/> <input type="text" id="z1" value="0.0" size="10" maxlength="6"/> <br /><br />
                <!-- Location of the second vertex -->
                Điểm 2:
                <input type="text" id="x2" value="10.0" size="10" maxlength="6"/> <input type="text" id="y2" value="0.0" size="10" maxlength="6"/> <input type="text" id="z2" value="0.0" size="10" maxlength="6"/> <br /><br />
                <!-- Location of the third vertex -->
                Điểm 3:
                <input type="text" id="x3" value="10.0" size="10" maxlength="6"/> <input type="text" id="y3" value="10.0" size="10" maxlength="6"/> <input type="text" id="z3" value="0.0" size="10" maxlength="6"/> <br /><br />
                <!-- Location of the fourth vertex -->
                Điểm 4:
                <input type="text" id="x4" value="0.0" size="10" maxlength="6"/> <input type="text" id="y4" value="10.0" size="10" maxlength="6"/> <input type="text" id="z4" value="0.0" size="10" maxlength="6"/> <br /><br />
            <input class="btn btn-success" type="button" onclick="send()" value="Tạo Face">
          </div>
       </div>
    </div>
</body>
</html>
 
     
        