p, pre, br, b, img, separator, i, u, small, mark, code tags
2025-07-22 15:34:42 +03:00
|
|
|
class_name Main
|
2025-07-20 13:45:07 +03:00
|
|
|
extends Control
|
|
|
|
|
|
p, pre, br, b, img, separator, i, u, small, mark, code tags
2025-07-22 15:34:42 +03:00
|
|
|
@onready var website_container: Control = %WebsiteContainer
|
|
|
|
|
@onready var tab_container: TabManager = $VBoxContainer/TabContainer
|
|
|
|
|
const LOADER_CIRCLE = preload("res://Assets/Icons/loader-circle.svg")
|
|
|
|
|
|
|
|
|
|
var loading_tween: Tween
|
|
|
|
|
|
|
|
|
|
const P = preload("res://Scenes/Tags/p.tscn")
|
|
|
|
|
const IMG = preload("res://Scenes/Tags/img.tscn")
|
|
|
|
|
const SEPARATOR = preload("res://Scenes/Tags/separator.tscn")
|
|
|
|
|
const PRE = preload("res://Scenes/Tags/pre.tscn")
|
|
|
|
|
const BR = preload("res://Scenes/Tags/br.tscn")
|
2025-07-22 21:15:57 +03:00
|
|
|
const SPAN = preload("res://Scenes/Tags/span.tscn")
|
2025-07-23 13:35:42 +03:00
|
|
|
const H1 = preload("res://Scenes/Tags/h1.tscn")
|
|
|
|
|
const H2 = preload("res://Scenes/Tags/h2.tscn")
|
|
|
|
|
const H3 = preload("res://Scenes/Tags/h3.tscn")
|
|
|
|
|
const H4 = preload("res://Scenes/Tags/h4.tscn")
|
|
|
|
|
const H5 = preload("res://Scenes/Tags/h5.tscn")
|
|
|
|
|
const H6 = preload("res://Scenes/Tags/h6.tscn")
|
2025-07-20 13:45:07 +03:00
|
|
|
|
|
|
|
|
func render():
|
p, pre, br, b, img, separator, i, u, small, mark, code tags
2025-07-22 15:34:42 +03:00
|
|
|
# Clear existing content
|
|
|
|
|
for child in website_container.get_children():
|
|
|
|
|
child.queue_free()
|
|
|
|
|
|
2025-07-20 13:45:07 +03:00
|
|
|
var html_bytes = "<head>
|
|
|
|
|
<title>My cool web</title>
|
2025-07-22 21:15:57 +03:00
|
|
|
<icon src=\"https://upload.wikimedia.org/wikipedia/commons/thumb/c/c1/Google_%22G%22_logo.svg/768px-Google_%22G%22_logo.svg.png\">
|
2025-07-20 13:45:07 +03:00
|
|
|
|
|
|
|
|
<meta name=\"theme-color\" content=\"#000000\">
|
|
|
|
|
<meta name=\"description\" content=\"My cool web\">
|
|
|
|
|
|
|
|
|
|
<style href=\"styles.css\">
|
|
|
|
|
<script src=\"script.lua\" />
|
|
|
|
|
</head>
|
|
|
|
|
|
|
|
|
|
<body>
|
2025-07-23 13:35:42 +03:00
|
|
|
<h1>Header 1</h1>
|
|
|
|
|
<h2>Header 2</h2>
|
|
|
|
|
<h3>Header 3</h3>
|
|
|
|
|
<h4>Header 4</h4>
|
|
|
|
|
<h5>Header 5</h5>
|
|
|
|
|
<h6>Header 6</h6>
|
|
|
|
|
|
p, pre, br, b, img, separator, i, u, small, mark, code tags
2025-07-22 15:34:42 +03:00
|
|
|
<p>Hey there! this is a test</p>
|
|
|
|
|
<b>This is bold</b>
|
2025-07-22 21:15:57 +03:00
|
|
|
<i>This is italic <mark>actually, and it's pretty <u>cool</u></mark></i>
|
p, pre, br, b, img, separator, i, u, small, mark, code tags
2025-07-22 15:34:42 +03:00
|
|
|
<u>This is underline</u>
|
|
|
|
|
<small>this is small</small>
|
|
|
|
|
<mark>this is marked</mark>
|
2025-07-22 21:15:57 +03:00
|
|
|
<code>this is code<span> THIS IS A SPAN AND SHOULDNT BE ANY DIFFERENT</span></code>
|
p, pre, br, b, img, separator, i, u, small, mark, code tags
2025-07-22 15:34:42 +03:00
|
|
|
|
2025-07-22 22:14:37 +03:00
|
|
|
<p>
|
|
|
|
|
<a href=\"https://youtube.com\">Hello gang</a>
|
|
|
|
|
</p>
|
|
|
|
|
|
p, pre, br, b, img, separator, i, u, small, mark, code tags
2025-07-22 15:34:42 +03:00
|
|
|
<pre>
|
|
|
|
|
Text in a pre element
|
|
|
|
|
is displayed in a fixed-width
|
|
|
|
|
font, and it preserves
|
|
|
|
|
both spaces and
|
|
|
|
|
line breaks
|
|
|
|
|
</pre>
|
2025-07-20 13:45:07 +03:00
|
|
|
|
p, pre, br, b, img, separator, i, u, small, mark, code tags
2025-07-22 15:34:42 +03:00
|
|
|
<separator direction=\"horizontal\" />
|
|
|
|
|
<img src=\"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQMNUPIKabszX0Js_c0kfa4cz_JQYKfGTuBUA&s\" />
|
|
|
|
|
<separator direction=\"vertical\" />
|
2025-07-20 13:45:07 +03:00
|
|
|
</body>".to_utf8_buffer()
|
|
|
|
|
|
|
|
|
|
# Create parser and parse
|
p, pre, br, b, img, separator, i, u, small, mark, code tags
2025-07-22 15:34:42 +03:00
|
|
|
var parser: HTMLParser = HTMLParser.new(html_bytes)
|
2025-07-20 13:45:07 +03:00
|
|
|
var parse_result = parser.parse()
|
|
|
|
|
|
|
|
|
|
print("Total elements found: " + str(parse_result.all_elements.size()))
|
|
|
|
|
|
|
|
|
|
if parse_result.errors.size() > 0:
|
|
|
|
|
print("Parse errors: " + str(parse_result.errors))
|
|
|
|
|
|
|
|
|
|
# TODO: render the shit on the screen
|
p, pre, br, b, img, separator, i, u, small, mark, code tags
2025-07-22 15:34:42 +03:00
|
|
|
var tab = tab_container.tabs[tab_container.active_tab]
|
|
|
|
|
|
|
|
|
|
var title = parser.get_title()
|
|
|
|
|
tab.set_title(title)
|
|
|
|
|
|
|
|
|
|
var icon = parser.get_icon()
|
|
|
|
|
set_loading_icon(tab)
|
2025-07-22 22:14:37 +03:00
|
|
|
call_deferred("update_tab_icon", tab, icon)
|
p, pre, br, b, img, separator, i, u, small, mark, code tags
2025-07-22 15:34:42 +03:00
|
|
|
|
|
|
|
|
var body = parser.find_first("body")
|
2025-07-22 21:15:57 +03:00
|
|
|
var i = 0
|
|
|
|
|
while i < body.children.size():
|
|
|
|
|
var element: HTMLParser.HTMLElement = body.children[i]
|
|
|
|
|
|
|
|
|
|
if element.is_inline_element():
|
|
|
|
|
# Collect consecutive inline elements and flatten nested ones
|
|
|
|
|
var inline_elements: Array[HTMLParser.HTMLElement] = []
|
2025-07-22 22:14:37 +03:00
|
|
|
var has_hyperlink = false
|
2025-07-22 21:15:57 +03:00
|
|
|
while i < body.children.size() and body.children[i].is_inline_element():
|
|
|
|
|
inline_elements.append(body.children[i])
|
2025-07-22 22:14:37 +03:00
|
|
|
if contains_hyperlink(body.children[i]):
|
|
|
|
|
has_hyperlink = true
|
2025-07-22 21:15:57 +03:00
|
|
|
i += 1
|
|
|
|
|
|
|
|
|
|
var inline_container = P.instantiate()
|
|
|
|
|
|
|
|
|
|
var temp_parent = HTMLParser.HTMLElement.new()
|
|
|
|
|
temp_parent.tag_name = "p"
|
|
|
|
|
temp_parent.children = inline_elements
|
|
|
|
|
inline_container.init(temp_parent)
|
2025-07-22 22:14:37 +03:00
|
|
|
|
2025-07-22 21:15:57 +03:00
|
|
|
website_container.add_child(inline_container)
|
2025-07-22 22:14:37 +03:00
|
|
|
|
|
|
|
|
if has_hyperlink:
|
|
|
|
|
inline_container.rich_text_label.meta_clicked.connect(func(meta): OS.shell_open(str(meta)))
|
|
|
|
|
|
2025-07-22 21:15:57 +03:00
|
|
|
continue
|
|
|
|
|
|
p, pre, br, b, img, separator, i, u, small, mark, code tags
2025-07-22 15:34:42 +03:00
|
|
|
match element.tag_name:
|
|
|
|
|
"p":
|
|
|
|
|
var p = P.instantiate()
|
|
|
|
|
p.init(element)
|
|
|
|
|
website_container.add_child(p)
|
2025-07-22 22:14:37 +03:00
|
|
|
if contains_hyperlink(element):
|
|
|
|
|
p.rich_text_label.meta_clicked.connect(func(meta): OS.shell_open(str(meta)))
|
2025-07-23 13:35:42 +03:00
|
|
|
"h1":
|
|
|
|
|
var h1 = H1.instantiate()
|
|
|
|
|
h1.init(element)
|
|
|
|
|
website_container.add_child(h1)
|
|
|
|
|
if contains_hyperlink(element):
|
|
|
|
|
h1.rich_text_label.meta_clicked.connect(func(meta): OS.shell_open(str(meta)))
|
|
|
|
|
"h2":
|
|
|
|
|
var h2 = H2.instantiate()
|
|
|
|
|
h2.init(element)
|
|
|
|
|
website_container.add_child(h2)
|
|
|
|
|
if contains_hyperlink(element):
|
|
|
|
|
h2.rich_text_label.meta_clicked.connect(func(meta): OS.shell_open(str(meta)))
|
|
|
|
|
"h3":
|
|
|
|
|
var h3 = H3.instantiate()
|
|
|
|
|
h3.init(element)
|
|
|
|
|
website_container.add_child(h3)
|
|
|
|
|
if contains_hyperlink(element):
|
|
|
|
|
h3.rich_text_label.meta_clicked.connect(func(meta): OS.shell_open(str(meta)))
|
|
|
|
|
"h4":
|
|
|
|
|
var h4 = H4.instantiate()
|
|
|
|
|
h4.init(element)
|
|
|
|
|
website_container.add_child(h4)
|
|
|
|
|
if contains_hyperlink(element):
|
|
|
|
|
h4.rich_text_label.meta_clicked.connect(func(meta): OS.shell_open(str(meta)))
|
|
|
|
|
"h5":
|
|
|
|
|
var h5 = H5.instantiate()
|
|
|
|
|
h5.init(element)
|
|
|
|
|
website_container.add_child(h5)
|
|
|
|
|
if contains_hyperlink(element):
|
|
|
|
|
h5.rich_text_label.meta_clicked.connect(func(meta): OS.shell_open(str(meta)))
|
|
|
|
|
"h6":
|
|
|
|
|
var h6 = H6.instantiate()
|
|
|
|
|
h6.init(element)
|
|
|
|
|
website_container.add_child(h6)
|
|
|
|
|
if contains_hyperlink(element):
|
|
|
|
|
h6.rich_text_label.meta_clicked.connect(func(meta): OS.shell_open(str(meta)))
|
p, pre, br, b, img, separator, i, u, small, mark, code tags
2025-07-22 15:34:42 +03:00
|
|
|
"pre":
|
|
|
|
|
var pre = PRE.instantiate()
|
|
|
|
|
pre.init(element)
|
|
|
|
|
website_container.add_child(pre)
|
|
|
|
|
"br":
|
|
|
|
|
var br = BR.instantiate()
|
|
|
|
|
br.init(element)
|
|
|
|
|
website_container.add_child(br)
|
|
|
|
|
"img":
|
|
|
|
|
var img = IMG.instantiate()
|
|
|
|
|
img.init(element)
|
|
|
|
|
website_container.add_child(img)
|
|
|
|
|
"separator":
|
|
|
|
|
var separator = SEPARATOR.instantiate()
|
|
|
|
|
separator.init(element)
|
|
|
|
|
website_container.add_child(separator)
|
2025-07-22 21:15:57 +03:00
|
|
|
"span":
|
|
|
|
|
var span = SPAN.instantiate()
|
|
|
|
|
span.init(element)
|
|
|
|
|
website_container.add_child(span)
|
p, pre, br, b, img, separator, i, u, small, mark, code tags
2025-07-22 15:34:42 +03:00
|
|
|
_:
|
|
|
|
|
print("Couldn't parse unsupported HTML tag \"%s\"" % element.tag_name)
|
2025-07-22 21:15:57 +03:00
|
|
|
|
|
|
|
|
i += 1
|
p, pre, br, b, img, separator, i, u, small, mark, code tags
2025-07-22 15:34:42 +03:00
|
|
|
|
|
|
|
|
func set_loading_icon(tab: Tab) -> void:
|
|
|
|
|
tab.set_icon(LOADER_CIRCLE)
|
|
|
|
|
|
|
|
|
|
loading_tween = create_tween()
|
|
|
|
|
loading_tween.set_loops()
|
|
|
|
|
|
|
|
|
|
var icon = tab.icon
|
|
|
|
|
icon.pivot_offset = Vector2(11.5, 11.5)
|
|
|
|
|
loading_tween.tween_method(func(angle): icon.rotation = angle, 0.0, TAU, 1.0)
|
|
|
|
|
|
|
|
|
|
func stop_loading_icon() -> void:
|
|
|
|
|
if loading_tween:
|
|
|
|
|
loading_tween.kill()
|
|
|
|
|
loading_tween = null
|
2025-07-22 22:14:37 +03:00
|
|
|
|
|
|
|
|
func update_tab_icon(tab: Tab, icon: String) -> void:
|
|
|
|
|
tab.set_icon(await Network.fetch_image(icon))
|
|
|
|
|
stop_loading_icon()
|
|
|
|
|
|
|
|
|
|
func contains_hyperlink(element: HTMLParser.HTMLElement) -> bool:
|
|
|
|
|
if element.tag_name == "a":
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
for child in element.children:
|
|
|
|
|
if contains_hyperlink(child):
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
return false
|