diff --git a/.ameba.yml b/.ameba.yml
new file mode 100644
index 0000000..04dd36e
--- /dev/null
+++ b/.ameba.yml
@@ -0,0 +1,15 @@
+# This configuration file was generated by `ameba --gen-config`
+# on 2021-06-26 15:36:50 UTC using Ameba version 0.13.4.
+# The point is for the user to remove these configuration records
+# one by one as the reported problems are removed from the code base.
+
+Style/RedundantBegin:
+ Description: Disallows redundant begin blocks
+ Enabled: false
+ Severity: Convention
+
+Metrics/CyclomaticComplexity:
+ Description: Disallows methods with a cyclomatic complexity higher than `MaxComplexity`
+ MaxComplexity: 30
+ Enabled: true
+ Severity: Convention
diff --git a/.gitignore b/.gitignore
index 0397cf7..479c8f2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,5 @@
/lib/
/bin/
/.shards/
+
+/shard.lock
\ No newline at end of file
diff --git a/example/application.cr b/example/application.cr
index 226f528..a324568 100644
--- a/example/application.cr
+++ b/example/application.cr
@@ -2,4 +2,5 @@ require "../src/layout"
builder = Layout::Transpiler::Builder.new
builder.build_from_document("#{__DIR__}/dist/index.html")
+
builder.run
diff --git a/example/dist/components/authorization.html b/example/dist/components/authorization.html
deleted file mode 100644
index 7b72103..0000000
--- a/example/dist/components/authorization.html
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/example/dist/index.html b/example/dist/index.html
index 480375d..e3d08eb 100644
--- a/example/dist/index.html
+++ b/example/dist/index.html
@@ -1,31 +1,80 @@
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
\ No newline at end of file
diff --git a/example/dist/scripts/authorization.js b/example/dist/scripts/authorization.js
deleted file mode 100644
index 55034a0..0000000
--- a/example/dist/scripts/authorization.js
+++ /dev/null
@@ -1,11 +0,0 @@
-function onComponentDidUpdate(_state, usernameField, eventType) {
- switch (eventType) {
- case "KEY_PRESS":
- var content = usernameField.getText();
- if (content.length > 6) {
- usernameField.setForegroundColor(200, 0, 0, 100);
- } else {
- usernameField.setForegroundColor(0, 0, 0, 100);
- }
- }
-}
\ No newline at end of file
diff --git a/example/dist/scripts/index.js b/example/dist/scripts/index.js
index e69de29..194270d 100644
--- a/example/dist/scripts/index.js
+++ b/example/dist/scripts/index.js
@@ -0,0 +1,31 @@
+/**
+ *
+ *
+ * Index.js - Contains the business logic for the main application component.
+ *
+ *
+ */
+
+const appConfiguration = {
+ window: {
+ width: 1200,
+ height: 800,
+ title: "Monkey Shoulder"
+ }
+};
+
+const textMutation = {
+ updateNumberOne: function (_element, text) {
+ var label = getElementByComponentId("numberOneLabel");
+ label.setText(text);
+
+ print(text);
+ },
+
+ updateNumberTwo: function (_element, text) {
+ var label = getElementByComponentId("numberTwoLabel");
+ label.setText(text);
+
+ print(text);
+ },
+};
\ No newline at end of file
diff --git a/example/dist/styles/authorization.css b/example/dist/styles/authorization.css
deleted file mode 100644
index dcbc5f0..0000000
--- a/example/dist/styles/authorization.css
+++ /dev/null
@@ -1,26 +0,0 @@
-.usernameTextInput {
- transition: all 0s ease;
- font-size: 20px;
- margin-top: 5px;
- margin-left: 150px;
- margin-bottom: 5px;
- margin-right: 150px;
-}
-
-.usernameTextInput selection {
- background-color: orange;
-}
-
-.passwordTextInput {
- transition: all 0s ease;
- font-size: 20px;
- margin-top: 5px;
- margin-left: 150px;
- margin-bottom: 5px;
- margin-right: 150px;
-}
-
-.passwordTextInput selection {
- background-color: orange;
-}
-
diff --git a/example/dist/styles/index.css b/example/dist/styles/index.css
index 4db6541..4ad3fe4 100644
--- a/example/dist/styles/index.css
+++ b/example/dist/styles/index.css
@@ -1,63 +1,183 @@
-.mainWindow {
- color: blue;
+.main-window {
background-color: black;
}
-.mainBox {
+.main-box {
transition: all 1s ease;
- background-color: white;
+ background-color: rgb(28, 30, 39);
}
-.disablerBox{
+.main-label {
+ margin-top: 20px;
margin-bottom: 20px;
+ margin-left: 20px;
+ margin-right: 20px;
+ font-weight: 200;
+ font-family: 'Fira Code';
+ color: rgb(233, 131, 118);
}
-.darkModeLabel {
- transition: all 1s ease;
- color: black;
+.main-text-input{
+ margin-top: 20px;
+ margin-bottom: 20px;
+ margin-left: 20px;
+ margin-right: 20px;
+ background-color: transparent;
+ border-color: rgb(231, 68, 111);
+ color: rgb(241, 149, 137);
+ font-weight: 100;
+ font-size: 12px;
+ border-radius: 100px;
+ box-shadow: none;
+ text-shadow: none;
}
-.darkModeSwitch {
- transition: all 1s ease;
+.number-text{
+ margin-left: 20px;
+ margin-right: 20px;
background-color: transparent;
- color: black;
+ border-color: rgb(231, 68, 111);
+ color: rgb(241, 149, 137);
+ font-weight: 100;
+ font-size: 12px;
+ border-radius: 100px;
+ box-shadow: none;
+ text-shadow: none;
}
-.darkModeSwitch slider {
- transition: all 1s ease;
- background-color: black;
- border: none;
+.number-text:hover{
+ color: red;
}
-.mainLabel {
- transition: all 1s ease;
- color: black;
- margin-top: 50px;
- font-size: 50px;
+.main-button{
+ transition: all 0.5s ease;
+ background-color: transparent;
+ background-image: none;
+ box-shadow: none;
+ text-shadow: none;
+ color: rgb(255,255,255);
+ font-weight: 100;
+ border-color: rgb(231, 68, 111);
+ border-radius: 100px;
+ margin-left: 20px;
+ margin-right: 20px;
+ margin-bottom: 20px;
}
-.mainLabel:focus {
- color: red;
+.main-button:hover{
+ transition: all 0.5s ease;
+ border-color: rgb(200, 60, 100);
+ color:rgb(200,200,200);
}
-.authorizationButton {
+.main-switch{
transition: all 1s ease;
- font-size: 25px;
- border-color: transparent;
background-color: transparent;
- margin-top: 25px;
- margin-bottom: 50px;
- color: black;
+ background-image: none;
+ box-shadow: none;
+ text-shadow: none;
+ border-color: rgb(231, 68, 111);
+ margin-top: 20px;
+ margin-bottom: 20px;
+ margin-right: 20px;
}
-.authorizationButton:hover {
- transition: all 1s ease;
- color: rgba(0, 0, 0, 0.5);
+.main-switch slider {
+ transition: all 0.5s ease;
+ background-color: transparent;
+ background-image: none;
+ box-shadow: none;
+ text-shadow: none;
+ border-color: rgb(241, 149, 137);
}
-.authorizationButton:active {
- transition: all 1s ease;
- color: transparent;
+.main-switch slider:hover {
+ transition: all 0.5s ease;
+ background-color: transparent;
+ background-image: none;
+ box-shadow: none;
+ text-shadow: none;
+ border-color: rgb(248, 202, 196);
+}
+
+.main-list-box {
+ margin-left: 20px;
+ margin-right: 20px;
+ margin-bottom: 20px;
+ background-color: transparent;
+}
+
+.main-list-box row{
+ transition: all 0.5s ease;
+ font-weight: 200;
+ font-family: 'Fira Code';
+ color: rgb(233, 131, 118);
+ background-color: transparent;
+ margin-top: 20px;
+}
+
+.main-list-box row:hover{
+ transition: all 0.5s ease;
+ color: rgb(248, 202, 196);
}
+.main-list-box row:active{
+ transition: all 0.5s ease;
+ color: rgb(255, 255, 255);
+}
+
+.horizontal-separator{
+ margin-left: 20px;
+ margin-right: 20px;
+ background-color: rgb(233, 131, 118);
+}
+
+.horizontal-separator-number{
+ margin-left: 20px;
+ margin-right: 20px;
+ margin-bottom: 20px;
+ background-color: rgb(233, 131, 118);
+}
+
+.main-scrolled-window {
+ margin-right: 5px;
+ margin-left: 5px;
+ margin-top: 5px;
+ margin-bottom: 5px;
+}
+
+.main-scrolled-window overshoot{
+ background-image: none;
+}
+
+.main-scrolled-window slider{
+ background-image: none;
+ background-color: rgb(200, 60, 100);
+ border: 5px solid transparent;
+}
+.main-scrolled-window scrollbar{
+ background-color: transparent;
+ border: none;
+}
+
+.main-frame{
+ color: rgb(233, 131, 118);
+ background-color: transparent;
+ margin-left: 20px;
+ margin-right: 20px;
+}
+
+.main-frame border {
+ border-radius: 5px;
+ border: 1px dotted rgb(200, 60, 100);
+}
+
+.main-frame label {
+ font-weight: 200;
+ font-family: 'Fira Code';
+}
+
+.number-box{
+ margin-bottom: 20px;
+}
diff --git a/shard.yml b/shard.yml
index f52d60b..d7843e4 100644
--- a/shard.yml
+++ b/shard.yml
@@ -11,11 +11,18 @@ targets:
dependencies:
duktape:
github: jessedoyle/duktape.cr
- version: ~> 1.0.0
gobject:
github: jhass/crystal-gobject
- version: ~> 0.10.0
+
+ beautify:
+ github: grkek/beautify
+ branch: master
+
+development_dependencies:
+ ameba:
+ github: crystal-ameba/ameba
+ version: ~> 0.13.0
crystal: 1.0.0
diff --git a/src/layout.cr b/src/layout.cr
index f50d380..6599d54 100644
--- a/src/layout.cr
+++ b/src/layout.cr
@@ -1,6 +1,5 @@
require "json"
+require "beautify"
require "./layout/**"
-GC.disable
-
module Layout; end
diff --git a/src/layout/dom/application.cr b/src/layout/dom/application.cr
index 0b49f1d..20af4fe 100644
--- a/src/layout/dom/application.cr
+++ b/src/layout/dom/application.cr
@@ -10,26 +10,7 @@ module Layout
def initialize(@attributes, @children)
@kind = "Application"
-
- @attributes.map do |key, value|
- matches = value.scan(/\${(.*?)}/)
-
- case matches.size
- when 0
- @attributes[key] = value
- else
- matches.each do |match|
- hash = match.to_h
-
- begin
- @attributes[key] = value.gsub(hash[0].not_nil!, "#{Layout::Js::Engine::INSTANCE.evaluate(hash[1].not_nil!)}")
- rescue ex : Exception
- @attributes[key] = value
- puts "An exception occured while evaluating a variable format routine: #{ex}"
- end
- end
- end
- end
+ substitution()
end
def to_html : String
diff --git a/src/layout/dom/box.cr b/src/layout/dom/box.cr
index f1b2290..ba1053e 100644
--- a/src/layout/dom/box.cr
+++ b/src/layout/dom/box.cr
@@ -8,33 +8,53 @@ module Layout
def initialize(@attributes, @children)
@kind = "Box"
+ substitution()
+ end
+
+ def initialize_component(widget : Gtk::Widget, component_storage : Transpiler::ComponentStorage)
+ id = @attributes["id"]? || ""
+ class_name = @attributes["className"]? || nil
+ horizontal_align = to_align(@attributes["horizontalAlign"]? || "")
+ vertical_align = to_align(@attributes["verticalAlign"]? || "")
+
+ box_expand = @attributes["boxExpand"]? || "false"
+ box_fill = @attributes["boxFill"]? || "false"
+ box_padding = @attributes["boxPadding"]? || "0"
+
+ if box_padding.includes?(".0")
+ box_padding = box_padding[..box_padding.size - 3]
+ end
- @attributes.map do |key, value|
- matches = value.scan(/\${(.*?)}/)
+ case @attributes["orientation"]?
+ when "vertical"
+ orientation = Gtk::Orientation::VERTICAL
+ when "horizontal"
+ orientation = Gtk::Orientation::HORIZONTAL
+ else
+ orientation = Gtk::Orientation::VERTICAL
+ end
+
+ spacing = @attributes["spacing"]? || "2"
+
+ box = Gtk::Box.new(name: id, orientation: orientation, spacing: spacing.to_i, halign: horizontal_align, valign: vertical_align)
- case matches.size
- when 0
- @attributes[key] = value
+ box.on_event_after do |_widget, event|
+ case event.event_type
+ when Gdk::EventType::MOTION_NOTIFY
+ false
else
- matches.each do |match|
- hash = match.to_h
-
- begin
- @attributes[key] = value.gsub(hash[0].not_nil!, "#{Layout::Js::Engine::INSTANCE.evaluate(hash[1].not_nil!)}")
- rescue ex : Exception
- @attributes[key] = value
- puts "An exception occured while evaluating a variable format routine: #{ex}"
- end
- end
+ did_update(@cid, event.event_type.to_s)
+ true
end
end
- if id = @attributes["id"]?
- Layout::Js::Engine::INSTANCE.evaluate(<<-JS
- const #{id}State = #{self.to_json}
- JS
- )
- end
+ containerize(widget, box, box_expand, box_fill, box_padding)
+ add_class_to_css(box, class_name)
+ component_storage.store(id, box)
+ component_storage.store(@cid, box)
+ did_mount(@cid)
+
+ box
end
def to_html : String
diff --git a/src/layout/dom/button.cr b/src/layout/dom/button.cr
index 16b0bb7..d7fd283 100644
--- a/src/layout/dom/button.cr
+++ b/src/layout/dom/button.cr
@@ -10,26 +10,59 @@ module Layout
def initialize(@attributes, @children)
@kind = "Button"
+ substitution()
+ end
+
+ def initialize_component(widget : Gtk::Widget, component_storage : Transpiler::ComponentStorage) : Gtk::Widget
+ id = @attributes["id"]? || ""
+ class_name = @attributes["className"]? || nil
+ relief = @attributes["relief"]? || nil
+ text = children.first.as(Text).data.to_s
+ on_click = @attributes["onClick"]? || ""
+
+ horizontal_align = to_align(@attributes["horizontalAlign"]? || "")
+ vertical_align = to_align(@attributes["verticalAlign"]? || "")
+
+ box_expand = @attributes["boxExpand"]? || "false"
+ box_fill = @attributes["boxFill"]? || "false"
+ box_padding = @attributes["boxPadding"]? || "0"
+
+ if box_padding.includes?(".0")
+ box_padding = box_padding[..box_padding.size - 3]
+ end
- @attributes.map do |key, value|
- matches = value.scan(/\${(.*?)}/)
+ case relief
+ when "none"
+ relief_style = Gtk::ReliefStyle::NONE
+ when "normal"
+ relief_style = Gtk::ReliefStyle::NORMAL
+ else
+ relief_style = Gtk::ReliefStyle::NORMAL
+ end
+
+ button = Gtk::Button.new(name: id, label: text, relief: relief_style, halign: horizontal_align, valign: vertical_align)
- case matches.size
- when 0
- @attributes[key] = value
+ button.on_event_after do |_widget, event|
+ case event.event_type
+ when Gdk::EventType::MOTION_NOTIFY
+ false
else
- matches.each do |match|
- hash = match.to_h
-
- begin
- @attributes[key] = value.gsub(hash[0].not_nil!, "#{Layout::Js::Engine::INSTANCE.evaluate(hash[1].not_nil!)}")
- rescue ex : Exception
- @attributes[key] = value
- puts "An exception occured while evaluating a variable format routine: #{ex}"
- end
- end
+ did_update(@cid, event.event_type.to_s)
+ true
end
end
+
+ button.on_clicked do
+ Layout::Js::Engine::INSTANCE.evaluate("#{on_click}(getElementByComponentId(\"#{@cid}\"))")
+ end
+
+ containerize(widget, button, box_expand, box_fill, box_padding)
+ add_class_to_css(button, class_name)
+ component_storage.store(id, button)
+ component_storage.store(@cid, button)
+ did_mount(@cid)
+
+ button
end
def to_html : String
diff --git a/src/layout/dom/element.cr b/src/layout/dom/element.cr
index 08b6cc5..7418670 100644
--- a/src/layout/dom/element.cr
+++ b/src/layout/dom/element.cr
@@ -7,8 +7,13 @@ module Layout
@attributes : Hash(String, String)
getter :attributes
+ getter cid : String = UUID.random.hexstring
def initialize(@kind, @attributes, @children = [] of Node)
+ substitution()
+ end
+
+ def substitution
@attributes.map do |key, value|
matches = value.scan(/\${(.*?)}/)
@@ -20,7 +25,7 @@ module Layout
hash = match.to_h
begin
- @attributes[key] = value.gsub(hash[0].not_nil!, "#{Layout::Js::Engine::INSTANCE.evaluate(hash[1].not_nil!)}")
+ @attributes[key] = value.gsub(hash[0].not_nil!, Layout::Js::Engine::INSTANCE.evaluate("__std__value_of__(#{hash[1].not_nil!})").to_s)
rescue ex : Exception
@attributes[key] = value
puts "An exception occured while evaluating a variable format routine: #{ex}"
@@ -30,37 +35,68 @@ module Layout
end
end
- def on_component_did_mount
- if class_id = @attributes["classId"]?
- Layout::Js::Engine::INSTANCE.evaluate("const #{class_id}State = #{self.to_json};")
- end
-
+ def did_mount(cid)
if function = @attributes["onComponentDidMount"]?
- if class_id = @attributes["classId"]?
- Layout::Js::Engine::INSTANCE.evaluate("#{function}(#{class_id}State)")
- else
- Layout::Js::Engine::INSTANCE.evaluate("#{function}({})")
- end
+ Layout::Js::Engine::INSTANCE.evaluate("#{function}(#{@attributes.to_json}, getElementByComponentId(\"#{cid}\"))")
end
end
- def on_component_did_update(class_id, event_type)
+ def did_update(cid, event_type)
if function = @attributes["onComponentDidUpdate"]?
- if class_id = @attributes["classId"]?
- Layout::Js::Engine::INSTANCE.evaluate("#{function}(#{class_id}State, getElementByClassId(\"#{class_id}\"), \"#{event_type}\")")
- else
- Layout::Js::Engine::INSTANCE.evaluate("#{function}({}, \"#{class_id}\", \"#{event_type}\")")
- end
+ Layout::Js::Engine::INSTANCE.evaluate("#{function}(#{@attributes.to_json}, getElementByComponentId(\"#{cid}\"), \"#{event_type}\")")
end
end
- def on_component_will_unmount
+ def will_unmount(cid)
if function = @attributes["onComponentWillUnmount"]?
- if class_id = @attributes["classId"]?
- Layout::Js::Engine::INSTANCE.evaluate("#{function}(#{class_id}State)")
- else
- Layout::Js::Engine::INSTANCE.evaluate("#{function}({})")
- end
+ Layout::Js::Engine::INSTANCE.evaluate("#{function}(#{@attributes.to_json}, getElementByComponentId(\"#{cid}\")")
+ end
+ end
+
+ private def to_align(str : String) : Gtk::Align
+ case str
+ when "fill"
+ Gtk::Align::FILL
+ when "start"
+ Gtk::Align::START
+ when "end"
+ Gtk::Align::END
+ when "center"
+ Gtk::Align::CENTER
+ when "baseline"
+ Gtk::Align::BASELINE
+ else
+ Gtk::Align::BASELINE
+ end
+ end
+
+ private def add_class_to_css(widget, class_name)
+ if class_name
+ context = widget.style_context
+ context.add_class(class_name.not_nil!)
+ end
+ end
+
+ private def to_bool(str : String) : Bool
+ if str == "true"
+ true
+ else
+ false
+ end
+ end
+
+ private def containerize(widget, component, box_expand, box_fill, box_padding)
+ case widget
+ when Gtk::Notebook
+ widget.append_page(component, nil)
+ when Gtk::Box
+ widget.pack_start(component, to_bool(box_expand), to_bool(box_fill), box_padding.to_i)
+ when Gtk::ScrolledWindow, Gtk::Frame
+ widget.add(component)
+ when Gtk::ListBox
+ widget.insert(component, 1_000_000)
+ when Gtk::ApplicationWindow
+ widget.add(component)
end
end
diff --git a/src/layout/dom/entry.cr b/src/layout/dom/entry.cr
index d98d4f2..c44b299 100644
--- a/src/layout/dom/entry.cr
+++ b/src/layout/dom/entry.cr
@@ -11,26 +11,90 @@ module Layout
def initialize(@attributes)
@kind = "TextInput"
@children = [] of Node
+ substitution()
+ end
+
+ def initialize_component(widget : Gtk::Widget, component_storage : Transpiler::ComponentStorage)
+ id = @attributes["id"]? || ""
+ class_name = @attributes["className"]? || nil
+ label = @attributes["value"]? || nil
+ placeholder = @attributes["placeholder"]? || nil
+ text_changed = @attributes["onChangeText"]? || nil
+ cut_clipboard = @attributes["onCut"]? || nil
+ copy_clipboard = @attributes["onCopy"]? || nil
+ paste_clipboard = @attributes["onPaste"]? || nil
+ on_activate = @attributes["onActivate"]? || nil
+
+ horizontal_align = to_align(@attributes["horizontalAlign"]? || "")
+ vertical_align = to_align(@attributes["verticalAlign"]? || "")
+
+ password_char = @attributes["passwordCharacter"]? || nil
+ visibility = to_bool(@attributes["isPassword"]? || "false") ? false : true
+ invisible_char = password_char.try(&.bytes.first.to_u32)
+
+ entry = Gtk::Entry.new(name: id, text: label, placeholder_text: placeholder, invisible_char: invisible_char, visibility: visibility, halign: horizontal_align, valign: vertical_align)
+
+ entry.buffer.on_inserted_text do |buffer|
+ if text_changed
+ Layout::Js::Engine::INSTANCE.evaluate("#{text_changed}(getElementByComponentId(\"#{@cid}\"), \"#{buffer.text}\")")
+ end
+ end
- @attributes.map do |key, value|
- matches = value.scan(/\${(.*?)}/)
+ entry.buffer.on_deleted_text do |buffer|
+ if text_changed
+ Layout::Js::Engine::INSTANCE.evaluate("#{text_changed}(getElementByComponentId(\"#{@cid}\"), \"#{buffer.text}\")")
+ end
+ end
- case matches.size
- when 0
- @attributes[key] = value
+ entry.on_cut_clipboard do
+ if cut_clipboard
+ Layout::Js::Engine::INSTANCE.evaluate("#{cut_clipboard}(getElementByComponentId(\"#{@cid}\"), \"#{entry.buffer.text}\")")
+ end
+ end
+
+ entry.on_copy_clipboard do
+ if copy_clipboard
+ Layout::Js::Engine::INSTANCE.evaluate("#{copy_clipboard}(getElementByComponentId(\"#{@cid}\"), \"#{entry.buffer.text}\")")
+ end
+ end
+
+ entry.on_paste_clipboard do
+ if paste_clipboard
+ Layout::Js::Engine::INSTANCE.evaluate("#{paste_clipboard}(getElementByComponentId(\"#{@cid}\"), \"#{entry.buffer.text}\")")
+ end
+ end
+
+ entry.on_activate do
+ if on_activate
+ Layout::Js::Engine::INSTANCE.evaluate("#{on_activate}(getElementByComponentId(\"#{@cid}\"), \"#{entry.buffer.text}\")")
+ end
+ end
+
+ box_expand = @attributes["boxExpand"]? || "false"
+ box_fill = @attributes["boxFill"]? || "false"
+ box_padding = @attributes["boxPadding"]? || "0"
+
+ if box_padding.includes?(".0")
+ box_padding = box_padding[..box_padding.size - 3]
+ end
+
+ entry.on_event_after do |_widget, event|
+ case event.event_type
+ when Gdk::EventType::MOTION_NOTIFY
+ false
else
- matches.each do |match|
- hash = match.to_h
-
- begin
- @attributes[key] = value.gsub(hash[0].not_nil!, "#{Layout::Js::Engine::INSTANCE.evaluate(hash[1].not_nil!)}")
- rescue ex : Exception
- @attributes[key] = value
- puts "An exception occured while evaluating a variable format routine: #{ex}"
- end
- end
+ did_update(@cid, event.event_type.to_s)
+ true
end
end
+
+ containerize(widget, entry, box_expand, box_fill, box_padding)
+ add_class_to_css(entry, class_name)
+ component_storage.store(id, entry)
+ component_storage.store(@cid, entry)
+ did_mount(@cid)
+
+ entry
end
def to_html : String
diff --git a/src/layout/dom/event_box.cr b/src/layout/dom/event_box.cr
index 0619297..f839b94 100644
--- a/src/layout/dom/event_box.cr
+++ b/src/layout/dom/event_box.cr
@@ -8,26 +8,7 @@ module Layout
def initialize(@attributes, @children)
@kind = "EventBox"
-
- @attributes.map do |key, value|
- matches = value.scan(/\${(.*?)}/)
-
- case matches.size
- when 0
- @attributes[key] = value
- else
- matches.each do |match|
- hash = match.to_h
-
- begin
- @attributes[key] = value.gsub(hash[0].not_nil!, "#{Layout::Js::Engine::INSTANCE.evaluate(hash[1].not_nil!)}")
- rescue ex : Exception
- @attributes[key] = value
- puts "An exception occured while evaluating a variable format routine: #{ex}"
- end
- end
- end
- end
+ substitution()
end
def to_html : String
diff --git a/src/layout/dom/export.cr b/src/layout/dom/export.cr
index 698b7f2..da1aef1 100644
--- a/src/layout/dom/export.cr
+++ b/src/layout/dom/export.cr
@@ -10,26 +10,7 @@ module Layout
def initialize(@attributes, @children)
@kind = "Export"
-
- @attributes.map do |key, value|
- matches = value.scan(/\${(.*?)}/)
-
- case matches.size
- when 0
- @attributes[key] = value
- else
- matches.each do |match|
- hash = match.to_h
-
- begin
- @attributes[key] = value.gsub(hash[0].not_nil!, "#{Layout::Js::Engine::INSTANCE.evaluate(hash[1].not_nil!)}")
- rescue ex : Exception
- @attributes[key] = value
- puts "An exception occured while evaluating a variable format routine: #{ex}"
- end
- end
- end
- end
+ substitution()
end
def to_html : String
diff --git a/src/layout/dom/frame.cr b/src/layout/dom/frame.cr
new file mode 100644
index 0000000..1776f57
--- /dev/null
+++ b/src/layout/dom/frame.cr
@@ -0,0 +1,59 @@
+require "./node"
+require "./element"
+
+module Layout
+ module Dom
+ class Frame < Element
+ property attributes : Hash(String, String)
+
+ def initialize(@attributes, @children)
+ @kind = "Frame"
+ substitution()
+ end
+
+ def initialize_component(widget : Gtk::Widget, component_storage : Transpiler::ComponentStorage)
+ id = @attributes["id"]? || ""
+ class_name = @attributes["className"]? || nil
+ horizontal_align = to_align(@attributes["horizontalAlign"]? || "")
+ vertical_align = to_align(@attributes["verticalAlign"]? || "")
+ value = @attributes["value"]? || ""
+ box_expand = @attributes["boxExpand"]? || "false"
+ box_fill = @attributes["boxFill"]? || "false"
+ box_padding = @attributes["boxPadding"]? || "0"
+
+ if box_padding.includes?(".0")
+ box_padding = box_padding[..box_padding.size - 3]
+ end
+
+ frame = Gtk::Frame.new(name: id, label: value, halign: horizontal_align, valign: vertical_align)
+
+ frame.on_event_after do |_widget, event|
+ case event.event_type
+ when Gdk::EventType::MOTION_NOTIFY
+ false
+ else
+ did_update(@cid, event.event_type.to_s)
+ true
+ end
+ end
+
+ containerize(widget, frame, box_expand, box_fill, box_padding)
+ add_class_to_css(frame, class_name)
+ component_storage.store(id, frame)
+ component_storage.store(@cid, frame)
+ did_mount(@cid)
+
+ frame
+ end
+
+ def to_html : String
+ attrs = attributes.map do |key, value|
+ "#{key}='#{value}'"
+ end
+
+ children_html = children.map(&.to_html.as(String)).join("")
+ "<#{kind} #{attrs.join(' ')}>#{children_html}#{kind}>"
+ end
+ end
+ end
+end
diff --git a/src/layout/dom/horizontal_separator.cr b/src/layout/dom/horizontal_separator.cr
new file mode 100644
index 0000000..9a2c104
--- /dev/null
+++ b/src/layout/dom/horizontal_separator.cr
@@ -0,0 +1,63 @@
+require "./node"
+require "./element"
+
+module Layout
+ module Dom
+ class HorizontalSeparator < Element
+ @attributes : Hash(String, String)
+
+ getter :attributes
+
+ def initialize(@attributes)
+ @kind = "HorizontalSeparator"
+ @children = [] of Node
+ substitution()
+ end
+
+ def initialize_component(widget : Gtk::Widget, component_storage : Transpiler::ComponentStorage)
+ id = @attributes["id"]? || ""
+ class_name = @attributes["className"]? || nil
+
+ horizontal_align = to_align(@attributes["horizontalAlign"]? || "")
+ vertical_align = to_align(@attributes["verticalAlign"]? || "")
+
+ horizontal_separator = Gtk::Separator.new(name: id, orientation: Gtk::Orientation::HORIZONTAL, halign: horizontal_align, valign: vertical_align)
+
+ box_expand = @attributes["boxExpand"]? || "false"
+ box_fill = @attributes["boxFill"]? || "false"
+ box_padding = @attributes["boxPadding"]? || "0"
+
+ if box_padding.includes?(".0")
+ box_padding = box_padding[..box_padding.size - 3]
+ end
+
+ horizontal_separator.on_event_after do |_widget, event|
+ case event.event_type
+ when Gdk::EventType::MOTION_NOTIFY
+ false
+ else
+ did_update(@cid, event.event_type.to_s)
+ true
+ end
+ end
+
+ containerize(widget, horizontal_separator, box_expand, box_fill, box_padding)
+ add_class_to_css(horizontal_separator, class_name)
+ component_storage.store(id, horizontal_separator)
+ component_storage.store(@cid, horizontal_separator)
+ did_mount(@cid)
+
+ horizontal_separator
+ end
+
+ def to_html : String
+ attrs = attributes.map do |key, value|
+ "#{key}='#{value}'"
+ end
+
+ children_html = children.map(&.to_html.as(String)).join("")
+ "<#{kind} #{attrs.join(' ')}>#{children_html}#{kind}>"
+ end
+ end
+ end
+end
diff --git a/src/layout/dom/image.cr b/src/layout/dom/image.cr
index ede2a55..994efce 100644
--- a/src/layout/dom/image.cr
+++ b/src/layout/dom/image.cr
@@ -11,26 +11,72 @@ module Layout
def initialize(@attributes)
@kind = "Image"
@children = [] of Node
+ substitution()
+ end
+
+ def initialize_component(widget : Gtk::Widget, component_storage : Transpiler::ComponentStorage)
+ id = @attributes["id"]? || ""
+ class_name = @attributes["className"]? || nil
+ source = @attributes["src"]? || ""
+
+ width = @attributes["width"]? || "256"
+ height = @attributes["height"]? || "256"
+
+ # preserve_aspect_ration = @attributes["preserveAspectRation"]? || "true"
+
+ if width.includes?(".0")
+ width = width[..width.size - 3]
+ end
+
+ if height.includes?(".0")
+ height = height[..height.size - 3]
+ end
- @attributes.map do |key, value|
- matches = value.scan(/\${(.*?)}/)
+ horizontal_align = to_align(@attributes["horizontalAlign"]? || "")
+ vertical_align = to_align(@attributes["verticalAlign"]? || "")
- case matches.size
- when 0
- @attributes[key] = value
+ if width && height
+ image = Gtk::Image.new(
+ name: id,
+ # TODO: Create an issue in the GTK bindings repository.
+ # pixbuf: GdkPixbuf::Pixbuf.new_from_file_at_scale(source, width.to_i, height.to_i, to_bool(preserve_aspect_ration)),
+ halign: horizontal_align,
+ valign: vertical_align
+ )
+ else
+ image = Gtk::Image.new(
+ name: id,
+ file: source,
+ halign: horizontal_align,
+ valign: vertical_align
+ )
+ end
+
+ box_expand = @attributes["boxExpand"]? || "false"
+ box_fill = @attributes["boxFill"]? || "false"
+ box_padding = @attributes["boxPadding"]? || "0"
+
+ if box_padding.includes?(".0")
+ box_padding = box_padding[..box_padding.size - 3]
+ end
+
+ image.on_event_after do |_widget, event|
+ case event.event_type
+ when Gdk::EventType::MOTION_NOTIFY
+ false
else
- matches.each do |match|
- hash = match.to_h
-
- begin
- @attributes[key] = value.gsub(hash[0].not_nil!, "#{Layout::Js::Engine::INSTANCE.evaluate(hash[1].not_nil!)}")
- rescue ex : Exception
- @attributes[key] = value
- puts "An exception occured while evaluating a variable format routine: #{ex}"
- end
- end
+ did_update(@cid, event.event_type.to_s)
+ true
end
end
+
+ containerize(widget, image, box_expand, box_fill, box_padding)
+ add_class_to_css(image, class_name)
+ component_storage.store(id, image)
+ component_storage.store(@cid, image)
+ did_mount(@cid)
+
+ image
end
def to_html : String
diff --git a/src/layout/dom/import.cr b/src/layout/dom/import.cr
index a6f0a35..636a25c 100644
--- a/src/layout/dom/import.cr
+++ b/src/layout/dom/import.cr
@@ -11,26 +11,7 @@ module Layout
def initialize(@attributes)
@kind = "Import"
@children = [] of Node
-
- @attributes.map do |key, value|
- matches = value.scan(/\${(.*?)}/)
-
- case matches.size
- when 0
- @attributes[key] = value
- else
- matches.each do |match|
- hash = match.to_h
-
- begin
- @attributes[key] = value.gsub(hash[0].not_nil!, "#{Layout::Js::Engine::INSTANCE.evaluate(hash[1].not_nil!)}")
- rescue ex : Exception
- @attributes[key] = value
- puts "An exception occured while evaluating a variable format routine: #{ex}"
- end
- end
- end
- end
+ substitution()
end
def to_html : String
diff --git a/src/layout/dom/label.cr b/src/layout/dom/label.cr
index 10af3c1..b24b39b 100644
--- a/src/layout/dom/label.cr
+++ b/src/layout/dom/label.cr
@@ -10,26 +10,42 @@ module Layout
def initialize(@attributes, @children)
@kind = "Label"
+ substitution()
+ end
+
+ def initialize_component(widget : Gtk::Widget, component_storage : Transpiler::ComponentStorage)
+ id = @attributes["id"]? || ""
+ class_name = @attributes["className"]? || nil
+ text = @children[0].as(Text).data.to_s
+ horizontal_align = to_align(@attributes["horizontalAlign"]? || "")
+ vertical_align = to_align(@attributes["verticalAlign"]? || "")
+ label = Gtk::Label.new(name: id, label: text, halign: horizontal_align, valign: vertical_align, wrap: true)
+
+ box_expand = @attributes["boxExpand"]? || "false"
+ box_fill = @attributes["boxFill"]? || "false"
+ box_padding = @attributes["boxPadding"]? || "0"
- @attributes.map do |key, value|
- matches = value.scan(/\${(.*?)}/)
+ if box_padding.includes?(".0")
+ box_padding = box_padding[..box_padding.size - 3]
+ end
- case matches.size
- when 0
- @attributes[key] = value
+ label.on_event_after do |_widget, event|
+ case event.event_type
+ when Gdk::EventType::MOTION_NOTIFY
+ false
else
- matches.each do |match|
- hash = match.to_h
-
- begin
- @attributes[key] = value.gsub(hash[0].not_nil!, "#{Layout::Js::Engine::INSTANCE.evaluate(hash[1].not_nil!)}")
- rescue ex : Exception
- @attributes[key] = value
- puts "An exception occured while evaluating a variable format routine: #{ex}"
- end
- end
+ did_update(@cid, event.event_type.to_s)
+ true
end
end
+
+ containerize(widget, label, box_expand, box_fill, box_padding)
+ add_class_to_css(label, class_name)
+ component_storage.store(id, label)
+ component_storage.store(@cid, label)
+ did_mount(@cid)
+
+ label
end
def to_html : String
diff --git a/src/layout/dom/list_box.cr b/src/layout/dom/list_box.cr
new file mode 100644
index 0000000..821e580
--- /dev/null
+++ b/src/layout/dom/list_box.cr
@@ -0,0 +1,59 @@
+require "./node"
+require "./element"
+
+module Layout
+ module Dom
+ class ListBox < Element
+ property attributes : Hash(String, String)
+
+ def initialize(@attributes, @children)
+ @kind = "ListBox"
+ substitution()
+ end
+
+ def initialize_component(widget : Gtk::Widget, component_storage : Transpiler::ComponentStorage)
+ id = @attributes["id"]? || ""
+ class_name = @attributes["className"]? || nil
+ horizontal_align = to_align(@attributes["horizontalAlign"]? || "")
+ vertical_align = to_align(@attributes["verticalAlign"]? || "")
+
+ box_expand = @attributes["boxExpand"]? || "false"
+ box_fill = @attributes["boxFill"]? || "false"
+ box_padding = @attributes["boxPadding"]? || "0"
+
+ if box_padding.includes?(".0")
+ box_padding = box_padding[..box_padding.size - 3]
+ end
+
+ list_box = Gtk::ListBox.new(name: id, halign: horizontal_align, valign: vertical_align)
+
+ list_box.on_event_after do |_widget, event|
+ case event.event_type
+ when Gdk::EventType::MOTION_NOTIFY
+ false
+ else
+ did_update(@cid, event.event_type.to_s)
+ true
+ end
+ end
+
+ containerize(widget, list_box, box_expand, box_fill, box_padding)
+ add_class_to_css(list_box, class_name)
+ component_storage.store(id, list_box)
+ component_storage.store(@cid, list_box)
+ did_mount(@cid)
+
+ list_box
+ end
+
+ def to_html : String
+ attrs = attributes.map do |key, value|
+ "#{key}='#{value}'"
+ end
+
+ children_html = children.map(&.to_html.as(String)).join("")
+ "<#{kind} #{attrs.join(' ')}>#{children_html}#{kind}>"
+ end
+ end
+ end
+end
diff --git a/src/layout/dom/progress_bar.cr b/src/layout/dom/progress_bar.cr
new file mode 100644
index 0000000..085d3c1
--- /dev/null
+++ b/src/layout/dom/progress_bar.cr
@@ -0,0 +1,27 @@
+require "./node"
+require "./element"
+
+module Layout
+ module Dom
+ class ProgressBar < Element
+ @attributes : Hash(String, String)
+
+ getter :attributes
+
+ def initialize(@attributes)
+ @kind = "ProgressBar"
+ @children = [] of Node
+ substitution()
+ end
+
+ def to_html : String
+ attrs = attributes.map do |key, value|
+ "#{key}='#{value}'"
+ end
+
+ children_html = children.map(&.to_html.as(String)).join("")
+ "<#{kind} #{attrs.join(' ')}>#{children_html}#{kind}>"
+ end
+ end
+ end
+end
diff --git a/src/layout/dom/script.cr b/src/layout/dom/script.cr
index 23e01d8..9351ff1 100644
--- a/src/layout/dom/script.cr
+++ b/src/layout/dom/script.cr
@@ -11,35 +11,29 @@ module Layout
def initialize(@attributes, @children)
@kind = "Script"
- @attributes.map do |key, value|
- matches = value.scan(/\${(.*?)}/)
-
- case matches.size
- when 0
- @attributes[key] = value
- else
- matches.each do |match|
- hash = match.to_h
+ if source_file = @attributes["src"]?
+ fp = File.open(source_file).gets_to_end
+ Layout::Js::Engine::INSTANCE.evaluate(fp)
- begin
- @attributes[key] = value.gsub(hash[0].not_nil!, "#{Layout::Js::Engine::INSTANCE.evaluate(hash[1].not_nil!)}")
- rescue ex : Exception
- @attributes[key] = value
- puts "An exception occured while evaluating a variable format routine: #{ex}"
+ if @children.size != 0
+ @children.each do |child|
+ case child
+ when Layout::Dom::Text
+ begin
+ Layout::Js::Engine::INSTANCE.evaluate(child.data)
+ rescue exception
+ pp exception
+ Layout::Js::Engine::INSTANCE.runtime.context.dump!
+ end
end
end
end
- end
-
- if source_file = @attributes["src"]?
- fp = File.open(source_file).gets_to_end
- Layout::Js::Engine::INSTANCE.evaluate(fp)
else
@children.each do |child|
case child
when Layout::Dom::Text
begin
- Layout::Js::Engine::INSTANCE.evaluate(child.data.strip)
+ Layout::Js::Engine::INSTANCE.evaluate(child.data)
rescue exception
pp exception
Layout::Js::Engine::INSTANCE.runtime.context.dump!
@@ -47,6 +41,8 @@ module Layout
end
end
end
+
+ substitution()
end
def to_html : String
diff --git a/src/layout/dom/scrolled_window.cr b/src/layout/dom/scrolled_window.cr
new file mode 100644
index 0000000..3ce0c34
--- /dev/null
+++ b/src/layout/dom/scrolled_window.cr
@@ -0,0 +1,58 @@
+require "./node"
+require "./element"
+
+module Layout
+ module Dom
+ class ScrolledWindow < Element
+ property attributes : Hash(String, String)
+
+ def initialize(@attributes, @children)
+ @kind = "ScrolledWindow"
+ substitution()
+ end
+
+ def initialize_component(widget : Gtk::Widget, component_storage : Transpiler::ComponentStorage)
+ id = @attributes["id"]? || ""
+ class_name = @attributes["className"]? || nil
+ horizontal_align = to_align(@attributes["horizontalAlign"]? || "")
+ vertical_align = to_align(@attributes["verticalAlign"]? || "")
+ box_expand = @attributes["boxExpand"]? || "false"
+ box_fill = @attributes["boxFill"]? || "false"
+ box_padding = @attributes["boxPadding"]? || "0"
+
+ if box_padding.includes?(".0")
+ box_padding = box_padding[..box_padding.size - 3]
+ end
+
+ scrolled_window = Gtk::ScrolledWindow.new(name: id, halign: horizontal_align, valign: vertical_align)
+
+ scrolled_window.on_event_after do |_widget, event|
+ case event.event_type
+ when Gdk::EventType::MOTION_NOTIFY
+ false
+ else
+ did_update(@cid, event.event_type.to_s)
+ true
+ end
+ end
+
+ containerize(widget, scrolled_window, box_expand, box_fill, box_padding)
+ add_class_to_css(scrolled_window, class_name)
+ component_storage.store(id, scrolled_window)
+ component_storage.store(@cid, scrolled_window)
+ did_mount(@cid)
+
+ scrolled_window
+ end
+
+ def to_html : String
+ attrs = attributes.map do |key, value|
+ "#{key}='#{value}'"
+ end
+
+ children_html = children.map(&.to_html.as(String)).join("")
+ "<#{kind} #{attrs.join(' ')}>#{children_html}#{kind}>"
+ end
+ end
+ end
+end
diff --git a/src/layout/dom/spinner.cr b/src/layout/dom/spinner.cr
new file mode 100644
index 0000000..6f8638c
--- /dev/null
+++ b/src/layout/dom/spinner.cr
@@ -0,0 +1,27 @@
+require "./node"
+require "./element"
+
+module Layout
+ module Dom
+ class Spinner < Element
+ @attributes : Hash(String, String)
+
+ getter :attributes
+
+ def initialize(@attributes)
+ @kind = "Spinner"
+ @children = [] of Node
+ substitution()
+ end
+
+ def to_html : String
+ attrs = attributes.map do |key, value|
+ "#{key}='#{value}'"
+ end
+
+ children_html = children.map(&.to_html.as(String)).join("")
+ "<#{kind} #{attrs.join(' ')}>#{children_html}#{kind}>"
+ end
+ end
+ end
+end
diff --git a/src/layout/dom/style_sheet.cr b/src/layout/dom/style_sheet.cr
index 402ade6..9e35107 100644
--- a/src/layout/dom/style_sheet.cr
+++ b/src/layout/dom/style_sheet.cr
@@ -11,26 +11,7 @@ module Layout
def initialize(@attributes)
@kind = "StyleSheet"
@children = [] of Node
-
- @attributes.map do |key, value|
- matches = value.scan(/\${(.*?)}/)
-
- case matches.size
- when 0
- @attributes[key] = value
- else
- matches.each do |match|
- hash = match.to_h
-
- begin
- @attributes[key] = value.gsub(hash[0].not_nil!, "#{Layout::Js::Engine::INSTANCE.evaluate(hash[1].not_nil!)}")
- rescue ex : Exception
- @attributes[key] = value
- puts "An exception occured while evaluating a variable format routine: #{ex}"
- end
- end
- end
- end
+ substitution()
end
def to_html : String
diff --git a/src/layout/dom/switch.cr b/src/layout/dom/switch.cr
index f7f1daa..12ffd7e 100644
--- a/src/layout/dom/switch.cr
+++ b/src/layout/dom/switch.cr
@@ -11,26 +11,54 @@ module Layout
def initialize(@attributes)
@kind = "Switch"
@children = [] of Node
+ substitution()
+ end
+
+ def initialize_component(widget : Gtk::Widget, component_storage : Transpiler::ComponentStorage)
+ id = @attributes["id"]? || ""
+ class_name = @attributes["className"]? || nil
+
+ horizontal_align = to_align(@attributes["horizontalAlign"]? || "")
+ vertical_align = to_align(@attributes["verticalAlign"]? || "")
+ value = to_bool(@attributes["value"]? || "false")
+
+ switch = Gtk::Switch.new(name: id, halign: horizontal_align, valign: vertical_align, state: value)
- @attributes.map do |key, value|
- matches = value.scan(/\${(.*?)}/)
+ box_expand = @attributes["boxExpand"]? || "false"
+ box_fill = @attributes["boxFill"]? || "false"
+ box_padding = @attributes["boxPadding"]? || "0"
+
+ value_change = @attributes["onValueChange"]? || nil
+
+ if box_padding.includes?(".0")
+ box_padding = box_padding[..box_padding.size - 3]
+ end
- case matches.size
- when 0
- @attributes[key] = value
+ switch.on_state_set do
+ if value_change
+ Layout::Js::Engine::INSTANCE.evaluate("#{value_change}(getElementByComponentId(\"#{@cid}\"), #{switch.active})")
+ end
+
+ true
+ end
+
+ switch.on_event_after do |_widget, event|
+ case event.event_type
+ when Gdk::EventType::MOTION_NOTIFY
+ false
else
- matches.each do |match|
- hash = match.to_h
-
- begin
- @attributes[key] = value.gsub(hash[0].not_nil!, "#{Layout::Js::Engine::INSTANCE.evaluate(hash[1].not_nil!)}")
- rescue ex : Exception
- @attributes[key] = value
- puts "An exception occured while evaluating a variable format routine: #{ex}"
- end
- end
+ did_update(@cid, event.event_type.to_s)
+ true
end
end
+
+ containerize(widget, switch, box_expand, box_fill, box_padding)
+ add_class_to_css(switch, class_name)
+ component_storage.store(id, switch)
+ component_storage.store(@cid, switch)
+ did_mount(@cid)
+
+ switch
end
def to_html : String
diff --git a/src/layout/dom/tab.cr b/src/layout/dom/tab.cr
index 2bb5766..189ef07 100644
--- a/src/layout/dom/tab.cr
+++ b/src/layout/dom/tab.cr
@@ -8,26 +8,42 @@ module Layout
def initialize(@attributes, @children)
@kind = "Tab"
+ substitution()
+ end
+
+ def initialize_component(widget : Gtk::Widget, component_storage : Transpiler::ComponentStorage)
+ id = @attributes["id"]? || ""
+ class_name = @attributes["className"]? || nil
+ horizontal_align = to_align(@attributes["horizontalAlign"]? || "")
+ vertical_align = to_align(@attributes["verticalAlign"]? || "")
- @attributes.map do |key, value|
- matches = value.scan(/\${(.*?)}/)
+ tab = Gtk::Notebook.new(name: id, halign: horizontal_align, valign: vertical_align)
- case matches.size
- when 0
- @attributes[key] = value
+ box_expand = @attributes["boxExpand"]? || "false"
+ box_fill = @attributes["boxFill"]? || "false"
+ box_padding = @attributes["boxPadding"]? || "0"
+
+ if box_padding.includes?(".0")
+ box_padding = box_padding[..box_padding.size - 3]
+ end
+
+ tab.on_event_after do |_widget, event|
+ case event.event_type
+ when Gdk::EventType::MOTION_NOTIFY
+ false
else
- matches.each do |match|
- hash = match.to_h
-
- begin
- @attributes[key] = value.gsub(hash[0].not_nil!, "#{Layout::Js::Engine::INSTANCE.evaluate(hash[1].not_nil!)}")
- rescue ex : Exception
- @attributes[key] = value
- puts "An exception occured while evaluating a variable format routine: #{ex}"
- end
- end
+ did_update(@cid, event.event_type.to_s)
+ true
end
end
+
+ containerize(widget, tab, box_expand, box_fill, box_padding)
+ add_class_to_css(tab, class_name)
+ component_storage.store(id, tab)
+ component_storage.store(@cid, tab)
+ did_mount(@cid)
+
+ tab
end
def to_html : String
diff --git a/src/layout/dom/text.cr b/src/layout/dom/text.cr
index 6c4333d..792795b 100644
--- a/src/layout/dom/text.cr
+++ b/src/layout/dom/text.cr
@@ -20,7 +20,7 @@ module Layout
hash = match.to_h
begin
- @data = data.gsub(hash[0].not_nil!, "#{Layout::Js::Engine::INSTANCE.evaluate(hash[1].not_nil!)}")
+ @data = data.gsub(hash[0].not_nil!, Layout::Js::Engine::INSTANCE.evaluate("__std__value_of__(#{hash[1].not_nil!})").to_s)
rescue ex : Exception
@data = data
puts "An exception occured while evaluating a variable format routine: #{ex}"
diff --git a/src/layout/dom/text_view.cr b/src/layout/dom/text_view.cr
new file mode 100644
index 0000000..fb6c86a
--- /dev/null
+++ b/src/layout/dom/text_view.cr
@@ -0,0 +1,62 @@
+require "./node"
+require "./element"
+
+module Layout
+ module Dom
+ class TextView < Element
+ @attributes : Hash(String, String)
+
+ getter :attributes
+
+ def initialize(@attributes, @children)
+ @kind = "TextView"
+ substitution()
+ end
+
+ def initialize_component(widget : Gtk::Widget, component_storage : Transpiler::ComponentStorage)
+ id = @attributes["id"]? || ""
+ class_name = @attributes["className"]? || nil
+ text = @children[0].as(Text).data.to_s
+ horizontal_align = to_align(@attributes["horizontalAlign"]? || "")
+ vertical_align = to_align(@attributes["verticalAlign"]? || "")
+ text_view = Gtk::TextView.new(name: id, halign: horizontal_align, valign: vertical_align)
+ text_view.buffer.set_text(text, text.size)
+
+ box_expand = @attributes["boxExpand"]? || "false"
+ box_fill = @attributes["boxFill"]? || "false"
+ box_padding = @attributes["boxPadding"]? || "0"
+
+ if box_padding.includes?(".0")
+ box_padding = box_padding[..box_padding.size - 3]
+ end
+
+ text_view.on_event_after do |_widget, event|
+ case event.event_type
+ when Gdk::EventType::MOTION_NOTIFY
+ false
+ else
+ did_update(@cid, event.event_type.to_s)
+ true
+ end
+ end
+
+ containerize(widget, text_view, box_expand, box_fill, box_padding)
+ add_class_to_css(text_view, class_name)
+ component_storage.store(id, text_view)
+ component_storage.store(@cid, text_view)
+ did_mount(@cid)
+
+ text_view
+ end
+
+ def to_html : String
+ attrs = attributes.map do |key, value|
+ "#{key}='#{value}'"
+ end
+
+ children_html = children.map(&.to_html.as(String)).join("")
+ "<#{kind} #{attrs.join(' ')}>#{children_html}#{kind}>"
+ end
+ end
+ end
+end
diff --git a/src/layout/dom/vertical_separator.cr b/src/layout/dom/vertical_separator.cr
new file mode 100644
index 0000000..5a899c7
--- /dev/null
+++ b/src/layout/dom/vertical_separator.cr
@@ -0,0 +1,64 @@
+require "./node"
+require "./element"
+
+module Layout
+ module Dom
+ class VerticalSeparator < Element
+ @attributes : Hash(String, String)
+
+ getter :attributes
+
+ def initialize(@attributes)
+ @kind = "VerticalSeparator"
+ @children = [] of Node
+ substitution()
+ end
+
+ def initialize_component(widget : Gtk::Widget, component_storage : Transpiler::ComponentStorage)
+ id = @attributes["id"]? || ""
+ class_name = @attributes["className"]? || nil
+
+ horizontal_align = to_align(@attributes["horizontalAlign"]? || "")
+ vertical_align = to_align(@attributes["verticalAlign"]? || "")
+
+ vertical_separator = Gtk::Separator.new(name: id, orientation: Gtk::Orientation::VERTICAL, halign: horizontal_align, valign: vertical_align)
+
+ box_expand = @attributes["boxExpand"]? || "false"
+ box_fill = @attributes["boxFill"]? || "false"
+ box_padding = @attributes["boxPadding"]? || "0"
+
+ if box_padding.includes?(".0")
+ box_padding = box_padding[..box_padding.size - 3]
+ end
+
+ vertical_separator.on_event_after do |_widget, event|
+ case event.event_type
+ when Gdk::EventType::MOTION_NOTIFY
+ false
+ else
+ did_update(@cid, event.event_type.to_s)
+ true
+ end
+ end
+
+ containerize(widget, vertical_separator, box_expand, box_fill, box_padding)
+
+ add_class_to_css(vertical_separator, class_name)
+ component_storage.store(id, vertical_separator)
+ component_storage.store(@cid, vertical_separator)
+ did_mount(@cid)
+
+ vertical_separator
+ end
+
+ def to_html : String
+ attrs = attributes.map do |key, value|
+ "#{key}='#{value}'"
+ end
+
+ children_html = children.map(&.to_html.as(String)).join("")
+ "<#{kind} #{attrs.join(' ')}>#{children_html}#{kind}>"
+ end
+ end
+ end
+end
diff --git a/src/layout/dom/window.cr b/src/layout/dom/window.cr
index 16d9fc1..ab4953f 100644
--- a/src/layout/dom/window.cr
+++ b/src/layout/dom/window.cr
@@ -10,26 +10,38 @@ module Layout
def initialize(@attributes, @children)
@kind = "Window"
+ substitution()
+ end
+
+ def initialize_component(widget : Gtk::Application, component_storage : Transpiler::ComponentStorage)
+ id = @attributes["id"]? || ""
+ class_name = @attributes["className"]? || nil
+ title = @attributes["title"]? || "Untitled"
+ width = @attributes["width"]? || "800"
+ height = @attributes["height"]? || "600"
+
+ if width.includes?(".0")
+ width = width[..width.size - 3]
+ end
- @attributes.map do |key, value|
- matches = value.scan(/\${(.*?)}/)
-
- case matches.size
- when 0
- @attributes[key] = value
- else
- matches.each do |match|
- hash = match.to_h
-
- begin
- @attributes[key] = value.gsub(hash[0].not_nil!, "#{Layout::Js::Engine::INSTANCE.evaluate(hash[1].not_nil!)}")
- rescue ex : Exception
- @attributes[key] = value
- puts "An exception occured while evaluating a variable format routine: #{ex}"
- end
- end
- end
+ if height.includes?(".0")
+ height = height[..height.size - 3]
end
+
+ window = Gtk::ApplicationWindow.new(
+ name: id,
+ application: widget,
+ title: title,
+ default_width: width.to_i,
+ default_height: height.to_i
+ )
+
+ window.try(&.connect "destroy", &->exit)
+ window.position = Gtk::WindowPosition::CENTER_ALWAYS
+
+ add_class_to_css(window, class_name)
+ component_storage.store(@cid, window)
+ window
end
def to_html : String
diff --git a/src/layout/exceptions/component_not_found_exception.cr b/src/layout/exceptions/component_not_found_exception.cr
new file mode 100644
index 0000000..7b2f3f3
--- /dev/null
+++ b/src/layout/exceptions/component_not_found_exception.cr
@@ -0,0 +1,9 @@
+module Layout
+ module Exceptions
+ class ComponentNotFoundException < Exception
+ def initialize(cid)
+ super("Component #{cid} was not found!")
+ end
+ end
+ end
+end
diff --git a/src/layout/exceptions/invalid_component_exception.cr b/src/layout/exceptions/invalid_component_exception.cr
new file mode 100644
index 0000000..79c42d1
--- /dev/null
+++ b/src/layout/exceptions/invalid_component_exception.cr
@@ -0,0 +1,19 @@
+require "levenshtein"
+
+module Layout
+ module Exceptions
+ class InvalidComponentException < Exception
+ def initialize(tag_name : String, position : Int32)
+ tag_names = {{ Dom.constants.map(&.stringify) }}
+ match = Levenshtein.find(tag_name, tag_names)
+
+ case match
+ when String
+ super("Component `\033[1;33m#{tag_name}\033[0m` defined at `\033[1;37m#{position}\033[0m` is not valid!\n\n Maybe you wanted to define the tag as: `\033[0;32m#{match}\033[0m` instead?\n\n")
+ when Nil
+ super("Component `\033[1;33m#{tag_name}\033[0m` defined at `\033[1;37m#{position}\033[0m` is not valid!")
+ end
+ end
+ end
+ end
+end
diff --git a/src/layout/exceptions/invalid_element_exception.cr b/src/layout/exceptions/invalid_element_exception.cr
deleted file mode 100644
index 11ec73f..0000000
--- a/src/layout/exceptions/invalid_element_exception.cr
+++ /dev/null
@@ -1,9 +0,0 @@
-module Layout
- module Exceptions
- class InvalidElementException < Exception
- def initialize(tag_name : String, position : Int32)
- super("Element `\033[1;33m#{tag_name}\033[0m` defined at `\033[1;37m#{position}\033[0m` is not valid!")
- end
- end
- end
-end
diff --git a/src/layout/exceptions/parser_exception.cr b/src/layout/exceptions/parser_exception.cr
index 69ae589..e082478 100644
--- a/src/layout/exceptions/parser_exception.cr
+++ b/src/layout/exceptions/parser_exception.cr
@@ -2,7 +2,7 @@ module Layout
module Exceptions
class ParserException < Exception
def initialize(position : Int32)
- super("Failed to parse a character at `\033[1;37m#{position}\033[0m`.")
+ super("Failed to parse a character at `\033[1;37m#{position}\033[0m`!")
end
end
end
diff --git a/src/layout/ext/runtime.cr b/src/layout/ext/runtime.cr
index 920b4c5..1c4db1e 100644
--- a/src/layout/ext/runtime.cr
+++ b/src/layout/ext/runtime.cr
@@ -1,5 +1,5 @@
module Duktape
class Runtime
- property context : Duktape::Sandbox | Duktape::Context
+ property context : Duktape::Context
end
end
diff --git a/src/layout/js/engine.cr b/src/layout/js/engine.cr
index 706bba9..d7f0fb8 100644
--- a/src/layout/js/engine.cr
+++ b/src/layout/js/engine.cr
@@ -21,7 +21,6 @@ module Layout
def initialize
@runtime = Duktape::Runtime.new
- # ameba:disable Lint/UselessAssign
context = @runtime.context
misc()
@@ -31,7 +30,7 @@ module Layout
fs()
system()
- context.eval_string! <<-JS
+ context.eval! <<-JS
String.prototype.format = function() {
var formatted = this;
for( var arg in arguments ) {
@@ -42,18 +41,17 @@ module Layout
JS
end
- def lazy_evaluate(js : String)
- is_lazy = true
- loop do
- if !is_lazy
- @runtime.context.eval(js)
- break
- end
- end
- end
-
def evaluate(js : String)
- @runtime.context.eval!(js)
+ begin
+ return_value = @runtime.eval(js)
+ return_value
+ rescue exception
+ puts "Execution failed during:"
+ puts "\033[0;33m#{Beautify.js(js)}\033[0m"
+
+ puts "Exception:"
+ puts exception
+ end
end
end
end
diff --git a/src/layout/js/std/fs.cr b/src/layout/js/std/fs.cr
index 3d0bbb9..d716853 100644
--- a/src/layout/js/std/fs.cr
+++ b/src/layout/js/std/fs.cr
@@ -36,7 +36,7 @@ module Layout
sbx.call_success
end
- context.eval_string! <<-JS
+ context.eval! <<-JS
const fs = {
readFile : function (filePath) {
return __std__read_file__(filePath);
diff --git a/src/layout/js/std/misc.cr b/src/layout/js/std/misc.cr
index 240bb51..da235b6 100644
--- a/src/layout/js/std/misc.cr
+++ b/src/layout/js/std/misc.cr
@@ -10,10 +10,14 @@ module Layout
sbx.call_success
end
- context.eval_string! <<-JS
+ context.eval! <<-JS
function print(args) {
__std__puts__(JSON.stringify(args));
}
+
+ function __std__value_of__(value) {
+ return value;
+ }
JS
end
end
diff --git a/src/layout/js/std/net.cr b/src/layout/js/std/net.cr
index 5b1fa37..3d282a8 100644
--- a/src/layout/js/std/net.cr
+++ b/src/layout/js/std/net.cr
@@ -13,7 +13,7 @@ module Layout
response = HTTP::Client.exec(method, path)
sbx.push_string response.body
-
+
sbx.call_success
end
end
diff --git a/src/layout/js/std/process.cr b/src/layout/js/std/process.cr
index 004ff9f..abfdb9c 100644
--- a/src/layout/js/std/process.cr
+++ b/src/layout/js/std/process.cr
@@ -10,7 +10,7 @@ module Layout
sbx.call_success
end
- context.eval_string! <<-JS
+ context.eval! <<-JS
const process = {
exit : function (exitCode) {
__std__exit__(exitCode);
diff --git a/src/layout/js/std/system.cr b/src/layout/js/std/system.cr
index e4ae2a4..551a1cd 100644
--- a/src/layout/js/std/system.cr
+++ b/src/layout/js/std/system.cr
@@ -15,7 +15,7 @@ module Layout
sbx.call_success
end
- context.eval_string! <<-JS
+ context.eval! <<-JS
const system = {
getCpuCount : function () {
return __std__cpu_count__();
diff --git a/src/layout/parser/tokenizer.cr b/src/layout/parser/tokenizer.cr
index 9889061..ad4a95a 100644
--- a/src/layout/parser/tokenizer.cr
+++ b/src/layout/parser/tokenizer.cr
@@ -119,14 +119,18 @@ module Layout
nil
when "StyleSheet"
Layout::Dom::StyleSheet.new(attrs)
- when "Script"
- Layout::Dom::Script.new(attrs, children)
-
- nil
when "TextInput"
Layout::Dom::TextInput.new(attrs)
+ when "Spinner"
+ Layout::Dom::Spinner.new(attrs)
+ when "ProgressBar"
+ Layout::Dom::ProgressBar.new(attrs)
when "Image"
Layout::Dom::Image.new(attrs)
+ when "VerticalSeparator"
+ Layout::Dom::VerticalSeparator.new(attrs)
+ when "HorizontalSeparator"
+ Layout::Dom::HorizontalSeparator.new(attrs)
when "Switch"
Layout::Dom::Switch.new(attrs)
else
@@ -142,12 +146,10 @@ module Layout
element.children[1].children.push(element.children.first)
child = element.children[1].as(Layout::Dom::Element)
child.attributes.merge!(attrs)
- child.on_component_did_mount
child
else
child = element.children.first.as(Layout::Dom::Element)
child.attributes.merge!(attrs)
- child.on_component_did_mount
child
end
else
@@ -163,7 +165,7 @@ module Layout
end
end
else
- raise Exceptions::InvalidElementException.new(tag_name, @position)
+ raise Exceptions::InvalidComponentException.new(tag_name, @position)
end
end
else
@@ -176,14 +178,19 @@ module Layout
case tag_name
when "Script"
Layout::Dom::Script.new(attrs, children)
-
nil
when "Application"
Layout::Dom::Application.new(attrs, children)
when "Window"
Layout::Dom::Window.new(attrs, children)
+ when "Frame"
+ Layout::Dom::Frame.new(attrs, children)
when "Box"
Layout::Dom::Box.new(attrs, children)
+ when "ListBox"
+ Layout::Dom::ListBox.new(attrs, children)
+ when "ScrolledWindow"
+ Layout::Dom::ScrolledWindow.new(attrs, children)
when "Tab"
Layout::Dom::Tab.new(attrs, children)
when "EventBox"
@@ -192,6 +199,8 @@ module Layout
Layout::Dom::Button.new(attrs, children)
when "Label"
Layout::Dom::Label.new(attrs, children)
+ when "TextView"
+ Layout::Dom::TextView.new(attrs, children)
when "Export"
Layout::Dom::Export.new(attrs, children)
else
@@ -199,10 +208,9 @@ module Layout
child = custom_components[tag_name].as(Layout::Dom::Element)
child.attributes.merge!(attrs)
child.children.concat(children)
- child.on_component_did_mount
child
else
- raise Exceptions::InvalidElementException.new(tag_name, @position)
+ raise Exceptions::InvalidComponentException.new(tag_name, @position)
end
end
end
diff --git a/src/layout/transpiler/builder.cr b/src/layout/transpiler/builder.cr
index 1d05090..4c517cf 100644
--- a/src/layout/transpiler/builder.cr
+++ b/src/layout/transpiler/builder.cr
@@ -7,10 +7,13 @@ module Layout
class Builder
property application : Gtk::Application?
- property window : Gtk::ApplicationWindow?
- property elements = {} of String => Pointer(LibGtk::Widget)
- property components = {} of String => Pointer(LibGtk::Widget)
+ property component_storage : ComponentStorage
+ def initialize
+ @component_storage = ComponentStorage.new
+ end
+
+ # Create the main application, initialize the JavaScript context and build the components.
def build_from_document(document)
File.open(document) do |fd|
structure = Layout::Parser.parse(fd.gets_to_end)
@@ -22,24 +25,20 @@ module Layout
)
@application.try(&.on_activate do
- get_element_by_class_id_proc = ->(class_id : String) {
- @elements[class_id].as(Pointer(LibGtk::Widget))
- }
-
- get_element_by_id_proc = ->(id : String) {
- @components[id].as(Pointer(LibGtk::Widget))
+ get_element_by_component_id_proc = ->(component_id : String) {
+ @component_storage.retrieve(component_id)
}
context = Layout::Js::Engine::INSTANCE.runtime.context
context.push_heap_stash
- context.push_pointer(::Box.box(get_element_by_class_id_proc))
- context.put_prop_string(-2, "getElementByClassIdClosure")
+ context.push_pointer(::Box.box(get_element_by_component_id_proc))
+ context.put_prop_string(-2, "getElementByComponentId")
- context.push_global_proc("getElementByClassId", 1) do |ptr|
+ context.push_global_proc("getElementByComponentId", 1) do |ptr|
env = Duktape::Sandbox.new(ptr)
env.push_heap_stash
- env.get_prop_string(-1, "getElementByClassIdClosure")
+ env.get_prop_string(-1, "getElementByComponentId")
function = ::Box(Proc(String, Pointer(LibGtk::Widget))).unbox(env.get_pointer(-1))
component_id = env.get_string(0).not_nil!
pointer = function.call(component_id)
@@ -92,8 +91,8 @@ module Layout
idx = env.push_object
- env.push_proc(1) do |ptr|
- sbx = Duktape::Sandbox.new(ptr)
+ env.push_proc(1) do |proc_ptr|
+ sbx = Duktape::Sandbox.new(proc_ptr)
sbx.push_heap_stash
sbx.get_prop_string(-1, "setOpacityClosure")
proc = ::Box(Proc(Float64, Nil)).unbox(sbx.get_pointer(-1))
@@ -104,8 +103,8 @@ module Layout
env.put_prop_string(-2, "setOpacity")
- env.push_proc(1) do |ptr|
- sbx = Duktape::Sandbox.new(ptr)
+ env.push_proc(1) do |proc_ptr|
+ sbx = Duktape::Sandbox.new(proc_ptr)
sbx.push_heap_stash
sbx.get_prop_string(-1, "setVisibilityClosure")
proc = ::Box(Proc(Bool, Nil)).unbox(sbx.get_pointer(-1))
@@ -116,8 +115,8 @@ module Layout
env.put_prop_string(-2, "setVisible")
- env.push_proc(1) do |ptr|
- sbx = Duktape::Sandbox.new(ptr)
+ env.push_proc(1) do |proc_ptr|
+ sbx = Duktape::Sandbox.new(proc_ptr)
sbx.push_heap_stash
sbx.get_prop_string(-1, "setTextClosure")
proc = ::Box(Proc(String, Nil)).unbox(sbx.get_pointer(-1))
@@ -128,8 +127,8 @@ module Layout
env.put_prop_string(-2, "setText")
- env.push_proc(1) do |ptr|
- sbx = Duktape::Sandbox.new(ptr)
+ env.push_proc(1) do |proc_ptr|
+ sbx = Duktape::Sandbox.new(proc_ptr)
sbx.push_heap_stash
sbx.get_prop_string(-1, "getTextClosure")
proc = ::Box(Proc(String)).unbox(sbx.get_pointer(-1))
@@ -139,8 +138,8 @@ module Layout
env.put_prop_string(-2, "getText")
- env.push_proc(4) do |ptr|
- sbx = Duktape::Sandbox.new(ptr)
+ env.push_proc(4) do |proc_ptr|
+ sbx = Duktape::Sandbox.new(proc_ptr)
sbx.push_heap_stash
sbx.get_prop_string(-1, "setForegroundColorClosure")
proc = ::Box(Proc(Float64, Float64, Float64, Float64, Nil)).unbox(sbx.get_pointer(-1))
@@ -156,8 +155,8 @@ module Layout
env.put_prop_string(-2, "setForegroundColor")
- env.push_proc(4) do |ptr|
- sbx = Duktape::Sandbox.new(ptr)
+ env.push_proc(4) do |proc_ptr|
+ sbx = Duktape::Sandbox.new(proc_ptr)
sbx.push_heap_stash
sbx.get_prop_string(-1, "setBackgroundColorClosure")
proc = ::Box(Proc(Float64, Float64, Float64, Float64, Nil)).unbox(sbx.get_pointer(-1))
@@ -181,212 +180,34 @@ module Layout
env.call_success
end
- context.push_heap_stash
- context.push_pointer(::Box.box(get_element_by_id_proc))
- context.put_prop_string(-2, "getElementByIdClosure")
-
- context.push_global_proc("getElementById", 1) do |ptr|
- env = Duktape::Sandbox.new(ptr)
- env.push_heap_stash
- env.get_prop_string(-1, "getElementByIdClosure")
- function = ::Box(Proc(String, Pointer(LibGtk::Widget))).unbox(env.get_pointer(-1))
- component_id = env.get_string(0).not_nil!
- pointer = function.call(component_id)
- widget = pointer.as(Gtk::Widget)
-
- set_opacity_proc = ->(opacity : Float64) { widget.opacity = opacity }
- set_visibility_proc = ->(visible : Bool) { widget.visible = visible }
- set_foreground_color_proc = ->(r : Float64, g : Float64, b : Float64, a : Float64) { widget.override_color(Gtk::StateFlags::NORMAL, Gdk::RGBA.new(r, g, b, a)) }
- set_background_color_proc = ->(r : Float64, g : Float64, b : Float64, a : Float64) { widget.override_background_color(Gtk::StateFlags::NORMAL, Gdk::RGBA.new(r, g, b, a)) }
-
- set_text_proc = ->(text : String) {
- begin
- widget.as(Gtk::Entry).text = text
- rescue
- widget.as(Gtk::Label).text = text
- end
- }
-
- get_text_proc = ->{
- begin
- widget.as(Gtk::Entry).text
- rescue
- widget.as(Gtk::Label).text
- end
- }
-
- env.push_heap_stash
- env.push_pointer(::Box.box(set_opacity_proc))
- env.put_prop_string(-2, "setOpacityClosure")
-
- env.push_heap_stash
- env.push_pointer(::Box.box(set_visibility_proc))
- env.put_prop_string(-2, "setVisibilityClosure")
-
- env.push_heap_stash
- env.push_pointer(::Box.box(set_text_proc))
- env.put_prop_string(-2, "setTextClosure")
-
- env.push_heap_stash
- env.push_pointer(::Box.box(get_text_proc))
- env.put_prop_string(-2, "getTextClosure")
-
- env.push_heap_stash
- env.push_pointer(::Box.box(set_foreground_color_proc))
- env.put_prop_string(-2, "setForegroundColorClosure")
-
- env.push_heap_stash
- env.push_pointer(::Box.box(set_background_color_proc))
- env.put_prop_string(-2, "setBackgroundColorClosure")
-
- idx = env.push_object
-
- env.push_proc(1) do |ptr|
- sbx = Duktape::Sandbox.new(ptr)
- sbx.push_heap_stash
- sbx.get_prop_string(-1, "setOpacityClosure")
- proc = ::Box(Proc(Float64, Nil)).unbox(sbx.get_pointer(-1))
- opacity = sbx.get_number(0).not_nil!.as(Float64)
- proc.call(opacity)
- sbx.call_success
- end
-
- env.put_prop_string(-2, "setOpacity")
-
- env.push_proc(1) do |ptr|
- sbx = Duktape::Sandbox.new(ptr)
- sbx.push_heap_stash
- sbx.get_prop_string(-1, "setVisibilityClosure")
- proc = ::Box(Proc(Bool, Nil)).unbox(sbx.get_pointer(-1))
- visible = sbx.get_boolean(0).not_nil!
- proc.call(visible)
- sbx.call_success
- end
-
- env.put_prop_string(-2, "setVisible")
-
- env.push_proc(1) do |ptr|
- sbx = Duktape::Sandbox.new(ptr)
- sbx.push_heap_stash
- sbx.get_prop_string(-1, "setTextClosure")
- proc = ::Box(Proc(String, Nil)).unbox(sbx.get_pointer(-1))
- text = sbx.get_string(0).not_nil!
- proc.call(text)
- sbx.call_success
- end
-
- env.put_prop_string(-2, "setText")
-
- env.push_proc(1) do |ptr|
- sbx = Duktape::Sandbox.new(ptr)
- sbx.push_heap_stash
- sbx.get_prop_string(-1, "getTextClosure")
- proc = ::Box(Proc(String)).unbox(sbx.get_pointer(-1))
- sbx.push_string(proc.call)
- sbx.call_success
- end
-
- env.put_prop_string(-2, "getText")
-
- env.push_proc(4) do |ptr|
- sbx = Duktape::Sandbox.new(ptr)
- sbx.push_heap_stash
- sbx.get_prop_string(-1, "setForegroundColorClosure")
- proc = ::Box(Proc(Float64, Float64, Float64, Float64, Nil)).unbox(sbx.get_pointer(-1))
-
- r = sbx.get_number(0).not_nil!
- g = sbx.get_number(1).not_nil!
- b = sbx.get_number(2).not_nil!
- a = sbx.get_number(3).not_nil!
-
- proc.call(r.to_f64, g.to_f64, b.to_f64, a.to_f64)
- sbx.call_success
- end
-
- env.put_prop_string(-2, "setForegroundColor")
-
- env.push_proc(4) do |ptr|
- sbx = Duktape::Sandbox.new(ptr)
- sbx.push_heap_stash
- sbx.get_prop_string(-1, "setBackgroundColorClosure")
- proc = ::Box(Proc(Float64, Float64, Float64, Float64, Nil)).unbox(sbx.get_pointer(-1))
-
- r = sbx.get_number(0).not_nil!
- g = sbx.get_number(1).not_nil!
- b = sbx.get_number(2).not_nil!
- a = sbx.get_number(2).not_nil!
-
- proc.call(r.to_f64, g.to_f64, b.to_f64, a.to_f64)
- sbx.call_success
- end
-
- env.put_prop_string(-2, "setBackgroundColor")
-
- env.push_number(widget.opacity)
- env.put_prop_string(idx, "opacity")
-
- env.push_boolean(widget.visible)
- env.put_prop_string(idx, "visible")
- env.call_success
- end
-
- context.push_heap_stash
- context.push_pointer(::Box.box(get_element_by_id_proc))
- context.put_prop_string(-2, "setElementOpacityByIdClosure")
-
- context.push_global_proc("setElementOpacityById", 2) do |ptr|
- env = Duktape::Sandbox.new(ptr)
- env.push_heap_stash
- env.get_prop_string(-1, "setElementOpacityByIdClosure")
- function = ::Box(Proc(String, Pointer(LibGtk::Widget))).unbox(env.get_pointer(-1))
- component_id = env.get_string(0).not_nil!
- component_value = env.get_number(1).not_nil!.as(Float64)
- widget = function.call(component_id)
- idx = env.push_object
- widget = widget.as(Gtk::Widget)
-
- widget.opacity = component_value
-
- env.call_success
- end
-
- context.push_heap_stash
- context.push_pointer(::Box.box(get_element_by_id_proc))
- context.put_prop_string(-2, "setElementVisibilityByIdClosure")
-
- context.push_global_proc("setElementVisibilityById", 2) do |ptr|
- env = Duktape::Sandbox.new(ptr)
- env.push_heap_stash
- env.get_prop_string(-1, "setElementVisibilityByIdClosure")
- function = ::Box(Proc(String, Pointer(LibGtk::Widget))).unbox(env.get_pointer(-1))
- component_id = env.get_string(0).not_nil!
- component_value = env.get_boolean(1).not_nil!
- widget = function.call(component_id)
- idx = env.push_object
- widget = widget.as(Gtk::Widget)
-
- widget.visible = component_value
-
- env.call_success
- end
-
- structure.as(Element).on_component_did_mount
+ @component_storage.store_application("MainApplication", @application.not_nil!)
# Do a little benchmark of how long it takes to build
# the structure.
puts "Building components..."
- elapsed_time = Time.measure { build_components(structure) }
- puts "Finished. #{elapsed_text(elapsed_time)}"
+ elapsed_time = Time.measure { build_components(structure, @application.not_nil!) }
+ puts "Finished: #{elapsed_text(elapsed_time)}"
+
+ cid =
+ @component_storage
+ .components
+ .first
+
+ window =
+ @component_storage
+ .retrieve(cid)
+ .as(Gtk::ApplicationWindow)
- @window.try(&.show_all)
+ window.try(&.show_all)
end)
else
# TODO: Refactor this later to an actual error message.
- raise "The first component must always be an application."
+ raise "The first component always must be an application."
end
end
end
+ # Calculate the elapsed text from elapsed time.
private def elapsed_text(elapsed)
millis = elapsed.total_milliseconds
return "#{millis.round(2)}ms" if millis >= 1
@@ -394,495 +215,33 @@ module Layout
"#{(millis * 1000).round(2)}µs"
end
- private def to_align(str : String) : Gtk::Align
- case str
- when "fill"
- Gtk::Align::FILL
- when "start"
- Gtk::Align::START
- when "end"
- Gtk::Align::END
- when "center"
- Gtk::Align::CENTER
- when "baseline"
- Gtk::Align::BASELINE
- else
- Gtk::Align::BASELINE
- end
- end
-
- private def add_class_to_css(widget, class_name)
- if class_name
- context = widget.style_context
- context.add_class(class_name.not_nil!)
- end
- end
-
- private def to_bool(str : String) : Bool
- if str == "true"
- true
- else
- false
- end
- end
-
- # ameba:disable Metrics/CyclomaticComplexity
- private def build_widget(child, widget : Gtk::Widget)
+ # Run the initalize_component method for each child and receive an actual Gtk::Widget from it
+ # then either containerize it if it is a container or just return the transpiled component.
+ private def transpile_component(child, widget : Gtk::Widget)
case child
- when Text
- else
- unless child.as(Element).attributes["classId"]?
- child.as(Element).attributes["classId"] = "#{child.kind.downcase}#{UUID.random.hexstring}"
- end
- end
-
- case child
- when Button
- id = child.attributes["id"]? || nil
- class_name = child.attributes["class"]? || nil
- relief = child.attributes["relief"]? || nil
- text = child.children[0].as(Text).data.to_s
- on_click = child.attributes["onClick"]? || ""
-
- horizontal_align = to_align(child.attributes["horizontalAlign"]? || "")
- vertical_align = to_align(child.attributes["verticalAlign"]? || "")
-
- case relief
- when "none"
- relief_style = Gtk::ReliefStyle::NONE
- when "normal"
- relief_style = Gtk::ReliefStyle::NORMAL
- else
- relief_style = Gtk::ReliefStyle::NORMAL
- end
-
- button = Gtk::Button.new(name: id, label: text, relief: relief_style, halign: horizontal_align, valign: vertical_align)
-
- box_expand = child.attributes["boxExpand"]? || "false"
- box_fill = child.attributes["boxFill"]? || "false"
- box_padding = child.attributes["boxPadding"]? || "0"
-
- if box_padding.includes?(".0")
- box_padding = box_padding[..box_padding.size - 3]
- end
-
- button.on_clicked do
- Layout::Js::Engine::INSTANCE.evaluate("#{on_click}()")
- end
-
- if class_id = child.attributes["classId"]?
- @elements[class_id] = button.as(Pointer(LibGtk::Widget))
- end
-
- if id = child.attributes["id"]?
- @components[id] = button.as(Pointer(LibGtk::Widget))
- end
-
- case widget
- when Gtk::Notebook
- widget.append_page(button, nil)
- when Gtk::Box
- widget.pack_start(button, to_bool(box_expand), to_bool(box_fill), box_padding.to_i)
- when Gtk::ApplicationWindow
- widget.add(button)
- end
-
- button.on_event_after do |widget, event|
- case event.event_type
- when Gdk::EventType::MOTION_NOTIFY
- false
- else
- child.on_component_did_update(child.attributes["classId"], event.event_type.to_s)
- true
- end
- end
-
- add_class_to_css(button, class_name)
- child.on_component_did_mount
- when TextInput
- id = child.attributes["id"]? || nil
- class_name = child.attributes["class"]? || nil
- label = child.attributes["value"]? || nil
- placeholder = child.attributes["placeholder"]? || nil
- text_changed = child.attributes["onChangeText"]? || nil
- cut_clipboard = child.attributes["onCut"]? || nil
- copy_clipboard = child.attributes["onCopy"]? || nil
- paste_clipboard = child.attributes["onPaste"]? || nil
- on_activate = child.attributes["onActivate"]? || nil
-
- horizontal_align = to_align(child.attributes["horizontalAlign"]? || "")
- vertical_align = to_align(child.attributes["verticalAlign"]? || "")
-
- password_char = child.attributes["passwordCharacter"]? || nil
- visibility = to_bool(child.attributes["isPassword"]? || "false") ? false : true
- invisible_char = password_char.try(&.bytes.first.to_u32)
- entry = Gtk::Entry.new(name: id, text: label, placeholder_text: placeholder, invisible_char: invisible_char, visibility: visibility, halign: horizontal_align, valign: vertical_align)
-
- entry.buffer.on_inserted_text do |buffer|
- if text_changed
- Layout::Js::Engine::INSTANCE.evaluate("#{text_changed}('#{buffer.text}')")
- end
- end
-
- entry.buffer.on_deleted_text do |buffer|
- if text_changed
- Layout::Js::Engine::INSTANCE.evaluate("#{text_changed}('#{buffer.text}')")
- end
- end
-
- entry.on_cut_clipboard do
- if cut_clipboard
- Layout::Js::Engine::INSTANCE.evaluate("#{cut_clipboard}('#{entry.buffer.text}')")
- end
- end
-
- entry.on_copy_clipboard do
- if copy_clipboard
- Layout::Js::Engine::INSTANCE.evaluate("#{copy_clipboard}('#{entry.buffer.text}')")
- end
- end
-
- entry.on_paste_clipboard do
- if paste_clipboard
- Layout::Js::Engine::INSTANCE.evaluate("#{paste_clipboard}('#{entry.buffer.text}')")
- end
- end
-
- entry.on_activate do
- if on_activate
- Layout::Js::Engine::INSTANCE.evaluate("#{on_activate}('#{entry.buffer.text}')")
- end
- end
-
- box_expand = child.attributes["boxExpand"]? || "false"
- box_fill = child.attributes["boxFill"]? || "false"
- box_padding = child.attributes["boxPadding"]? || "0"
-
- if box_padding.includes?(".0")
- box_padding = box_padding[..box_padding.size - 3]
- end
-
- if class_id = child.attributes["classId"]?
- @elements[class_id] = entry.as(Pointer(LibGtk::Widget))
- end
-
- if id = child.attributes["id"]?
- @components[id] = entry.as(Pointer(LibGtk::Widget))
- end
-
- case widget
- when Gtk::Notebook
- widget.append_page(entry, nil)
- when Gtk::Box
- widget.pack_start(entry, to_bool(box_expand), to_bool(box_fill), box_padding.to_i)
- when Gtk::ApplicationWindow
- widget.add(entry)
- end
-
- entry.on_event_after do |widget, event|
- case event.event_type
- when Gdk::EventType::MOTION_NOTIFY
- false
- else
- child.on_component_did_update(child.attributes["classId"], event.event_type.to_s)
- true
- end
- end
-
- add_class_to_css(entry, class_name)
- child.on_component_did_mount
- when Switch
- id = child.attributes["id"]? || nil
- class_name = child.attributes["class"]? || nil
-
- horizontal_align = to_align(child.attributes["horizontalAlign"]? || "")
- vertical_align = to_align(child.attributes["verticalAlign"]? || "")
- value = to_bool(child.attributes["value"]? || "false")
-
- switch = Gtk::Switch.new(name: id, halign: horizontal_align, valign: vertical_align, state: value)
-
- box_expand = child.attributes["boxExpand"]? || "false"
- box_fill = child.attributes["boxFill"]? || "false"
- box_padding = child.attributes["boxPadding"]? || "0"
-
- value_change = child.attributes["onValueChange"]? || nil
-
- if box_padding.includes?(".0")
- box_padding = box_padding[..box_padding.size - 3]
- end
-
- switch.on_state_set do
- if value_change
- Layout::Js::Engine::INSTANCE.evaluate("#{value_change}(#{switch.active})")
- end
-
- true
- end
-
- if class_id = child.attributes["classId"]?
- @elements[class_id] = switch.as(Pointer(LibGtk::Widget))
- end
-
- if id = child.attributes["id"]?
- @components[id] = switch.as(Pointer(LibGtk::Widget))
- end
-
- case widget
- when Gtk::Notebook
- widget.append_page(switch, nil)
- when Gtk::Box
- widget.pack_start(switch, to_bool(box_expand), to_bool(box_fill), box_padding.to_i)
- when Gtk::ApplicationWindow
- widget.add(switch)
- end
-
- switch.on_event_after do |widget, event|
- case event.event_type
- when Gdk::EventType::MOTION_NOTIFY
- false
- else
- child.on_component_did_update(child.attributes["classId"], event.event_type.to_s)
- true
- end
- end
-
- add_class_to_css(switch, class_name)
- child.on_component_did_mount
- when Image
- id = child.attributes["id"]? || nil
- class_name = child.attributes["class"]? || nil
- source = child.attributes["src"]? || ""
-
- width = child.attributes["width"]? || "256"
- height = child.attributes["height"]? || "256"
-
- preserve_aspect_ration = child.attributes["preserveAspectRation"]? || "true"
-
- if width.includes?(".0")
- width = width[..width.size - 3]
- end
-
- if height.includes?(".0")
- height = height[..height.size - 3]
- end
-
- horizontal_align = to_align(child.attributes["horizontalAlign"]? || "")
- vertical_align = to_align(child.attributes["verticalAlign"]? || "")
-
- if width && height
- image = Gtk::Image.new(
- name: id,
- # TODO: Create an issue in the GTK bindings repository.
- # pixbuf: GdkPixbuf::Pixbuf.new_from_file_at_scale(source, width.to_i, height.to_i, to_bool(preserve_aspect_ration)),
- halign: horizontal_align,
- valign: vertical_align
- )
- else
- image = Gtk::Image.new(
- name: id,
- file: source,
- halign: horizontal_align,
- valign: vertical_align
- )
- end
-
- box_expand = child.attributes["boxExpand"]? || "false"
- box_fill = child.attributes["boxFill"]? || "false"
- box_padding = child.attributes["boxPadding"]? || "0"
-
- if box_padding.includes?(".0")
- box_padding = box_padding[..box_padding.size - 3]
- end
-
- if class_id = child.attributes["classId"]?
- @elements[class_id] = image.as(Pointer(LibGtk::Widget))
- end
-
- if id = child.attributes["id"]?
- @components[id] = image.as(Pointer(LibGtk::Widget))
- end
-
- case widget
- when Gtk::Notebook
- widget.append_page(image, nil)
- when Gtk::Box
- widget.pack_start(image, to_bool(box_expand), to_bool(box_fill), box_padding.to_i)
- when Gtk::ApplicationWindow
- widget.add(image)
- end
-
- image.on_event_after do |widget, event|
- case event.event_type
- when Gdk::EventType::MOTION_NOTIFY
- false
- else
- child.on_component_did_update(child.attributes["classId"], event.event_type.to_s)
- true
- end
- end
-
- add_class_to_css(image, class_name)
- child.on_component_did_mount
- when Label
- id = child.attributes["id"]? || nil
- class_name = child.attributes["class"]? || nil
- text = child.children[0].as(Text).data.to_s
- horizontal_align = to_align(child.attributes["horizontalAlign"]? || "")
- vertical_align = to_align(child.attributes["verticalAlign"]? || "")
- label = Gtk::Label.new(name: id, label: text, halign: horizontal_align, valign: vertical_align, wrap: true)
-
- box_expand = child.attributes["boxExpand"]? || "false"
- box_fill = child.attributes["boxFill"]? || "false"
- box_padding = child.attributes["boxPadding"]? || "0"
-
- if box_padding.includes?(".0")
- box_padding = box_padding[..box_padding.size - 3]
- end
-
- if class_id = child.attributes["classId"]?
- @elements[class_id] = label.as(Pointer(LibGtk::Widget))
- end
-
- if id = child.attributes["id"]?
- @components[id] = label.as(Pointer(LibGtk::Widget))
- end
-
- case widget
- when Gtk::Notebook
- widget.append_page(label, nil)
- when Gtk::Box
- widget.pack_start(label, to_bool(box_expand), to_bool(box_fill), box_padding.to_i)
- when Gtk::ApplicationWindow
- widget.add(label)
- end
-
- label.on_event_after do |widget, event|
- case event.event_type
- when Gdk::EventType::MOTION_NOTIFY
- false
- else
- child.on_component_did_update(child.attributes["classId"], event.event_type.to_s)
- true
- end
- end
-
- add_class_to_css(label, class_name)
- child.on_component_did_mount
- when Tab
- id = child.attributes["id"]? || nil
- class_name = child.attributes["class"]? || nil
- horizontal_align = to_align(child.attributes["horizontalAlign"]? || "")
- vertical_align = to_align(child.attributes["verticalAlign"]? || "")
-
- tab = Gtk::Notebook.new(name: id, halign: horizontal_align, valign: vertical_align)
+ when Box, Frame, Tab, ListBox, ScrolledWindow
+ container = child.initialize_component(widget, @component_storage)
child.children.each do |subchild|
- build_widget(subchild, tab)
- end
-
- box_expand = child.attributes["boxExpand"]? || "false"
- box_fill = child.attributes["boxFill"]? || "false"
- box_padding = child.attributes["boxPadding"]? || "0"
-
- if box_padding.includes?(".0")
- box_padding = box_padding[..box_padding.size - 3]
- end
-
- if class_id = child.attributes["classId"]?
- @elements[class_id] = tab.as(Pointer(LibGtk::Widget))
- end
-
- if id = child.attributes["id"]?
- @components[id] = tab.as(Pointer(LibGtk::Widget))
- end
-
- case widget
- when Gtk::Notebook
- widget.append_page(tab, nil)
- when Gtk::Box
- widget.pack_start(tab, to_bool(box_expand), to_bool(box_fill), box_padding.to_i)
- when Gtk::ApplicationWindow
- widget.add(tab)
- end
-
- tab.on_event_after do |widget, event|
- case event.event_type
- when Gdk::EventType::MOTION_NOTIFY
- false
- else
- child.on_component_did_update(child.attributes["classId"], event.event_type.to_s)
- true
- end
- end
-
- add_class_to_css(tab, class_name)
- child.on_component_did_mount
- when Box
- id = child.attributes["id"]? || nil
- class_name = child.attributes["class"]? || nil
- horizontal_align = to_align(child.attributes["horizontalAlign"]? || "")
- vertical_align = to_align(child.attributes["verticalAlign"]? || "")
- case child.attributes["orientation"]?
- when "vertical"
- orientation = Gtk::Orientation::VERTICAL
- when "horizontal"
- orientation = Gtk::Orientation::HORIZONTAL
- else
- orientation = Gtk::Orientation::VERTICAL
+ transpile_component(subchild, container)
end
-
- box_expand = child.attributes["boxExpand"]? || "false"
- box_fill = child.attributes["boxFill"]? || "false"
- box_padding = child.attributes["boxPadding"]? || "0"
-
- if box_padding.includes?(".0")
- box_padding = box_padding[..box_padding.size - 3]
- end
-
- spacing = child.attributes["spacing"]? || "2"
-
- box = Gtk::Box.new(name: id, orientation: orientation, spacing: spacing.to_i, halign: horizontal_align, valign: vertical_align)
-
- child.children.each do |subchild|
- build_widget(subchild, box)
- end
-
- box.on_event_after do |widget, event|
- case event.event_type
- when Gdk::EventType::MOTION_NOTIFY
- false
- else
- child.on_component_did_update(child.attributes["classId"], event.event_type.to_s)
- true
- end
- end
-
- if class_id = child.attributes["classId"]?
- @elements[class_id] = box.as(Pointer(LibGtk::Widget))
- end
-
- if id = child.attributes["id"]?
- @components[id] = box.as(Pointer(LibGtk::Widget))
- end
-
- case widget
- when Gtk::Notebook
- widget.append_page(box, nil)
- when Gtk::Box
- widget.pack_start(box, to_bool(box_expand), to_bool(box_fill), box_padding.to_i)
- when Gtk::ApplicationWindow
- widget.add(box)
- end
-
- add_class_to_css(box, class_name)
- child.on_component_did_mount
- when EventBox
- nil
+ when Button, Label, TextInput, HorizontalSeparator, VerticalSeparator, Switch
+ child.initialize_component(widget, @component_storage)
else
nil
end
end
+ # Process the StyleSheet's first and then proceed to processing the components.
+ private def transpile_components(parent, widget : Gtk::Widget)
+ recursive_stylesheet_processing(parent)
+
+ parent.children.each do |child|
+ transpile_component(child, widget)
+ end
+ end
+
+ # Recursively drill down the components and find StyleSheet components and process them before proceeding.
private def recursive_stylesheet_processing(parent)
parent.children.each do |child|
case child
@@ -894,15 +253,7 @@ module Layout
end
end
- # ameba:disable Metrics/CyclomaticComplexity
- private def build_widgets(parent, widget : Gtk::Widget)
- recursive_stylesheet_processing(parent)
-
- parent.children.each do |child|
- build_widget(child, widget.not_nil!)
- end
- end
-
+ # Use the Gtk::CssProvider to update the style context with the source of the CSS file.
private def process_stylesheet(child)
css_provider = Gtk::CssProvider.new
css_provider.load_from_path(child.attributes["src"])
@@ -911,72 +262,16 @@ module Layout
Gtk::StyleContext.add_provider_for_screen screen, css_provider, Gtk::STYLE_PROVIDER_PRIORITY_APPLICATION
end
- private def build_components(document)
+ # Build components from the main document model, start with either a StyleSheet component or the Window component.
+ private def build_components(document, widget)
document.children.each do |child|
case child
when StyleSheet
process_stylesheet(child)
when Window
- id = child.attributes["id"]? || nil
- class_name = child.attributes["class"]? || nil
- title = child.attributes["title"]? || "Untitled"
- width = child.attributes["width"]? || "800"
- height = child.attributes["height"]? || "600"
-
- unless child.as(Element).attributes["classId"]?
- child.as(Element).attributes["classId"] = "#{child.kind.downcase}#{UUID.random.hexstring}"
- end
-
- if width.includes?(".0")
- width = width[..width.size - 3]
- end
-
- if height.includes?(".0")
- height = height[..height.size - 3]
- end
-
- @window = Gtk::ApplicationWindow.new(
- name: id,
- application: @application.not_nil!,
- title: title,
- default_width: width.to_i,
- default_height: height.to_i
- )
-
- @window.try(&.connect "destroy", &->exit)
-
- window_resize_proc = ->(width : Int32, height : Int32) {
- @window.not_nil!.resize(width, height)
- }
-
- context = Layout::Js::Engine::INSTANCE.runtime.context
-
- context.push_heap_stash
- context.push_pointer(::Box.box(window_resize_proc))
- context.put_prop_string(-2, "resizeWindowClosure")
-
- context.push_global_proc("resizeWindow", 2) do |ptr|
- env = Duktape::Sandbox.new(ptr)
- env.push_heap_stash
- env.get_prop_string(-1, "resizeWindowClosure")
- function = ::Box(Proc(Int32, Int32, Nil)).unbox(env.get_pointer(-1))
- function.call(env.get_int(0), env.get_int(1))
- env.call_success
- end
-
- if class_id = child.attributes["classId"]?
- @elements[class_id] = @window.not_nil!.as(Pointer(LibGtk::Widget))
- end
-
- if id = child.attributes["id"]?
- @components[id] = @window.not_nil!.as(Pointer(LibGtk::Widget))
- end
-
- @window.not_nil!.position = Gtk::WindowPosition::CENTER_ALWAYS
-
- child.on_component_did_mount
- add_class_to_css(@window.not_nil!, class_name)
- build_widgets(child, @window.not_nil!)
+ window = child.initialize_component(widget, @component_storage)
+ transpile_components(child, window)
+ child.did_mount(child.cid)
else
# TODO: Handle other non-crucial parts.
end
diff --git a/src/layout/transpiler/component_storage.cr b/src/layout/transpiler/component_storage.cr
new file mode 100644
index 0000000..cb6675d
--- /dev/null
+++ b/src/layout/transpiler/component_storage.cr
@@ -0,0 +1,43 @@
+module Layout
+ module Transpiler
+ class ComponentStorage
+ INSTANCE = new
+
+ private property applications : Hash(String, Gtk::Application)
+ private property components : Hash(String, Pointer(LibGtk::Widget))
+
+ def initialize
+ @applications = {} of String => Gtk::Application
+ @components = {} of String => Pointer(LibGtk::Widget)
+ end
+
+ def store(cid : String, widget : Gtk::Widget)
+ @components[cid] = widget.as(Pointer(LibGtk::Widget))
+ end
+
+ def retrieve(cid : String) : Pointer(LibGtk::Widget)
+ if widget = @components[cid]?
+ widget.not_nil!
+ else
+ raise Exceptions::ComponentNotFoundException.new(cid)
+ end
+ end
+
+ def components : Array(String)
+ @components.keys
+ end
+
+ def applications : Array(String)
+ @applications.keys
+ end
+
+ def store_application(cid : String, application : Gtk::Application)
+ @applications[cid] = application
+ end
+
+ def retrieve_application(cid : String) : Gtk::Application
+ @applications[cid]
+ end
+ end
+ end
+end
diff --git a/src/layout/version.cr b/src/layout/version.cr
index a53d926..270c3c7 100644
--- a/src/layout/version.cr
+++ b/src/layout/version.cr
@@ -1,3 +1,3 @@
module Layout
- VERSION = "0.1.0"
+ VERSION = {{ `shards version #{__DIR__}`.chomp.stringify }}
end