From 2abe49c1a57d1a657459cf81d62b921fc1eb37a0 Mon Sep 17 00:00:00 2001 From: Giorgi Kavrelishvili Date: Fri, 4 Jun 2021 19:35:03 +0400 Subject: [PATCH 1/5] Stash changes --- .gitignore | 2 + example/application.cr | 1 + example/dist/components/authorization.html | 22 - example/dist/index.html | 105 +++-- example/dist/scripts/authorization.js | 11 - example/dist/scripts/index.js | 31 ++ example/dist/styles/authorization.css | 26 -- example/dist/styles/index.css | 188 ++++++-- shard.yml | 6 +- src/layout.cr | 3 +- src/layout/dom/application.cr | 2 +- src/layout/dom/box.cr | 9 +- src/layout/dom/button.cr | 2 +- src/layout/dom/element.cr | 2 +- src/layout/dom/entry.cr | 2 +- src/layout/dom/event_box.cr | 2 +- src/layout/dom/export.cr | 2 +- src/layout/dom/frame.cr | 43 ++ src/layout/dom/horizontal_separator.cr | 46 ++ src/layout/dom/image.cr | 2 +- src/layout/dom/import.cr | 2 +- src/layout/dom/label.cr | 2 +- src/layout/dom/list_box.cr | 43 ++ src/layout/dom/progress_bar.cr | 46 ++ src/layout/dom/script.cr | 18 +- src/layout/dom/scrolled_window.cr | 43 ++ src/layout/dom/spinner.cr | 46 ++ src/layout/dom/style_sheet.cr | 2 +- src/layout/dom/switch.cr | 2 +- src/layout/dom/tab.cr | 2 +- src/layout/dom/text.cr | 2 +- src/layout/dom/text_view.cr | 45 ++ src/layout/dom/vertical_separator.cr | 46 ++ src/layout/dom/window.cr | 4 +- src/layout/ext/debug.cr | 7 + src/layout/ext/runtime.cr | 2 +- src/layout/js/engine.cr | 23 +- src/layout/js/std/fs.cr | 2 +- src/layout/js/std/misc.cr | 6 +- src/layout/js/std/process.cr | 2 +- src/layout/js/std/system.cr | 2 +- src/layout/parser/tokenizer.cr | 19 +- src/layout/transpiler/builder.cr | 484 ++++++++++++++++----- 43 files changed, 1080 insertions(+), 277 deletions(-) delete mode 100644 example/dist/components/authorization.html delete mode 100644 example/dist/scripts/authorization.js delete mode 100644 example/dist/styles/authorization.css create mode 100644 src/layout/dom/frame.cr create mode 100644 src/layout/dom/horizontal_separator.cr create mode 100644 src/layout/dom/list_box.cr create mode 100644 src/layout/dom/progress_bar.cr create mode 100644 src/layout/dom/scrolled_window.cr create mode 100644 src/layout/dom/spinner.cr create mode 100644 src/layout/dom/text_view.cr create mode 100644 src/layout/dom/vertical_separator.cr create mode 100644 src/layout/ext/debug.cr 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/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..2440265 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" + } +} + +if (fs.fileExists("./config.json")) { + appConfiguration = JSON.parse(fs.readFile("./config.json")) +} else { + fs.writeFile("./config.json", JSON.stringify(appConfiguration, null, 2)) +} + +function updateNumberOne(buffer) { + var element = getElementById("numberOneLabel"); + element.setText(buffer); +} + +function updateNumberTwo(buffer) { + var element = getElementById("numberTwoLabel"); + element.setText(buffer); +} \ 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..49f4ec3 100644 --- a/example/dist/styles/index.css +++ b/example/dist/styles/index.css @@ -1,63 +1,179 @@ -.mainWindow { - color: blue; - background-color: black; +.main-box { + transition: all 1s ease; + background-color: rgb(28, 30, 39); } -.mainBox { - transition: all 1s ease; - background-color: white; +.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); } -.disablerBox{ +.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; } -.darkModeLabel { - transition: all 1s ease; - color: black; +.number-text{ + 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:hover{ + color: red; +} + +.main-button{ + transition: all 0.5s ease; background-color: transparent; - color: black; + 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; } -.darkModeSwitch slider { - transition: all 1s ease; - background-color: black; - border: none; +.main-button:hover{ + transition: all 0.5s ease; + border-color: rgb(200, 60, 100); + color:rgb(200,200,200); } -.mainLabel { +.main-switch{ transition: all 1s ease; - color: black; - margin-top: 50px; - font-size: 50px; + background-color: transparent; + background-image: none; + box-shadow: none; + text-shadow: none; + border-color: rgb(231, 68, 111); + margin-top: 20px; + margin-bottom: 20px; + margin-right: 20px; } -.mainLabel:focus { - color: red; +.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 { - transition: all 1s ease; - font-size: 25px; - border-color: transparent; +.main-switch slider:hover { + transition: all 0.5s ease; background-color: transparent; - margin-top: 25px; - margin-bottom: 50px; - color: black; + background-image: none; + box-shadow: none; + text-shadow: none; + border-color: rgb(248, 202, 196); } -.authorizationButton:hover { - transition: all 1s ease; - color: rgba(0, 0, 0, 0.5); +.main-list-box { + margin-left: 20px; + margin-right: 20px; + margin-bottom: 20px; + background-color: transparent; } -.authorizationButton:active { - transition: all 1s ease; - 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..d7c9c52 100644 --- a/shard.yml +++ b/shard.yml @@ -11,11 +11,13 @@ 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 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..226ead2 100644 --- a/src/layout/dom/application.cr +++ b/src/layout/dom/application.cr @@ -22,7 +22,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}" diff --git a/src/layout/dom/box.cr b/src/layout/dom/box.cr index f1b2290..f708713 100644 --- a/src/layout/dom/box.cr +++ b/src/layout/dom/box.cr @@ -20,7 +20,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}" @@ -28,13 +28,6 @@ module Layout end end end - - if id = @attributes["id"]? - Layout::Js::Engine::INSTANCE.evaluate(<<-JS - const #{id}State = #{self.to_json} - JS - ) - end end def to_html : String diff --git a/src/layout/dom/button.cr b/src/layout/dom/button.cr index 16b0bb7..c9d41f4 100644 --- a/src/layout/dom/button.cr +++ b/src/layout/dom/button.cr @@ -22,7 +22,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}" diff --git a/src/layout/dom/element.cr b/src/layout/dom/element.cr index 08b6cc5..3596583 100644 --- a/src/layout/dom/element.cr +++ b/src/layout/dom/element.cr @@ -20,7 +20,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}" diff --git a/src/layout/dom/entry.cr b/src/layout/dom/entry.cr index d98d4f2..b515832 100644 --- a/src/layout/dom/entry.cr +++ b/src/layout/dom/entry.cr @@ -23,7 +23,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}" diff --git a/src/layout/dom/event_box.cr b/src/layout/dom/event_box.cr index 0619297..d758ff9 100644 --- a/src/layout/dom/event_box.cr +++ b/src/layout/dom/event_box.cr @@ -20,7 +20,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}" diff --git a/src/layout/dom/export.cr b/src/layout/dom/export.cr index 698b7f2..8051696 100644 --- a/src/layout/dom/export.cr +++ b/src/layout/dom/export.cr @@ -22,7 +22,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}" diff --git a/src/layout/dom/frame.cr b/src/layout/dom/frame.cr new file mode 100644 index 0000000..0f6163b --- /dev/null +++ b/src/layout/dom/frame.cr @@ -0,0 +1,43 @@ +require "./node" +require "./element" + +module Layout + module Dom + class Frame < Element + property attributes : Hash(String, String) + + def initialize(@attributes, @children) + @kind = "Frame" + + @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("__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}" + end + end + end + end + 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}" + 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..7fa27c7 --- /dev/null +++ b/src/layout/dom/horizontal_separator.cr @@ -0,0 +1,46 @@ +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 + + @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("__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}" + end + end + end + end + 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}" + end + end + end +end diff --git a/src/layout/dom/image.cr b/src/layout/dom/image.cr index ede2a55..71072d9 100644 --- a/src/layout/dom/image.cr +++ b/src/layout/dom/image.cr @@ -23,7 +23,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}" diff --git a/src/layout/dom/import.cr b/src/layout/dom/import.cr index a6f0a35..bbad5e3 100644 --- a/src/layout/dom/import.cr +++ b/src/layout/dom/import.cr @@ -23,7 +23,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}" diff --git a/src/layout/dom/label.cr b/src/layout/dom/label.cr index 10af3c1..e79fe1a 100644 --- a/src/layout/dom/label.cr +++ b/src/layout/dom/label.cr @@ -22,7 +22,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}" diff --git a/src/layout/dom/list_box.cr b/src/layout/dom/list_box.cr new file mode 100644 index 0000000..e27b38a --- /dev/null +++ b/src/layout/dom/list_box.cr @@ -0,0 +1,43 @@ +require "./node" +require "./element" + +module Layout + module Dom + class ListBox < Element + property attributes : Hash(String, String) + + def initialize(@attributes, @children) + @kind = "ListBox" + + @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("__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}" + end + end + end + end + 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}" + 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..afcb150 --- /dev/null +++ b/src/layout/dom/progress_bar.cr @@ -0,0 +1,46 @@ +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 + + @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("__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}" + end + end + end + end + 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}" + end + end + end +end diff --git a/src/layout/dom/script.cr b/src/layout/dom/script.cr index 23e01d8..3f0d43c 100644 --- a/src/layout/dom/script.cr +++ b/src/layout/dom/script.cr @@ -22,7 +22,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}" @@ -34,12 +34,26 @@ module Layout if source_file = @attributes["src"]? fp = File.open(source_file).gets_to_end Layout::Js::Engine::INSTANCE.evaluate(fp) + + 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 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! diff --git a/src/layout/dom/scrolled_window.cr b/src/layout/dom/scrolled_window.cr new file mode 100644 index 0000000..2360c1a --- /dev/null +++ b/src/layout/dom/scrolled_window.cr @@ -0,0 +1,43 @@ +require "./node" +require "./element" + +module Layout + module Dom + class ScrolledWindow < Element + property attributes : Hash(String, String) + + def initialize(@attributes, @children) + @kind = "ScrolledWindow" + + @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("__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}" + end + end + end + end + 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}" + end + end + end +end diff --git a/src/layout/dom/spinner.cr b/src/layout/dom/spinner.cr new file mode 100644 index 0000000..87e33e1 --- /dev/null +++ b/src/layout/dom/spinner.cr @@ -0,0 +1,46 @@ +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 + + @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("__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}" + end + end + end + end + 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}" + end + end + end +end diff --git a/src/layout/dom/style_sheet.cr b/src/layout/dom/style_sheet.cr index 402ade6..ccf8c2f 100644 --- a/src/layout/dom/style_sheet.cr +++ b/src/layout/dom/style_sheet.cr @@ -23,7 +23,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}" diff --git a/src/layout/dom/switch.cr b/src/layout/dom/switch.cr index f7f1daa..475fb9a 100644 --- a/src/layout/dom/switch.cr +++ b/src/layout/dom/switch.cr @@ -23,7 +23,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}" diff --git a/src/layout/dom/tab.cr b/src/layout/dom/tab.cr index 2bb5766..4d2c0d9 100644 --- a/src/layout/dom/tab.cr +++ b/src/layout/dom/tab.cr @@ -20,7 +20,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}" 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..5630a68 --- /dev/null +++ b/src/layout/dom/text_view.cr @@ -0,0 +1,45 @@ +require "./node" +require "./element" + +module Layout + module Dom + class TextView < Element + @attributes : Hash(String, String) + + getter :attributes + + def initialize(@attributes, @children) + @kind = "TextView" + + @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("__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}" + end + end + end + end + 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}" + 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..276e774 --- /dev/null +++ b/src/layout/dom/vertical_separator.cr @@ -0,0 +1,46 @@ +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 + + @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("__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}" + end + end + end + end + 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}" + end + end + end +end diff --git a/src/layout/dom/window.cr b/src/layout/dom/window.cr index 16d9fc1..4dc13de 100644 --- a/src/layout/dom/window.cr +++ b/src/layout/dom/window.cr @@ -13,16 +13,14 @@ module Layout @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!)}") + @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}" diff --git a/src/layout/ext/debug.cr b/src/layout/ext/debug.cr new file mode 100644 index 0000000..3afb6dd --- /dev/null +++ b/src/layout/ext/debug.cr @@ -0,0 +1,7 @@ +module Duktape + module API::Debug + def dump! + puts("STACK: #{stack}") + 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..8923c1e 100644 --- a/src/layout/js/engine.cr +++ b/src/layout/js/engine.cr @@ -31,7 +31,7 @@ module Layout fs() system() - context.eval_string! <<-JS + @runtime.context.eval! <<-JS String.prototype.format = function() { var formatted = this; for( var arg in arguments ) { @@ -42,18 +42,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/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..4cf1e3c 100644 --- a/src/layout/parser/tokenizer.cr +++ b/src/layout/parser/tokenizer.cr @@ -121,12 +121,19 @@ module Layout 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 +149,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 @@ -182,8 +187,14 @@ module Layout 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 +203,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 diff --git a/src/layout/transpiler/builder.cr b/src/layout/transpiler/builder.cr index 1d05090..0a8be93 100644 --- a/src/layout/transpiler/builder.cr +++ b/src/layout/transpiler/builder.cr @@ -5,6 +5,8 @@ module Layout module Transpiler include Layout::Dom + MAX_LIST_BOX_SIZE = 1000000 # A quick hack around the NULL pointer error to avoid messy orders. + class Builder property application : Gtk::Application? property window : Gtk::ApplicationWindow? @@ -330,46 +332,6 @@ 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, "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 # Do a little benchmark of how long it takes to build @@ -377,12 +339,11 @@ module Layout puts "Building components..." elapsed_time = Time.measure { build_components(structure) } puts "Finished. #{elapsed_text(elapsed_time)}" - @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 @@ -426,8 +387,23 @@ module Layout 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, MAX_LIST_BOX_SIZE) + when Gtk::ApplicationWindow + widget.add(component) + end + end + # ameba:disable Metrics/CyclomaticComplexity - private def build_widget(child, widget : Gtk::Widget) + private def transpile_component(child, widget : Gtk::Widget) case child when Text else @@ -478,14 +454,7 @@ module Layout @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 + containerize(widget, button, box_expand, box_fill, box_padding) button.on_event_after do |widget, event| case event.event_type @@ -516,6 +485,7 @@ module Layout 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| @@ -570,14 +540,7 @@ module Layout @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 + containerize(widget, entry, box_expand, box_fill, box_padding) entry.on_event_after do |widget, event| case event.event_type @@ -627,14 +590,7 @@ module Layout @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 + containerize(widget, switch, box_expand, box_fill, box_padding) switch.on_event_after do |widget, event| case event.event_type @@ -702,14 +658,7 @@ module Layout @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 + containerize(widget, image, box_expand, box_fill, box_padding) image.on_event_after do |widget, event| case event.event_type @@ -747,14 +696,7 @@ module Layout @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 + containerize(widget, label, box_expand, box_fill, box_padding) label.on_event_after do |widget, event| case event.event_type @@ -768,6 +710,45 @@ module Layout add_class_to_css(label, class_name) child.on_component_did_mount + when TextView + 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"]? || "") + text_view = Gtk::TextView.new(name: id, halign: horizontal_align, valign: vertical_align) + text_view.buffer.set_text(text, text.size) + + 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] = text_view.as(Pointer(LibGtk::Widget)) + end + + if id = child.attributes["id"]? + @components[id] = text_view.as(Pointer(LibGtk::Widget)) + end + + containerize(widget, text_view, box_expand, box_fill, box_padding) + + text_view.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(text_view, class_name) + child.on_component_did_mount when Tab id = child.attributes["id"]? || nil class_name = child.attributes["class"]? || nil @@ -777,7 +758,7 @@ module Layout tab = Gtk::Notebook.new(name: id, halign: horizontal_align, valign: vertical_align) child.children.each do |subchild| - build_widget(subchild, tab) + transpile_component(subchild, tab) end box_expand = child.attributes["boxExpand"]? || "false" @@ -796,14 +777,7 @@ module Layout @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 + containerize(widget, tab, box_expand, box_fill, box_padding) tab.on_event_after do |widget, event| case event.event_type @@ -844,7 +818,7 @@ module Layout 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) + transpile_component(subchild, box) end box.on_event_after do |widget, event| @@ -865,17 +839,319 @@ module Layout @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 + containerize(widget, box, box_expand, box_fill, box_padding) add_class_to_css(box, class_name) child.on_component_did_mount + when Frame + 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 = child.attributes["value"]? || "" + 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" + + frame = Gtk::Frame.new(name: id, label: value, halign: horizontal_align, valign: vertical_align) + + child.children.each do |subchild| + transpile_component(subchild, frame) + end + + frame.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] = frame.as(Pointer(LibGtk::Widget)) + end + + if id = child.attributes["id"]? + @components[id] = frame.as(Pointer(LibGtk::Widget)) + end + + containerize(widget, frame, box_expand, box_fill, box_padding) + + add_class_to_css(frame, class_name) + child.on_component_did_mount + when ScrolledWindow + 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"]? || "") + 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" + + scrolled_window = Gtk::ScrolledWindow.new(name: id, halign: horizontal_align, valign: vertical_align) + + child.children.each do |subchild| + transpile_component(subchild, scrolled_window) + end + + scrolled_window.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] = scrolled_window.as(Pointer(LibGtk::Widget)) + end + + if id = child.attributes["id"]? + @components[id] = scrolled_window.as(Pointer(LibGtk::Widget)) + end + + containerize(widget, scrolled_window, box_expand, box_fill, box_padding) + + add_class_to_css(scrolled_window, class_name) + child.on_component_did_mount + when VerticalSeparator + 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"]? || "") + + vertical_separator = Gtk::Separator.new(name: id, orientation: Gtk::Orientation::VERTICAL, 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 + + if class_id = child.attributes["classId"]? + @elements[class_id] = vertical_separator.as(Pointer(LibGtk::Widget)) + end + + if id = child.attributes["id"]? + @components[id] = vertical_separator.as(Pointer(LibGtk::Widget)) + end + + containerize(widget, vertical_separator, box_expand, box_fill, box_padding) + + vertical_separator.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(vertical_separator, class_name) + child.on_component_did_mount + when HorizontalSeparator + 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"]? || "") + + horizontal_separator = Gtk::Separator.new(name: id, orientation: Gtk::Orientation::HORIZONTAL, 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 + + if class_id = child.attributes["classId"]? + @elements[class_id] = horizontal_separator.as(Pointer(LibGtk::Widget)) + end + + if id = child.attributes["id"]? + @components[id] = horizontal_separator.as(Pointer(LibGtk::Widget)) + end + + containerize(widget, horizontal_separator, box_expand, box_fill, box_padding) + + horizontal_separator.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(horizontal_separator, class_name) + child.on_component_did_mount + when ListBox + 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 + 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" + + list_box = Gtk::ListBox.new(name: id, halign: horizontal_align, valign: vertical_align) + + child.children.each do |subchild| + transpile_component(subchild, list_box) + end + + list_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] = list_box.as(Pointer(LibGtk::Widget)) + end + + if id = child.attributes["id"]? + @components[id] = list_box.as(Pointer(LibGtk::Widget)) + end + + containerize(widget, list_box, box_expand, box_fill, box_padding) + + add_class_to_css(list_box, class_name) + child.on_component_did_mount + when Spinner + 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"]? || "") + + spinner = Gtk::Spinner.new( + name: id, + halign: horizontal_align, + valign: vertical_align, + active: 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] = spinner.as(Pointer(LibGtk::Widget)) + end + + if id = child.attributes["id"]? + @components[id] = spinner.as(Pointer(LibGtk::Widget)) + end + + containerize(widget, spinner, box_expand, box_fill, box_padding) + + spinner.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(spinner, class_name) + child.on_component_did_mount + when ProgressBar + id = child.attributes["id"]? || nil + class_name = child.attributes["class"]? || nil + + value = child.attributes["value"]? || "" + inverted = to_bool(child.attributes["inverted"]? || "false") + + horizontal_align = to_align(child.attributes["horizontalAlign"]? || "") + vertical_align = to_align(child.attributes["verticalAlign"]? || "") + + progress_bar = Gtk::ProgressBar.new( + name: id, + text: value, + inverted: inverted, + show_text: value.size != 0, + 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 + + if class_id = child.attributes["classId"]? + @elements[class_id] = progress_bar.as(Pointer(LibGtk::Widget)) + end + + if id = child.attributes["id"]? + @components[id] = progress_bar.as(Pointer(LibGtk::Widget)) + end + + containerize(widget, progress_bar, box_expand, box_fill, box_padding) + + progress_bar.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(progress_bar, class_name) + child.on_component_did_mount when EventBox nil else @@ -895,11 +1171,11 @@ module Layout end # ameba:disable Metrics/CyclomaticComplexity - private def build_widgets(parent, widget : Gtk::Widget) + private def transpile_components(parent, widget : Gtk::Widget) recursive_stylesheet_processing(parent) parent.children.each do |child| - build_widget(child, widget.not_nil!) + transpile_component(child, widget.not_nil!) end end @@ -976,7 +1252,7 @@ module Layout child.on_component_did_mount add_class_to_css(@window.not_nil!, class_name) - build_widgets(child, @window.not_nil!) + transpile_components(child, @window.not_nil!) else # TODO: Handle other non-crucial parts. end From d39ae886bdb54ba9a89f28ac03ba743bfba9eabe Mon Sep 17 00:00:00 2001 From: Giorgi Kavrelishvili Date: Fri, 4 Jun 2021 22:25:53 +0400 Subject: [PATCH 2/5] Updated the substitution code and made it re-usable --- src/layout/dom/application.cr | 21 +-------------------- src/layout/dom/box.cr | 21 +-------------------- src/layout/dom/button.cr | 21 +-------------------- src/layout/dom/element.cr | 4 ++++ src/layout/dom/entry.cr | 21 +-------------------- src/layout/dom/event_box.cr | 21 +-------------------- src/layout/dom/export.cr | 21 +-------------------- src/layout/dom/frame.cr | 21 +-------------------- src/layout/dom/horizontal_separator.cr | 21 +-------------------- src/layout/dom/image.cr | 21 +-------------------- src/layout/dom/import.cr | 21 +-------------------- src/layout/dom/label.cr | 21 +-------------------- src/layout/dom/list_box.cr | 21 +-------------------- src/layout/dom/progress_bar.cr | 21 +-------------------- src/layout/dom/script.cr | 22 ++-------------------- src/layout/dom/scrolled_window.cr | 21 +-------------------- src/layout/dom/spinner.cr | 21 +-------------------- src/layout/dom/style_sheet.cr | 21 +-------------------- src/layout/dom/switch.cr | 21 +-------------------- src/layout/dom/tab.cr | 21 +-------------------- src/layout/dom/text_view.cr | 21 +-------------------- src/layout/dom/vertical_separator.cr | 21 +-------------------- src/layout/dom/window.cr | 19 +------------------ 23 files changed, 27 insertions(+), 438 deletions(-) diff --git a/src/layout/dom/application.cr b/src/layout/dom/application.cr index 226ead2..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("__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}" - 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 f708713..72bc1e3 100644 --- a/src/layout/dom/box.cr +++ b/src/layout/dom/box.cr @@ -8,26 +8,7 @@ module Layout def initialize(@attributes, @children) @kind = "Box" - - @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("__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}" - end - end - end - end + substitution() end def to_html : String diff --git a/src/layout/dom/button.cr b/src/layout/dom/button.cr index c9d41f4..de6c7a0 100644 --- a/src/layout/dom/button.cr +++ b/src/layout/dom/button.cr @@ -10,26 +10,7 @@ module Layout def initialize(@attributes, @children) @kind = "Button" - - @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("__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}" - end - end - end - end + substitution() end def to_html : String diff --git a/src/layout/dom/element.cr b/src/layout/dom/element.cr index 3596583..b11ca88 100644 --- a/src/layout/dom/element.cr +++ b/src/layout/dom/element.cr @@ -9,6 +9,10 @@ module Layout getter :attributes def initialize(@kind, @attributes, @children = [] of Node) + substitution() + end + + def substitution @attributes.map do |key, value| matches = value.scan(/\${(.*?)}/) diff --git a/src/layout/dom/entry.cr b/src/layout/dom/entry.cr index b515832..63eb21f 100644 --- a/src/layout/dom/entry.cr +++ b/src/layout/dom/entry.cr @@ -11,26 +11,7 @@ module Layout def initialize(@attributes) @kind = "TextInput" @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("__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}" - end - end - end - end + substitution() end def to_html : String diff --git a/src/layout/dom/event_box.cr b/src/layout/dom/event_box.cr index d758ff9..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("__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}" - 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 8051696..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("__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}" - end - end - end - end + substitution() end def to_html : String diff --git a/src/layout/dom/frame.cr b/src/layout/dom/frame.cr index 0f6163b..ec0de96 100644 --- a/src/layout/dom/frame.cr +++ b/src/layout/dom/frame.cr @@ -8,26 +8,7 @@ module Layout def initialize(@attributes, @children) @kind = "Frame" - - @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("__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}" - end - end - end - end + substitution() end def to_html : String diff --git a/src/layout/dom/horizontal_separator.cr b/src/layout/dom/horizontal_separator.cr index 7fa27c7..e6eec62 100644 --- a/src/layout/dom/horizontal_separator.cr +++ b/src/layout/dom/horizontal_separator.cr @@ -11,26 +11,7 @@ module Layout def initialize(@attributes) @kind = "HorizontalSeparator" @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("__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}" - end - end - end - end + substitution() end def to_html : String diff --git a/src/layout/dom/image.cr b/src/layout/dom/image.cr index 71072d9..a4d3608 100644 --- a/src/layout/dom/image.cr +++ b/src/layout/dom/image.cr @@ -11,26 +11,7 @@ module Layout def initialize(@attributes) @kind = "Image" @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("__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}" - end - end - end - end + substitution() end def to_html : String diff --git a/src/layout/dom/import.cr b/src/layout/dom/import.cr index bbad5e3..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("__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}" - 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 e79fe1a..5433977 100644 --- a/src/layout/dom/label.cr +++ b/src/layout/dom/label.cr @@ -10,26 +10,7 @@ module Layout def initialize(@attributes, @children) @kind = "Label" - - @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("__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}" - end - end - end - end + substitution() end def to_html : String diff --git a/src/layout/dom/list_box.cr b/src/layout/dom/list_box.cr index e27b38a..400abae 100644 --- a/src/layout/dom/list_box.cr +++ b/src/layout/dom/list_box.cr @@ -8,26 +8,7 @@ module Layout def initialize(@attributes, @children) @kind = "ListBox" - - @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("__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}" - end - end - end - end + substitution() end def to_html : String diff --git a/src/layout/dom/progress_bar.cr b/src/layout/dom/progress_bar.cr index afcb150..085d3c1 100644 --- a/src/layout/dom/progress_bar.cr +++ b/src/layout/dom/progress_bar.cr @@ -11,26 +11,7 @@ module Layout def initialize(@attributes) @kind = "ProgressBar" @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("__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}" - end - end - end - end + substitution() end def to_html : String diff --git a/src/layout/dom/script.cr b/src/layout/dom/script.cr index 3f0d43c..9351ff1 100644 --- a/src/layout/dom/script.cr +++ b/src/layout/dom/script.cr @@ -11,26 +11,6 @@ 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 - - begin - @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}" - end - end - end - end - if source_file = @attributes["src"]? fp = File.open(source_file).gets_to_end Layout::Js::Engine::INSTANCE.evaluate(fp) @@ -61,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 index 2360c1a..e4fd13b 100644 --- a/src/layout/dom/scrolled_window.cr +++ b/src/layout/dom/scrolled_window.cr @@ -8,26 +8,7 @@ module Layout def initialize(@attributes, @children) @kind = "ScrolledWindow" - - @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("__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}" - end - end - end - end + substitution() end def to_html : String diff --git a/src/layout/dom/spinner.cr b/src/layout/dom/spinner.cr index 87e33e1..6f8638c 100644 --- a/src/layout/dom/spinner.cr +++ b/src/layout/dom/spinner.cr @@ -11,26 +11,7 @@ module Layout def initialize(@attributes) @kind = "Spinner" @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("__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}" - end - end - end - end + substitution() end def to_html : String diff --git a/src/layout/dom/style_sheet.cr b/src/layout/dom/style_sheet.cr index ccf8c2f..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("__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}" - 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 475fb9a..4bc9ea1 100644 --- a/src/layout/dom/switch.cr +++ b/src/layout/dom/switch.cr @@ -11,26 +11,7 @@ module Layout def initialize(@attributes) @kind = "Switch" @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("__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}" - end - end - end - end + substitution() end def to_html : String diff --git a/src/layout/dom/tab.cr b/src/layout/dom/tab.cr index 4d2c0d9..e45690c 100644 --- a/src/layout/dom/tab.cr +++ b/src/layout/dom/tab.cr @@ -8,26 +8,7 @@ module Layout def initialize(@attributes, @children) @kind = "Tab" - - @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("__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}" - end - end - end - end + substitution() end def to_html : String diff --git a/src/layout/dom/text_view.cr b/src/layout/dom/text_view.cr index 5630a68..1296b70 100644 --- a/src/layout/dom/text_view.cr +++ b/src/layout/dom/text_view.cr @@ -10,26 +10,7 @@ module Layout def initialize(@attributes, @children) @kind = "TextView" - - @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("__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}" - end - end - end - end + substitution() end def to_html : String diff --git a/src/layout/dom/vertical_separator.cr b/src/layout/dom/vertical_separator.cr index 276e774..10ddb3d 100644 --- a/src/layout/dom/vertical_separator.cr +++ b/src/layout/dom/vertical_separator.cr @@ -11,26 +11,7 @@ module Layout def initialize(@attributes) @kind = "VerticalSeparator" @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("__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}" - end - end - end - end + substitution() end def to_html : String diff --git a/src/layout/dom/window.cr b/src/layout/dom/window.cr index 4dc13de..e56bd45 100644 --- a/src/layout/dom/window.cr +++ b/src/layout/dom/window.cr @@ -10,24 +10,7 @@ module Layout def initialize(@attributes, @children) @kind = "Window" - - @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("__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}" - end - end - end - end + substitution() end def to_html : String From 6d504b54df12c0781ce3c3d6ca176e6991fe4a65 Mon Sep 17 00:00:00 2001 From: Giorgi Kavrelishvili Date: Sat, 5 Jun 2021 02:31:26 +0400 Subject: [PATCH 3/5] Refactor the comoponents and move them out of the single file --- example/dist/index.html | 64 +- example/dist/scripts/index.js | 12 +- example/dist/styles/index.css | 4 + src/layout/dom/box.cr | 47 + src/layout/dom/button.cr | 53 + src/layout/dom/element.cr | 76 +- src/layout/dom/entry.cr | 84 ++ src/layout/dom/frame.cr | 38 + src/layout/dom/horizontal_separator.cr | 37 + src/layout/dom/image.cr | 67 ++ src/layout/dom/label.cr | 36 + src/layout/dom/list_box.cr | 46 + src/layout/dom/scrolled_window.cr | 37 + src/layout/dom/switch.cr | 48 + src/layout/dom/tab.cr | 36 + src/layout/dom/text_view.cr | 37 + src/layout/dom/vertical_separator.cr | 37 + src/layout/dom/window.cr | 31 + .../component_not_found_exception.cr | 9 + src/layout/ext/debug.cr | 7 - src/layout/parser/tokenizer.cr | 1 - src/layout/transpiler/builder.cr | 1059 +---------------- src/layout/transpiler/component_storage.cr | 43 + 23 files changed, 813 insertions(+), 1096 deletions(-) create mode 100644 src/layout/exceptions/component_not_found_exception.cr delete mode 100644 src/layout/ext/debug.cr create mode 100644 src/layout/transpiler/component_storage.cr diff --git a/example/dist/index.html b/example/dist/index.html index 6ad7a23..7478ae7 100644 --- a/example/dist/index.html +++ b/example/dist/index.html @@ -1,66 +1,66 @@ - + - - + + - - - + - - + + - + - - + + - + - - + + - - + + - + - - - + + + - + - - - + + + @@ -70,11 +70,11 @@ - + - - + + - + \ No newline at end of file diff --git a/example/dist/scripts/index.js b/example/dist/scripts/index.js index 2440265..b3e10f8 100644 --- a/example/dist/scripts/index.js +++ b/example/dist/scripts/index.js @@ -12,20 +12,10 @@ const appConfiguration = { height: 800, title: "Monkey Shoulder" } -} +}; if (fs.fileExists("./config.json")) { appConfiguration = JSON.parse(fs.readFile("./config.json")) } else { fs.writeFile("./config.json", JSON.stringify(appConfiguration, null, 2)) } - -function updateNumberOne(buffer) { - var element = getElementById("numberOneLabel"); - element.setText(buffer); -} - -function updateNumberTwo(buffer) { - var element = getElementById("numberTwoLabel"); - element.setText(buffer); -} \ No newline at end of file diff --git a/example/dist/styles/index.css b/example/dist/styles/index.css index 49f4ec3..4ad3fe4 100644 --- a/example/dist/styles/index.css +++ b/example/dist/styles/index.css @@ -1,3 +1,7 @@ +.main-window { + background-color: black; +} + .main-box { transition: all 1s ease; background-color: rgb(28, 30, 39); diff --git a/src/layout/dom/box.cr b/src/layout/dom/box.cr index 72bc1e3..400616a 100644 --- a/src/layout/dom/box.cr +++ b/src/layout/dom/box.cr @@ -11,6 +11,53 @@ module Layout 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 + + 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) + + 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, 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 attrs = attributes.map do |key, value| "#{key}='#{value}'" diff --git a/src/layout/dom/button.cr b/src/layout/dom/button.cr index de6c7a0..2667490 100644 --- a/src/layout/dom/button.cr +++ b/src/layout/dom/button.cr @@ -13,6 +13,59 @@ module Layout 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 + + 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) + + button.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 + + button.on_clicked do + Layout::Js::Engine::INSTANCE.evaluate("#{on_click}(getElementByComponentId(\"#{@cid}\"))") + end + + add_class_to_css(button, class_name) + + containerize(widget, button, box_expand, box_fill, box_padding) + component_storage.store(id, button) + component_storage.store(@cid, button) + did_mount(@cid) + + button + end + def to_html : String attrs = attributes.map do |key, value| "#{key}='#{value}'" diff --git a/src/layout/dom/element.cr b/src/layout/dom/element.cr index b11ca88..7418670 100644 --- a/src/layout/dom/element.cr +++ b/src/layout/dom/element.cr @@ -7,6 +7,7 @@ module Layout @attributes : Hash(String, String) getter :attributes + getter cid : String = UUID.random.hexstring def initialize(@kind, @attributes, @children = [] of Node) substitution() @@ -34,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 63eb21f..282d569 100644 --- a/src/layout/dom/entry.cr +++ b/src/layout/dom/entry.cr @@ -14,6 +14,90 @@ module Layout 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 + + entry.buffer.on_deleted_text do |buffer| + if text_changed + Layout::Js::Engine::INSTANCE.evaluate("#{text_changed}(getElementByComponentId(\"#{@cid}\"), \"#{buffer.text}\")") + end + end + + 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 + + containerize(widget, entry, box_expand, box_fill, box_padding) + + entry.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 + + 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 attrs = attributes.map do |key, value| "#{key}='#{value}'" diff --git a/src/layout/dom/frame.cr b/src/layout/dom/frame.cr index ec0de96..0500743 100644 --- a/src/layout/dom/frame.cr +++ b/src/layout/dom/frame.cr @@ -11,6 +11,44 @@ module Layout 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 + + spacing = @attributes["spacing"]? || "2" + + 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}'" diff --git a/src/layout/dom/horizontal_separator.cr b/src/layout/dom/horizontal_separator.cr index e6eec62..623a17f 100644 --- a/src/layout/dom/horizontal_separator.cr +++ b/src/layout/dom/horizontal_separator.cr @@ -14,6 +14,43 @@ module Layout 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 + + containerize(widget, horizontal_separator, box_expand, box_fill, box_padding) + + 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 + + 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}'" diff --git a/src/layout/dom/image.cr b/src/layout/dom/image.cr index a4d3608..614056e 100644 --- a/src/layout/dom/image.cr +++ b/src/layout/dom/image.cr @@ -14,6 +14,73 @@ module Layout 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 + + horizontal_align = to_align(@attributes["horizontalAlign"]? || "") + vertical_align = to_align(@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 = @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 + + containerize(widget, image, box_expand, box_fill, box_padding) + + image.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 + + 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 attrs = attributes.map do |key, value| "#{key}='#{value}'" diff --git a/src/layout/dom/label.cr b/src/layout/dom/label.cr index 5433977..982c682 100644 --- a/src/layout/dom/label.cr +++ b/src/layout/dom/label.cr @@ -13,6 +13,42 @@ module Layout 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" + + if box_padding.includes?(".0") + box_padding = box_padding[..box_padding.size - 3] + end + + containerize(widget, label, box_expand, box_fill, box_padding) + + label.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 + + 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 attrs = attributes.map do |key, value| "#{key}='#{value}'" diff --git a/src/layout/dom/list_box.cr b/src/layout/dom/list_box.cr index 400abae..1b55307 100644 --- a/src/layout/dom/list_box.cr +++ b/src/layout/dom/list_box.cr @@ -11,6 +11,52 @@ module Layout 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"]? || "") + case @attributes["orientation"]? + when "vertical" + orientation = Gtk::Orientation::VERTICAL + when "horizontal" + orientation = Gtk::Orientation::HORIZONTAL + else + orientation = Gtk::Orientation::VERTICAL + 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 + + spacing = @attributes["spacing"]? || "2" + + 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}'" diff --git a/src/layout/dom/scrolled_window.cr b/src/layout/dom/scrolled_window.cr index e4fd13b..b968ca3 100644 --- a/src/layout/dom/scrolled_window.cr +++ b/src/layout/dom/scrolled_window.cr @@ -11,6 +11,43 @@ module Layout 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 + + spacing = @attributes["spacing"]? || "2" + + 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}'" diff --git a/src/layout/dom/switch.cr b/src/layout/dom/switch.cr index 4bc9ea1..9a2b527 100644 --- a/src/layout/dom/switch.cr +++ b/src/layout/dom/switch.cr @@ -14,6 +14,54 @@ module Layout 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) + + 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 + + switch.on_state_set do + if value_change + Layout::Js::Engine::INSTANCE.evaluate("#{value_change}(getElementByComponentId(\"#{@cid}\"), #{switch.active})") + end + + true + end + + containerize(widget, switch, box_expand, box_fill, box_padding) + + switch.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 + + 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 attrs = attributes.map do |key, value| "#{key}='#{value}'" diff --git a/src/layout/dom/tab.cr b/src/layout/dom/tab.cr index e45690c..acd1ab8 100644 --- a/src/layout/dom/tab.cr +++ b/src/layout/dom/tab.cr @@ -11,6 +11,42 @@ module Layout 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"]? || "") + + tab = Gtk::Notebook.new(name: id, 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 + + containerize(widget, tab, box_expand, box_fill, box_padding) + + tab.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 + + 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 attrs = attributes.map do |key, value| "#{key}='#{value}'" diff --git a/src/layout/dom/text_view.cr b/src/layout/dom/text_view.cr index 1296b70..d0ae28e 100644 --- a/src/layout/dom/text_view.cr +++ b/src/layout/dom/text_view.cr @@ -13,6 +13,43 @@ module Layout 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 + + containerize(widget, text_view, box_expand, box_fill, box_padding) + + 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 + + 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}'" diff --git a/src/layout/dom/vertical_separator.cr b/src/layout/dom/vertical_separator.cr index 10ddb3d..104d664 100644 --- a/src/layout/dom/vertical_separator.cr +++ b/src/layout/dom/vertical_separator.cr @@ -14,6 +14,43 @@ module Layout 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 + + containerize(widget, vertical_separator, box_expand, box_fill, box_padding) + + 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 + + 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}'" diff --git a/src/layout/dom/window.cr b/src/layout/dom/window.cr index e56bd45..ab4953f 100644 --- a/src/layout/dom/window.cr +++ b/src/layout/dom/window.cr @@ -13,6 +13,37 @@ module Layout 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 + + 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 attrs = attributes.map do |key, value| "#{key}='#{value}'" 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..166c5a9 --- /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/ext/debug.cr b/src/layout/ext/debug.cr deleted file mode 100644 index 3afb6dd..0000000 --- a/src/layout/ext/debug.cr +++ /dev/null @@ -1,7 +0,0 @@ -module Duktape - module API::Debug - def dump! - puts("STACK: #{stack}") - end - end -end diff --git a/src/layout/parser/tokenizer.cr b/src/layout/parser/tokenizer.cr index 4cf1e3c..9c7d399 100644 --- a/src/layout/parser/tokenizer.cr +++ b/src/layout/parser/tokenizer.cr @@ -212,7 +212,6 @@ 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) diff --git a/src/layout/transpiler/builder.cr b/src/layout/transpiler/builder.cr index 0a8be93..fb49655 100644 --- a/src/layout/transpiler/builder.cr +++ b/src/layout/transpiler/builder.cr @@ -5,13 +5,13 @@ module Layout module Transpiler include Layout::Dom - MAX_LIST_BOX_SIZE = 1000000 # A quick hack around the NULL pointer error to avoid messy orders. - 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 def build_from_document(document) File.open(document) do |fd| @@ -24,173 +24,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_global_proc("getElementByClassId", 1) do |ptr| - env = Duktape::Sandbox.new(ptr) - env.push_heap_stash - env.get_prop_string(-1, "getElementByClassIdClosure") - 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, "getElementByIdClosure") + context.push_pointer(::Box.box(get_element_by_component_id_proc)) + context.put_prop_string(-2, "getElementByComponentId") - context.push_global_proc("getElementById", 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, "getElementByIdClosure") + 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) @@ -332,14 +179,25 @@ module Layout 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)}" - @window.try(&.show_all) + 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) end) else # TODO: Refactor this later to an actual error message. @@ -355,805 +213,17 @@ 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 - - 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, MAX_LIST_BOX_SIZE) - when Gtk::ApplicationWindow - widget.add(component) - end - end - # ameba:disable Metrics/CyclomaticComplexity 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 - - containerize(widget, button, box_expand, box_fill, box_padding) - - 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 - - containerize(widget, entry, box_expand, box_fill, box_padding) - - 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 - - containerize(widget, switch, box_expand, box_fill, box_padding) - - 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 - - containerize(widget, image, box_expand, box_fill, box_padding) - - 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 - - containerize(widget, label, box_expand, box_fill, box_padding) - - 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 TextView - 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"]? || "") - text_view = Gtk::TextView.new(name: id, halign: horizontal_align, valign: vertical_align) - text_view.buffer.set_text(text, text.size) - - 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] = text_view.as(Pointer(LibGtk::Widget)) - end - - if id = child.attributes["id"]? - @components[id] = text_view.as(Pointer(LibGtk::Widget)) - end - - containerize(widget, text_view, box_expand, box_fill, box_padding) - - text_view.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(text_view, 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| - transpile_component(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 - - containerize(widget, tab, box_expand, box_fill, box_padding) - - 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| - transpile_component(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 - - containerize(widget, box, box_expand, box_fill, box_padding) - - add_class_to_css(box, class_name) - child.on_component_did_mount - when Frame - 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 = child.attributes["value"]? || "" - 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" - - frame = Gtk::Frame.new(name: id, label: value, halign: horizontal_align, valign: vertical_align) - - child.children.each do |subchild| - transpile_component(subchild, frame) - end - - frame.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] = frame.as(Pointer(LibGtk::Widget)) - end - - if id = child.attributes["id"]? - @components[id] = frame.as(Pointer(LibGtk::Widget)) - end - - containerize(widget, frame, box_expand, box_fill, box_padding) - - add_class_to_css(frame, class_name) - child.on_component_did_mount - when ScrolledWindow - 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"]? || "") - 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" - - scrolled_window = Gtk::ScrolledWindow.new(name: id, halign: horizontal_align, valign: vertical_align) - - child.children.each do |subchild| - transpile_component(subchild, scrolled_window) - end - - scrolled_window.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] = scrolled_window.as(Pointer(LibGtk::Widget)) - end - - if id = child.attributes["id"]? - @components[id] = scrolled_window.as(Pointer(LibGtk::Widget)) - end - - containerize(widget, scrolled_window, box_expand, box_fill, box_padding) - - add_class_to_css(scrolled_window, class_name) - child.on_component_did_mount - when VerticalSeparator - 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"]? || "") - - vertical_separator = Gtk::Separator.new(name: id, orientation: Gtk::Orientation::VERTICAL, 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 - - if class_id = child.attributes["classId"]? - @elements[class_id] = vertical_separator.as(Pointer(LibGtk::Widget)) - end - - if id = child.attributes["id"]? - @components[id] = vertical_separator.as(Pointer(LibGtk::Widget)) - end - - containerize(widget, vertical_separator, box_expand, box_fill, box_padding) - - vertical_separator.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(vertical_separator, class_name) - child.on_component_did_mount - when HorizontalSeparator - 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"]? || "") - - horizontal_separator = Gtk::Separator.new(name: id, orientation: Gtk::Orientation::HORIZONTAL, 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 - - if class_id = child.attributes["classId"]? - @elements[class_id] = horizontal_separator.as(Pointer(LibGtk::Widget)) - end - - if id = child.attributes["id"]? - @components[id] = horizontal_separator.as(Pointer(LibGtk::Widget)) - end - - containerize(widget, horizontal_separator, box_expand, box_fill, box_padding) - - horizontal_separator.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(horizontal_separator, class_name) - child.on_component_did_mount - when ListBox - 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 - 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" - - list_box = Gtk::ListBox.new(name: id, halign: horizontal_align, valign: vertical_align) - - child.children.each do |subchild| - transpile_component(subchild, list_box) - end - - list_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] = list_box.as(Pointer(LibGtk::Widget)) - end - - if id = child.attributes["id"]? - @components[id] = list_box.as(Pointer(LibGtk::Widget)) - end - - containerize(widget, list_box, box_expand, box_fill, box_padding) - - add_class_to_css(list_box, class_name) - child.on_component_did_mount - when Spinner - 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"]? || "") - - spinner = Gtk::Spinner.new( - name: id, - halign: horizontal_align, - valign: vertical_align, - active: 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] = spinner.as(Pointer(LibGtk::Widget)) - end - - if id = child.attributes["id"]? - @components[id] = spinner.as(Pointer(LibGtk::Widget)) - end - - containerize(widget, spinner, box_expand, box_fill, box_padding) - - spinner.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(spinner, class_name) - child.on_component_did_mount - when ProgressBar - id = child.attributes["id"]? || nil - class_name = child.attributes["class"]? || nil - - value = child.attributes["value"]? || "" - inverted = to_bool(child.attributes["inverted"]? || "false") - - horizontal_align = to_align(child.attributes["horizontalAlign"]? || "") - vertical_align = to_align(child.attributes["verticalAlign"]? || "") - - progress_bar = Gtk::ProgressBar.new( - name: id, - text: value, - inverted: inverted, - show_text: value.size != 0, - 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 - - if class_id = child.attributes["classId"]? - @elements[class_id] = progress_bar.as(Pointer(LibGtk::Widget)) - end - - if id = child.attributes["id"]? - @components[id] = progress_bar.as(Pointer(LibGtk::Widget)) - end - - containerize(widget, progress_bar, box_expand, box_fill, box_padding) - - progress_bar.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(progress_bar, 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 @@ -1175,7 +245,7 @@ module Layout recursive_stylesheet_processing(parent) parent.children.each do |child| - transpile_component(child, widget.not_nil!) + transpile_component(child, widget) end end @@ -1187,72 +257,15 @@ module Layout Gtk::StyleContext.add_provider_for_screen screen, css_provider, Gtk::STYLE_PROVIDER_PRIORITY_APPLICATION end - private def build_components(document) + 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) - transpile_components(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..ee0780a --- /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]? + return 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 From 8e679591080be2c0c3aa94aac1a1fcb615442776 Mon Sep 17 00:00:00 2001 From: Giorgi Kavrelishvili Date: Sun, 6 Jun 2021 18:58:40 +0400 Subject: [PATCH 4/5] Stash changes --- example/dist/index.html | 51 ++++++++++++++++++++++++-------- src/layout/transpiler/builder.cr | 1 - 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/example/dist/index.html b/example/dist/index.html index 7478ae7..4c24a3e 100644 --- a/example/dist/index.html +++ b/example/dist/index.html @@ -2,7 +2,8 @@ - + - - + + diff --git a/src/layout/transpiler/builder.cr b/src/layout/transpiler/builder.cr index fb49655..eaf1d53 100644 --- a/src/layout/transpiler/builder.cr +++ b/src/layout/transpiler/builder.cr @@ -213,7 +213,6 @@ module Layout "#{(millis * 1000).round(2)}µs" end - # ameba:disable Metrics/CyclomaticComplexity private def transpile_component(child, widget : Gtk::Widget) case child when Box, Frame, Tab, ListBox, ScrolledWindow From d3ad9340ce4f9c93cb65fbc3fc0ff8d7df50e56b Mon Sep 17 00:00:00 2001 From: Giorgi Kavrelishvili Date: Wed, 21 Jul 2021 08:29:13 +0400 Subject: [PATCH 5/5] Add multiple features, refactor the builder and move most of the code out to the components instead of a single file, add WIP components which will be worked on later. --- .ameba.yml | 15 ++++++ example/dist/index.html | 53 +++++-------------- example/dist/scripts/index.js | 20 +++++-- shard.yml | 5 ++ src/layout/dom/box.cr | 3 +- src/layout/dom/button.cr | 5 +- src/layout/dom/entry.cr | 5 +- src/layout/dom/frame.cr | 5 +- src/layout/dom/horizontal_separator.cr | 5 +- src/layout/dom/image.cr | 8 ++- src/layout/dom/label.cr | 5 +- src/layout/dom/list_box.cr | 13 +---- src/layout/dom/scrolled_window.cr | 5 +- src/layout/dom/switch.cr | 5 +- src/layout/dom/tab.cr | 5 +- src/layout/dom/text_view.cr | 5 +- src/layout/dom/vertical_separator.cr | 6 +-- .../component_not_found_exception.cr | 2 +- .../exceptions/invalid_component_exception.cr | 19 +++++++ .../exceptions/invalid_element_exception.cr | 9 ---- src/layout/exceptions/parser_exception.cr | 2 +- src/layout/js/engine.cr | 3 +- src/layout/js/std/net.cr | 2 +- src/layout/parser/tokenizer.cr | 8 +-- src/layout/transpiler/builder.cr | 49 +++++++++-------- src/layout/transpiler/component_storage.cr | 2 +- src/layout/version.cr | 2 +- 27 files changed, 127 insertions(+), 139 deletions(-) create mode 100644 .ameba.yml create mode 100644 src/layout/exceptions/invalid_component_exception.cr delete mode 100644 src/layout/exceptions/invalid_element_exception.cr 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/example/dist/index.html b/example/dist/index.html index 4c24a3e..e3d08eb 100644 --- a/example/dist/index.html +++ b/example/dist/index.html @@ -1,9 +1,8 @@ - + - + - - + + diff --git a/example/dist/scripts/index.js b/example/dist/scripts/index.js index b3e10f8..194270d 100644 --- a/example/dist/scripts/index.js +++ b/example/dist/scripts/index.js @@ -14,8 +14,18 @@ const appConfiguration = { } }; -if (fs.fileExists("./config.json")) { - appConfiguration = JSON.parse(fs.readFile("./config.json")) -} else { - fs.writeFile("./config.json", JSON.stringify(appConfiguration, null, 2)) -} +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/shard.yml b/shard.yml index d7c9c52..d7843e4 100644 --- a/shard.yml +++ b/shard.yml @@ -19,6 +19,11 @@ dependencies: github: grkek/beautify branch: master +development_dependencies: + ameba: + github: crystal-ameba/ameba + version: ~> 0.13.0 + crystal: 1.0.0 license: MIT diff --git a/src/layout/dom/box.cr b/src/layout/dom/box.cr index 400616a..ba1053e 100644 --- a/src/layout/dom/box.cr +++ b/src/layout/dom/box.cr @@ -38,7 +38,7 @@ module Layout box = Gtk::Box.new(name: id, orientation: orientation, spacing: spacing.to_i, halign: horizontal_align, valign: vertical_align) - box.on_event_after do |widget, event| + box.on_event_after do |_widget, event| case event.event_type when Gdk::EventType::MOTION_NOTIFY false @@ -49,7 +49,6 @@ module Layout 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) diff --git a/src/layout/dom/button.cr b/src/layout/dom/button.cr index 2667490..d7fd283 100644 --- a/src/layout/dom/button.cr +++ b/src/layout/dom/button.cr @@ -42,7 +42,7 @@ module Layout button = Gtk::Button.new(name: id, label: text, relief: relief_style, halign: horizontal_align, valign: vertical_align) - button.on_event_after do |widget, event| + button.on_event_after do |_widget, event| case event.event_type when Gdk::EventType::MOTION_NOTIFY false @@ -56,9 +56,8 @@ module Layout Layout::Js::Engine::INSTANCE.evaluate("#{on_click}(getElementByComponentId(\"#{@cid}\"))") end - add_class_to_css(button, class_name) - 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) diff --git a/src/layout/dom/entry.cr b/src/layout/dom/entry.cr index 282d569..c44b299 100644 --- a/src/layout/dom/entry.cr +++ b/src/layout/dom/entry.cr @@ -78,9 +78,7 @@ module Layout box_padding = box_padding[..box_padding.size - 3] end - containerize(widget, entry, box_expand, box_fill, box_padding) - - entry.on_event_after do |widget, event| + entry.on_event_after do |_widget, event| case event.event_type when Gdk::EventType::MOTION_NOTIFY false @@ -90,6 +88,7 @@ module Layout 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) diff --git a/src/layout/dom/frame.cr b/src/layout/dom/frame.cr index 0500743..1776f57 100644 --- a/src/layout/dom/frame.cr +++ b/src/layout/dom/frame.cr @@ -25,11 +25,9 @@ module Layout box_padding = box_padding[..box_padding.size - 3] end - spacing = @attributes["spacing"]? || "2" - frame = Gtk::Frame.new(name: id, label: value, halign: horizontal_align, valign: vertical_align) - frame.on_event_after do |widget, event| + frame.on_event_after do |_widget, event| case event.event_type when Gdk::EventType::MOTION_NOTIFY false @@ -40,7 +38,6 @@ module Layout 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) diff --git a/src/layout/dom/horizontal_separator.cr b/src/layout/dom/horizontal_separator.cr index 623a17f..9a2c104 100644 --- a/src/layout/dom/horizontal_separator.cr +++ b/src/layout/dom/horizontal_separator.cr @@ -31,9 +31,7 @@ module Layout box_padding = box_padding[..box_padding.size - 3] end - containerize(widget, horizontal_separator, box_expand, box_fill, box_padding) - - horizontal_separator.on_event_after do |widget, event| + horizontal_separator.on_event_after do |_widget, event| case event.event_type when Gdk::EventType::MOTION_NOTIFY false @@ -43,6 +41,7 @@ module Layout 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) diff --git a/src/layout/dom/image.cr b/src/layout/dom/image.cr index 614056e..994efce 100644 --- a/src/layout/dom/image.cr +++ b/src/layout/dom/image.cr @@ -22,7 +22,7 @@ module Layout width = @attributes["width"]? || "256" height = @attributes["height"]? || "256" - preserve_aspect_ration = @attributes["preserveAspectRation"]? || "true" + # preserve_aspect_ration = @attributes["preserveAspectRation"]? || "true" if width.includes?(".0") width = width[..width.size - 3] @@ -60,9 +60,7 @@ module Layout box_padding = box_padding[..box_padding.size - 3] end - containerize(widget, image, box_expand, box_fill, box_padding) - - image.on_event_after do |widget, event| + image.on_event_after do |_widget, event| case event.event_type when Gdk::EventType::MOTION_NOTIFY false @@ -72,8 +70,8 @@ module Layout 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) diff --git a/src/layout/dom/label.cr b/src/layout/dom/label.cr index 982c682..b24b39b 100644 --- a/src/layout/dom/label.cr +++ b/src/layout/dom/label.cr @@ -29,9 +29,7 @@ module Layout box_padding = box_padding[..box_padding.size - 3] end - containerize(widget, label, box_expand, box_fill, box_padding) - - label.on_event_after do |widget, event| + label.on_event_after do |_widget, event| case event.event_type when Gdk::EventType::MOTION_NOTIFY false @@ -41,6 +39,7 @@ module Layout 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) diff --git a/src/layout/dom/list_box.cr b/src/layout/dom/list_box.cr index 1b55307..821e580 100644 --- a/src/layout/dom/list_box.cr +++ b/src/layout/dom/list_box.cr @@ -16,14 +16,6 @@ module Layout class_name = @attributes["className"]? || nil horizontal_align = to_align(@attributes["horizontalAlign"]? || "") vertical_align = to_align(@attributes["verticalAlign"]? || "") - case @attributes["orientation"]? - when "vertical" - orientation = Gtk::Orientation::VERTICAL - when "horizontal" - orientation = Gtk::Orientation::HORIZONTAL - else - orientation = Gtk::Orientation::VERTICAL - end box_expand = @attributes["boxExpand"]? || "false" box_fill = @attributes["boxFill"]? || "false" @@ -33,11 +25,9 @@ module Layout box_padding = box_padding[..box_padding.size - 3] end - spacing = @attributes["spacing"]? || "2" - list_box = Gtk::ListBox.new(name: id, halign: horizontal_align, valign: vertical_align) - list_box.on_event_after do |widget, event| + list_box.on_event_after do |_widget, event| case event.event_type when Gdk::EventType::MOTION_NOTIFY false @@ -48,7 +38,6 @@ module Layout 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) diff --git a/src/layout/dom/scrolled_window.cr b/src/layout/dom/scrolled_window.cr index b968ca3..3ce0c34 100644 --- a/src/layout/dom/scrolled_window.cr +++ b/src/layout/dom/scrolled_window.cr @@ -24,11 +24,9 @@ module Layout box_padding = box_padding[..box_padding.size - 3] end - spacing = @attributes["spacing"]? || "2" - scrolled_window = Gtk::ScrolledWindow.new(name: id, halign: horizontal_align, valign: vertical_align) - scrolled_window.on_event_after do |widget, event| + scrolled_window.on_event_after do |_widget, event| case event.event_type when Gdk::EventType::MOTION_NOTIFY false @@ -39,7 +37,6 @@ module Layout 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) diff --git a/src/layout/dom/switch.cr b/src/layout/dom/switch.cr index 9a2b527..12ffd7e 100644 --- a/src/layout/dom/switch.cr +++ b/src/layout/dom/switch.cr @@ -42,9 +42,7 @@ module Layout true end - containerize(widget, switch, box_expand, box_fill, box_padding) - - switch.on_event_after do |widget, event| + switch.on_event_after do |_widget, event| case event.event_type when Gdk::EventType::MOTION_NOTIFY false @@ -54,6 +52,7 @@ module Layout 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) diff --git a/src/layout/dom/tab.cr b/src/layout/dom/tab.cr index acd1ab8..189ef07 100644 --- a/src/layout/dom/tab.cr +++ b/src/layout/dom/tab.cr @@ -27,9 +27,7 @@ module Layout box_padding = box_padding[..box_padding.size - 3] end - containerize(widget, tab, box_expand, box_fill, box_padding) - - tab.on_event_after do |widget, event| + tab.on_event_after do |_widget, event| case event.event_type when Gdk::EventType::MOTION_NOTIFY false @@ -39,6 +37,7 @@ module Layout 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) diff --git a/src/layout/dom/text_view.cr b/src/layout/dom/text_view.cr index d0ae28e..fb6c86a 100644 --- a/src/layout/dom/text_view.cr +++ b/src/layout/dom/text_view.cr @@ -30,9 +30,7 @@ module Layout box_padding = box_padding[..box_padding.size - 3] end - containerize(widget, text_view, box_expand, box_fill, box_padding) - - text_view.on_event_after do |widget, event| + text_view.on_event_after do |_widget, event| case event.event_type when Gdk::EventType::MOTION_NOTIFY false @@ -42,6 +40,7 @@ module Layout 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) diff --git a/src/layout/dom/vertical_separator.cr b/src/layout/dom/vertical_separator.cr index 104d664..5a899c7 100644 --- a/src/layout/dom/vertical_separator.cr +++ b/src/layout/dom/vertical_separator.cr @@ -31,9 +31,7 @@ module Layout box_padding = box_padding[..box_padding.size - 3] end - containerize(widget, vertical_separator, box_expand, box_fill, box_padding) - - vertical_separator.on_event_after do |widget, event| + vertical_separator.on_event_after do |_widget, event| case event.event_type when Gdk::EventType::MOTION_NOTIFY false @@ -43,6 +41,8 @@ module Layout 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) diff --git a/src/layout/exceptions/component_not_found_exception.cr b/src/layout/exceptions/component_not_found_exception.cr index 166c5a9..7b2f3f3 100644 --- a/src/layout/exceptions/component_not_found_exception.cr +++ b/src/layout/exceptions/component_not_found_exception.cr @@ -2,7 +2,7 @@ module Layout module Exceptions class ComponentNotFoundException < Exception def initialize(cid) - super("Component #{cid} was not found") + super("Component #{cid} was not found!") 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/js/engine.cr b/src/layout/js/engine.cr index 8923c1e..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() - @runtime.context.eval! <<-JS + context.eval! <<-JS String.prototype.format = function() { var formatted = this; for( var arg in arguments ) { 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/parser/tokenizer.cr b/src/layout/parser/tokenizer.cr index 9c7d399..ad4a95a 100644 --- a/src/layout/parser/tokenizer.cr +++ b/src/layout/parser/tokenizer.cr @@ -119,9 +119,6 @@ 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" @@ -168,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 @@ -181,7 +178,6 @@ module Layout case tag_name when "Script" Layout::Dom::Script.new(attrs, children) - nil when "Application" Layout::Dom::Application.new(attrs, children) @@ -214,7 +210,7 @@ module Layout child.children.concat(children) 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 eaf1d53..4c517cf 100644 --- a/src/layout/transpiler/builder.cr +++ b/src/layout/transpiler/builder.cr @@ -13,6 +13,7 @@ module Layout @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) @@ -90,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)) @@ -102,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)) @@ -114,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)) @@ -126,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)) @@ -137,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)) @@ -154,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)) @@ -206,6 +207,7 @@ module Layout 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 @@ -213,6 +215,8 @@ module Layout "#{(millis * 1000).round(2)}µs" end + # 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 Box, Frame, Tab, ListBox, ScrolledWindow @@ -228,6 +232,16 @@ module Layout 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 @@ -239,15 +253,7 @@ module Layout end end - # ameba:disable Metrics/CyclomaticComplexity - private def transpile_components(parent, widget : Gtk::Widget) - recursive_stylesheet_processing(parent) - - parent.children.each do |child| - transpile_component(child, widget) - 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"]) @@ -256,6 +262,7 @@ module Layout Gtk::StyleContext.add_provider_for_screen screen, css_provider, Gtk::STYLE_PROVIDER_PRIORITY_APPLICATION end + # 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 diff --git a/src/layout/transpiler/component_storage.cr b/src/layout/transpiler/component_storage.cr index ee0780a..cb6675d 100644 --- a/src/layout/transpiler/component_storage.cr +++ b/src/layout/transpiler/component_storage.cr @@ -17,7 +17,7 @@ module Layout def retrieve(cid : String) : Pointer(LibGtk::Widget) if widget = @components[cid]? - return widget.not_nil! + widget.not_nil! else raise Exceptions::ComponentNotFoundException.new(cid) 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