mirror of
https://github.com/TakeV-Lambda/Tooth.git
synced 2024-09-27 13:13:20 +00:00
18e66885ee
fix: #67 again Setting MarkupView to HORIZONTAL breaks wrapping. Vexpanding RichLabels fixes the issue shown in the linked issue while keeping wrapping.
179 lines
3.7 KiB
Vala
179 lines
3.7 KiB
Vala
using Gtk;
|
|
|
|
public class Tooth.Widgets.MarkupView : Box {
|
|
|
|
public delegate void NodeFn (Xml.Node* node);
|
|
public delegate void NodeHandlerFn (MarkupView view, Xml.Node* node);
|
|
|
|
string? current_chunk = null;
|
|
|
|
string _content = "";
|
|
public string content {
|
|
get {
|
|
return _content;
|
|
}
|
|
set {
|
|
_content = value;
|
|
update_content (_content);
|
|
}
|
|
}
|
|
|
|
private bool _selectable = false;
|
|
public bool selectable {
|
|
get { return _selectable; }
|
|
set {
|
|
_selectable = value;
|
|
|
|
var w = this.get_first_child();
|
|
while (w != null) {
|
|
var label = w as RichLabel;
|
|
if (label != null) {
|
|
label.selectable = _selectable;
|
|
}
|
|
w = w.get_next_sibling();
|
|
};
|
|
}
|
|
}
|
|
|
|
construct {
|
|
orientation = Orientation.VERTICAL;
|
|
spacing = 12;
|
|
}
|
|
|
|
void update_content (string content) {
|
|
current_chunk = null;
|
|
|
|
for (var w = get_first_child (); w != null; w = w.get_next_sibling ()) {
|
|
w.destroy ();
|
|
}
|
|
|
|
var doc = Html.Doc.read_doc (content, "", "utf8");
|
|
if (doc != null) {
|
|
var root = doc->get_root_element ();
|
|
if (root != null) {
|
|
default_handler (this, root);
|
|
}
|
|
}
|
|
|
|
delete doc;
|
|
|
|
visible = get_first_child () != null;
|
|
}
|
|
|
|
static void traverse (Xml.Node* root, owned NodeFn cb) {
|
|
for (var iter = root->children; iter != null; iter = iter->next) {
|
|
cb (iter);
|
|
}
|
|
}
|
|
|
|
static void traverse_and_handle (MarkupView v, Xml.Node* root, owned NodeHandlerFn handler) {
|
|
traverse (root, node => {
|
|
handler (v, node);
|
|
});
|
|
}
|
|
|
|
void commit_chunk () {
|
|
if (current_chunk != null && current_chunk != "") {
|
|
var label = new RichLabel (current_chunk) {
|
|
visible = true,
|
|
// markup = MarkupPolicy.TRUST,
|
|
selectable = _selectable,
|
|
vexpand = true
|
|
};
|
|
prepend (label);
|
|
}
|
|
current_chunk = null;
|
|
}
|
|
|
|
void write_chunk (string? chunk) {
|
|
if (chunk == null) return;
|
|
|
|
if (current_chunk == null)
|
|
current_chunk = chunk;
|
|
else
|
|
current_chunk += chunk;
|
|
}
|
|
|
|
public static void default_handler (MarkupView v, Xml.Node* root) {
|
|
switch (root->name) {
|
|
case "html":
|
|
case "span":
|
|
case "markup":
|
|
case "pre":
|
|
case "ul":
|
|
case "ol":
|
|
traverse_and_handle (v, root, default_handler);
|
|
break;
|
|
case "body":
|
|
traverse_and_handle (v, root, default_handler);
|
|
v.commit_chunk ();
|
|
break;
|
|
case "p":
|
|
// Don't add spacing if this is the first paragraph
|
|
if (v.current_chunk != "" && v.current_chunk != null)
|
|
v.write_chunk ("\n\n");
|
|
|
|
traverse_and_handle (v, root, default_handler);
|
|
break;
|
|
case "code":
|
|
case "blockquote":
|
|
v.commit_chunk ();
|
|
|
|
var text = "";
|
|
traverse (root, (node) => {
|
|
switch (node->name) {
|
|
case "text":
|
|
text += node->content;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
});
|
|
|
|
var label = new RichLabel (text) {
|
|
visible = true
|
|
// markup = MarkupPolicy.DISALLOW
|
|
};
|
|
label.get_style_context ().add_class ("ttl-code");
|
|
v.prepend (label);
|
|
break;
|
|
case "a":
|
|
var href = root->get_prop ("href");
|
|
if (href != null) {
|
|
v.write_chunk ("<a href='" + GLib.Markup.escape_text (href) + "'>");
|
|
traverse_and_handle (v, root, default_handler);
|
|
v.write_chunk ("</a>");
|
|
}
|
|
break;
|
|
|
|
case "b":
|
|
case "i":
|
|
case "u":
|
|
case "s":
|
|
case "sup":
|
|
case "sub":
|
|
v.write_chunk (@"<$(root->name)>");
|
|
traverse_and_handle (v, root, default_handler);
|
|
v.write_chunk (@"</$(root->name)>");
|
|
break;
|
|
|
|
case "li":
|
|
v.write_chunk ("\n• ");
|
|
traverse_and_handle (v, root, default_handler);
|
|
break;
|
|
case "br":
|
|
v.write_chunk ("\n");
|
|
break;
|
|
case "text":
|
|
if (root->content != null)
|
|
v.write_chunk (GLib.Markup.escape_text (root->content));
|
|
break;
|
|
default:
|
|
warning (@"Unknown HTML tag: \"$(root->name)\"");
|
|
traverse_and_handle (v, root, default_handler);
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|