Clone in a terminal:
Commit 46f1099
Johan Mattsson - 2016-01-14T18:38:02+00:00
46f109983f33587c994679ab3776cb0cfdd65d8fjohan.mattsson.m@gmail.com

Bird UI - a new GUI toolkit based on SVG components

Changed files (25)

Diff

diff --git a/birdfont/GtkWindow.vala b/birdfont/GtkWindow.valaindex 1964b3c..d5851cb 100644--- a/birdfont/GtkWindow.vala+++ b/birdfont/GtkWindow.vala@@ -52,8 +52,6 @@ public class GtkWindow : Gtk.Window, NativeWindow { 	Box text_box; 	Gtk.Button submit_text_button; 	-	Gtk.Window tooltip_window = new Gtk.Window ();-	 	ToolboxCanvas toolbox; 	 	Task background_task = new Task(idle);@@ -112,12 +110,7 @@ public class GtkWindow : Gtk.Window, NativeWindow { 				uri = Preview.get_uri (); 				html = Preview.get_html_with_absolute_paths (); 										-				try {	-					html_canvas.load_html_string (html, uri);-				} catch (Error e) {-					warning (e.message);-					warning ("Failed to load html into canvas.");-				}+				html_canvas.load_html_string (html, uri); 				 				// show the webview when loading has finished  				html_box.set_visible (true); diff --git a/birdfont/Main.vala b/birdfont/Main.valaindex 09bc331..be3dd94 100644--- a/birdfont/Main.vala+++ b/birdfont/Main.vala@@ -32,7 +32,7 @@ public static int main (string[] arg) { 	window.set_native (native_window); 	native_window.init (); -	birdfont.load_font_from_command_line ();+	BirdFont.BirdFont.load_font_from_command_line (); 	 	Gtk.main (); 	return 0;diff --git a/birdui/AbsoluteLayout.vala b/birdui/AbsoluteLayout.valanew file mode 100644index 0000000..d8aaa1f--- /dev/null+++ b/birdui/AbsoluteLayout.vala@@ -0,0 +1,35 @@+/*+	Copyright (C) 2016 Johan Mattsson++	This library is free software; you can redistribute it and/or modify +	it under the terms of the GNU Lesser General Public License as +	published by the Free Software Foundation; either version 3 of the +	License, or (at your option) any later version.++	This library is distributed in the hope that it will be useful, but +	WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +	Lesser General Public License for more details.+*/++using Gee;+using SvgBird;+using B;++namespace Bird {++class AbsoluteLayout : Component {++	public AbsoluteLayout () {+	}++	public AbsoluteLayout.for_tag (XmlElement layout_tag) {+		parse (layout_tag);+	}++	public override string to_string () {+		return "AbsoluteLayout";+	}+}++}diff --git a/birdui/BoxLayout.vala b/birdui/BoxLayout.valanew file mode 100644index 0000000..32845bb--- /dev/null+++ b/birdui/BoxLayout.vala@@ -0,0 +1,94 @@+/*+	Copyright (C) 2016 Johan Mattsson++	This library is free software; you can redistribute it and/or modify +	it under the terms of the GNU Lesser General Public License as +	published by the Free Software Foundation; either version 3 of the +	License, or (at your option) any later version.++	This library is distributed in the hope that it will be useful, but +	WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +	Lesser General Public License for more details.+*/++using Gee;+using SvgBird;+using B;++namespace Bird {++public enum BoxOrientation {+	HORIZONTAL,+	VERTICAL+}++class BoxLayout : Component {++	public BoxOrientation orientation { get; private set; }+	+	public BoxLayout (BoxOrientation orientation) {+		base ();+		this.orientation = orientation;+	}++	public BoxLayout.for_tag (XmlElement layout_tag, BoxOrientation orientation) {+		base.for_tag (layout_tag);+		this.orientation = orientation;+	}+		+	public override void layout () {+		switch (orientation) {+		case BoxOrientation.HORIZONTAL:+			layout_horizontal ();+			break;+		case BoxOrientation.VERTICAL:+			layout_vertical ();+			break;+		}+	}++	void layout_horizontal () {+		double child_x = 0;+		double child_y = 0;+		+		foreach (Component component in components) {+			component.x = child_x;+			component.y = child_y;+			component.layout ();+			child_x += component.padded_width;+			+			if (component.height > height) {+				height = component.height;+			}+		}+		+		width = child_x;+	}++	void layout_vertical () {+		double child_x = 0;+		double child_y = 0;+		+		foreach (Component component in components) {+			component.x = child_x;+			component.y = child_y;+			component.layout ();+			component.apply_padding ();++			child_y += component.padded_height;+						+			if (component.width > width) {+				width = component.width;+			}+		}+		+		height = child_y;+	}++	public override string to_string () {+		return "BoxLayout";+	}+}++}diff --git a/birdui/Component.vala b/birdui/Component.valanew file mode 100644index 0000000..21854f7--- /dev/null+++ b/birdui/Component.vala@@ -0,0 +1,291 @@+/*+	Copyright (C) 2016 Johan Mattsson++	This library is free software; you can redistribute it and/or modify +	it under the terms of the GNU Lesser General Public License as +	published by the Free Software Foundation; either version 3 of the +	License, or (at your option) any later version.++	This library is distributed in the hope that it will be useful, but +	WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +	Lesser General Public License for more details.+*/++using B;+using SvgBird;+using Gee;+using Cairo;++namespace Bird {++class Component : GLib.Object {+	public double width { get; protected set; }+	public double height { get; protected set; }+	+	public double padded_width {+		get {+			return width + get_padding_top () + get_padding_bottom ();+		}+	}+	+	public double padded_height { +		get {+			return height + get_padding_left () + get_padding_right ();+		}+	}+	+	/** Vertical placement for this component relative to the parent container. */+	public double x { get; protected set; }+	+	/** Horizontal placement for this component relative to the parent container. */+	public double y { get; protected set; }++	/** The parts this component is made of. */+	protected ArrayList<Component> components = new ArrayList<Component> ();++	XmlElement? component_tag = null;+	+	/** Style sheet and other SVG definitions. */+	Defs? defs = null;+	SvgStyle style = new SvgStyle ();++	string? css_class = null;+	string? id = null;++	public Component () {+	}++	public Component.for_tag (XmlElement component_tag) {+		this.component_tag = component_tag;+		parse (component_tag);+	}++	private void inherit_style (Defs? defs) {+		this.defs = defs;+	}++	protected void apply_padding () {	+	}++	protected void load_svg_file (string file_name) {+		SvgComponent svg = new SvgComponent.for_file (file_name);+		add_component (svg);		+	}++	protected void parse_style (XmlElement style_tag) {+		Defs definitions = new Defs ();		+		StyleSheet style_sheet = StyleSheet.parse (definitions, style_tag);+		+		if (defs != null) {+			Defs subscope_definitions = ((!) defs).shallow_copy ();+			subscope_definitions.style_sheet.merge (style_sheet);+		} else {+			defs = definitions;+		}+	}+	+	protected void parse_svg (XmlElement svg_tag) {+		foreach (Attribute attribute in svg_tag.get_attributes ()) {+			string attribute_name = attribute.get_name ();+			+			if (attribute_name == "file") {+				load_svg_file (attribute.get_content ());+			}+		}+	}++	protected void add_component (Component component) {+		components.add (component);+		component.inherit_style (defs);+		+		if (component.component_tag != null) {+			style = SvgStyle.parse (defs, style, (!) component.component_tag);+		}+	}++	protected void parse_layout (XmlElement layout_tag) {+		foreach (Attribute attribute in layout_tag.get_attributes ()) {+			string attribute_name = attribute.get_name ();+			+			if (attribute_name == "type") {+				if (attribute.get_content () == "hbox") {+					HBox hbox = new HBox.for_tag (layout_tag);+					add_component (hbox);+				} else if (attribute.get_content () == "vbox") {+					VBox hbox = new VBox.for_tag (layout_tag);+					add_component (hbox);+				} else {+					warning ("Layout of type " + attribute.get_content ()+						+ " is not implemented in this verison.");+				}+			} else if (attribute_name == "id") {+				id = attribute.get_content ();+			} else if (attribute_name == "class") {+				css_class = attribute.get_content ();+			} else if (attribute_name == "style") {+				// style will be parsed later+			} else {+				unused_attribute (attribute_name);+			}+		}+	}++	protected void parse_component (XmlElement component_tag) {+		foreach (Attribute attribute in component_tag.get_attributes ()) {+			string attribute_name = attribute.get_name ();+			+			if (attribute_name == "file") {+				Component component = new Component ();+				component.load (attribute.get_content ());+				add_component (component);+			} else {+				unused_attribute (attribute_name);+			}+		}+	}+	+	protected void parse (XmlElement component_tag) {+		foreach (XmlElement tag in component_tag) {+			string tag_name = tag.get_name ();+			+			if (tag_name == "ui") {+				parse (tag);+			} else if (tag_name == "layout") {+				parse_layout (tag);+			} else if (tag_name == "component") {+				parse_component (tag);+			} else if (tag_name == "svg") {+				parse_svg (tag);+			} else if (tag_name == "style") {+				parse_style (tag);+			} else {+				unused_tag (tag_name);+			}+		}+	}++	internal void unused_attribute (string attribute) {+		warning ("The attribute " + attribute + " is not known in this version.");+	}+	+	internal void unused_tag (string tag_name) {+		warning ("The tag " + tag_name + " is not known in this version.");+	}++	public void load (string file_name) {+		if (file_name.has_suffix (".ui")) {+			load_layout (file_name);+		} else if (file_name.has_suffix (".svg")) {+			load_svg_file (file_name);+		} else {+			warning (file_name + " is not a ui file or svg file.");+		}+	}++	public void load_layout (string file_name) {+		string? path = find_file (file_name);+		+		if (path == null) {+			warning (file_name + " not found.");+			return;+		}+		+		string xml_data;+		File layout_file = File.new_for_path ((!) path); +		+		try {+			FileUtils.get_contents((!) layout_file.get_path (), out xml_data);+		} catch (GLib.Error error) {+			warning (error.message);+			return;+		}++		XmlTree xml_parser = new XmlTree (xml_data);+		Component component = new Component.for_tag (xml_parser.get_root ());+		add_component (component);+		layout ();+	}+	+	public static string find_file (string file_name) {+		File file = File.new_for_path ("birdui/" + file_name);+		+		if (file.query_exists ()) {+			return (!) file.get_path ();+		}+		+		return file_name;+	}+	+	public virtual void layout () {+		if (unlikely (components.size > 1)) {+			warning ("A component has several parts but no layout has been set.");+		}+		+		foreach (Component component in components) {+			component.layout ();+			width = component.width;+			height = component.height;+		}+	}+	+	public virtual void draw (Context cairo) {+		foreach (Component component in components) {+			cairo.save ();+			cairo.translate (component.x, component.y);+			component.draw (cairo);+			cairo.restore ();+		}+	}+	+	public virtual void motion_notify_event (double x, double y) {	+	}+	+	public virtual void button_press_event (uint button, double x, double y) {	+	}+	+	public virtual string to_string () {+		if (component_tag != null) {+			XmlElement tag = (!) component_tag;+			return @"Component $(tag.get_name ())";+		} else {+			+			return "Component";+		}+	}+	+	public void print_tree () {+		print_tree_level (0);+	}++	protected void print_tree_level (int indent) {+		for (int i = 0; i < indent; i++) {+			print ("\t");+		}+		+		print (@"$(to_string ()), x: $x, y: $y, w: $width, h: $height\n");+		+		foreach (Component component in components) {+			component.print_tree_level (indent + 1);+		}+	}+	+	double get_padding_bottom () {+		return SvgFile.parse_number (style.get_css_property ("padding-bottom"));+	}++	double get_padding_top () {+		return SvgFile.parse_number (style.get_css_property ("padding-top"));+	}++	double get_padding_left () {+		return SvgFile.parse_number (style.get_css_property ("padding-left"));+	}++	double get_padding_right () {+		return SvgFile.parse_number (style.get_css_property ("padding-right"));+	}+}++}+diff --git a/birdui/GtkWidget.vala b/birdui/GtkWidget.valanew file mode 100644index 0000000..9981c1d--- /dev/null+++ b/birdui/GtkWidget.vala@@ -0,0 +1,51 @@+/*+	Copyright (C) 2016 Johan Mattsson++	This library is free software; you can redistribute it and/or modify +	it under the terms of the GNU Lesser General Public License as +	published by the Free Software Foundation; either version 3 of the +	License, or (at your option) any later version.++	This library is distributed in the hope that it will be useful, but +	WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +	Lesser General Public License for more details.+*/++using Gtk;+using Gdk;+using Cairo;++namespace Bird {++class Widget : DrawingArea {+	+	Component component;+	+	public Widget (Component main_component) {		+		component = main_component;++		add_events (EventMask.BUTTON_PRESS_MASK +			| EventMask.POINTER_MOTION_MASK+			| EventMask.LEAVE_NOTIFY_MASK);+			  +		motion_notify_event.connect ((event)=> {+			component.motion_notify_event (event.x, event.y);+			return true;+		});	+				+		button_press_event.connect ((event)=> {+			component.button_press_event (event.button, event.x, event.y);+			return true;+		});++		draw.connect ((event) => {+			Context cairo_context = cairo_create (get_window ());+			component.draw (cairo_context);+			return true;+		});+	}+	+}++}diff --git a/birdui/GtkWindow.vala b/birdui/GtkWindow.valanew file mode 100644index 0000000..a32bd4e--- /dev/null+++ b/birdui/GtkWindow.vala@@ -0,0 +1,38 @@+/*+	Copyright (C) 2016 Johan Mattsson++	This library is free software; you can redistribute it and/or modify +	it under the terms of the GNU Lesser General Public License as +	published by the Free Software Foundation; either version 3 of the +	License, or (at your option) any later version.++	This library is distributed in the hope that it will be useful, but +	WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +	Lesser General Public License for more details.+*/++using Bird;+using Gtk;++int main (string[] args) {+	Gtk.init (ref args);+	+	Window window = new Window ();+	window.set_title ("UI Bird");+	window.destroy.connect (Gtk.main_quit);+	+	Component layout = new Component ();+	layout.load ("test.ui");+	+	Bird.Widget primary_layout = new Bird.Widget (layout);++	Box vbox = new Box (Orientation.VERTICAL, 0);+	vbox.pack_start(primary_layout, true, true, 0);+	window.add (vbox);+	+	window.show_all ();+	+	Gtk.main ();+	return 0;+}diff --git a/birdui/HBox.vala b/birdui/HBox.valanew file mode 100644index 0000000..e26102d--- /dev/null+++ b/birdui/HBox.vala@@ -0,0 +1,34 @@+/*+	Copyright (C) 2016 Johan Mattsson++	This library is free software; you can redistribute it and/or modify +	it under the terms of the GNU Lesser General Public License as +	published by the Free Software Foundation; either version 3 of the +	License, or (at your option) any later version.++	This library is distributed in the hope that it will be useful, but +	WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +	Lesser General Public License for more details.+*/++using Gee;+using B;++namespace Bird {++class HBox : BoxLayout {+	public HBox () {+		base (BoxOrientation.HORIZONTAL);+	}++	public HBox.for_tag (XmlElement layout) {+		base.for_tag (layout, BoxOrientation.HORIZONTAL);+	}+	+	public override string to_string () {+		return "HBox";+	}+}++}diff --git a/birdui/Style.vala b/birdui/Style.valanew file mode 100644index 0000000..d1e3d2c--- /dev/null+++ b/birdui/Style.vala@@ -0,0 +1,34 @@+/*+	Copyright (C) 2016 Johan Mattsson++	This library is free software; you can redistribute it and/or modify +	it under the terms of the GNU Lesser General Public License as +	published by the Free Software Foundation; either version 3 of the +	License, or (at your option) any later version.++	This library is distributed in the hope that it will be useful, but +	WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +	Lesser General Public License for more details.+*/++using B;+using SvgBird;+using Gee;+using Cairo;++namespace Bird {++public class Style : GLib.Object {+	public SvgStyle svg_style;+	+	public Style () {+		svg_style = new SvgStyle ();+	}+	+	public Style.for_svg (SvgStyle svg_style) {+		this.svg_style = svg_style;+	}+}++}diff --git a/birdui/SvgComponent.vala b/birdui/SvgComponent.valanew file mode 100644index 0000000..106a853--- /dev/null+++ b/birdui/SvgComponent.vala@@ -0,0 +1,88 @@+/*+	Copyright (C) 2016 Johan Mattsson++	This library is free software; you can redistribute it and/or modify +	it under the terms of the GNU Lesser General Public License as +	published by the Free Software Foundation; either version 3 of the +	License, or (at your option) any later version.++	This library is distributed in the hope that it will be useful, but +	WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +	Lesser General Public License for more details.+*/++using B;+using SvgBird;+using Gee;+using Cairo;++namespace Bird {++class SvgComponent : Component {+	string? path = null;+	string file_name = "";+	SvgDrawing? drawing = null;+	+	public SvgComponent () {+		base ();+	}++	public SvgComponent.for_tag (XmlElement svg_component_tag) {+		base ();+	}++	public SvgComponent.for_file (string svg_file) {+		base ();+		load_svg (svg_file);+	}++	public override void layout () {+		if (unlikely (components.size > 0)) {+			warning ("SVG files can not have subviews.");+		}+		+		if (drawing != null) {+			SvgDrawing svg = (!) drawing;+			width = svg.width;+			height = svg.height;+		}+	}+			+	public override string to_string () {+		return "Svg: " + file_name;+	}++	private void load_svg (string file_name) {+		this.file_name = file_name;+		path = find_file (file_name);+		+		if (path == null) {+			warning (file_name + " not found.");+			return;+		}+				+		string xml_data;+		File svg_file = File.new_for_path ((!) path); +		try {+			FileUtils.get_contents((!) svg_file.get_path (), out xml_data);+		} catch (GLib.Error error) {+			warning (error.message);+			return;+		}+		+		SvgFile svg_parser = new SvgFile ();+		drawing = svg_parser.parse_svg_data (xml_data);+	}++	public override void draw (Context cairo) {+		if (drawing != null) {+			SvgDrawing svg = (!) drawing;+			svg.draw (cairo);+		}+	}++}++}+diff --git a/birdui/VBox.vala b/birdui/VBox.valanew file mode 100644index 0000000..a9a1e03--- /dev/null+++ b/birdui/VBox.vala@@ -0,0 +1,35 @@+/*+	Copyright (C) 2016 Johan Mattsson++	This library is free software; you can redistribute it and/or modify +	it under the terms of the GNU Lesser General Public License as +	published by the Free Software Foundation; either version 3 of the +	License, or (at your option) any later version.++	This library is distributed in the hope that it will be useful, but +	WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +	Lesser General Public License for more details.+*/++using Gee;+using B;++namespace Bird {+	+class VBox : BoxLayout {+	public VBox () {+		base (BoxOrientation.VERTICAL);+	}++	public VBox.for_tag (XmlElement layout) {+		base.for_tag (layout, BoxOrientation.VERTICAL);+	}+	+	public override string to_string () {+		return "VBox";+	}++}++}diff --git a/dodo.py b/dodo.pyindex fe207c6..460a246 100644--- a/dodo.py+++ b/dodo.py@@ -559,4 +559,55 @@ def make_birdfont_test(target_binary, deps): def task_birdfont_test():     yield make_birdfont_test('birdfont-test', ['libbirdgems.so', 'libbirdfont.so']) +def make_birdui(target_binary, deps):+    valac_command = config.VALAC + """\+		-C \+		--pkg posix \+		--pkg """ + config.GEE + """ \+		--pkg gtk+-3.0 \+		--pkg libsvgbird \+		--pkg xmlbird \+		--vapidir=./ \+		--basedir=build/birdui/ \+		""" + config.NON_NULL + """ \+		--enable-experimental \+		birdui/*.vala \+        """++    cc_command = config.CC + """ \+			-fPIC \+			$(pkg-config --cflags gtk+-3.0) \+			$(pkg-config --cflags glib-2.0) \+			$(pkg-config --cflags xmlbird) \+			$(pkg-config --cflags """ + config.GEE + """) \+			-g \+			-I ./build/libsvgbird \+			-c C_SOURCE \+            -o OBJECT_FILE \+			"""++    linker_command = config.CC + """ \+			""" + soname(target_binary) + """ \+			-fPIC \+			-g \+			build/birdui/*.o \+			$(pkg-config --libs gtk+-3.0) \+			$(pkg-config --libs glib-2.0) \+			$(pkg-config --libs gobject-2.0) \+			$(pkg-config --libs xmlbird) \+			$(pkg-config --libs """ + config.GEE + """) \+			-L ./build/bin -l svgbird \+			-o build/bin/""" + target_binary++    libbirdgems = Builder('birdui',+                          valac_command, +                          cc_command,+                          linker_command,+                          target_binary,+						  None,+                          deps)+			+    yield libbirdgems.build() +def task_birdui():+    yield make_birdui('birdui', []) diff --git a/libsvgbird/AttributePattern.vala b/libsvgbird/AttributePattern.valaindex 7e1aca6..722f377 100644--- a/libsvgbird/AttributePattern.vala+++ b/libsvgbird/AttributePattern.vala@@ -29,6 +29,14 @@ public class AttributePattern : GLib.Object { 	public string name = ""; 	public string? content = null; 	public AttributePatternType type = AttributePatternType.NONE;++	public AttributePattern copy () {+		AttributePattern a = new AttributePattern ();+		a.name = name;+		a.content = content;+		a.type = type;+		return a;+	} 	 	public bool match (Attributes attributes) { 		switch (type) {diff --git a/libsvgbird/Color.vala b/libsvgbird/Color.valaindex 75950d5..a952a3a 100644--- a/libsvgbird/Color.vala+++ b/libsvgbird/Color.vala@@ -95,9 +95,9 @@ public class Color { 	public string to_rgb_hex () { 		StringBuilder rgb = new StringBuilder (); 		rgb.append ("#");-		rgb.append_printf ("%x", (int) Math.rint (r  * 254));-		rgb.append_printf ("%x", (int) Math.rint (g  * 254));-		rgb.append_printf ("%x", (int) Math.rint (b  * 254));+		rgb.append_printf ("%02x", (int) Math.rint (r  * 254));+		rgb.append_printf ("%02x", (int) Math.rint (g  * 254));+		rgb.append_printf ("%02x", (int) Math.rint (b  * 254)); 		return rgb.str; 	} @@ -108,7 +108,6 @@ public class Color { 		return rgba.str; 	} - 	public Color copy () { 		return new Color (r, g, b, a); 	}diff --git a/libsvgbird/Defs.vala b/libsvgbird/Defs.valaindex 6bc730c..5908723 100644--- a/libsvgbird/Defs.vala+++ b/libsvgbird/Defs.vala@@ -81,16 +81,29 @@ public class Defs { 		return ((!) attribute).has_prefix ("url"); 	} -	public Defs copy () {+	public Defs shallow_copy () { 		Defs d = new Defs (); 		 		foreach (Gradient g in gradients) { 			d.add (g); 		} 		+		d.style_sheet = style_sheet.shallow_copy ();+		 		return d; 	} +	public Defs copy () {+		Defs d = new Defs ();+		+		foreach (Gradient g in gradients) {+			d.add (g.copy ());+		}+		+		d.style_sheet = style_sheet.copy ();+		+		return d;+	} }  }diff --git a/libsvgbird/Object.vala b/libsvgbird/Object.valaindex 82af6c5..e1e6a65 100644--- a/libsvgbird/Object.vala+++ b/libsvgbird/Object.vala@@ -91,7 +91,7 @@ public abstract class Object : GLib.Object { 		bool need_stroke = style.stroke_gradient != null || style.stroke != null;  		cr.set_line_width (style.stroke_width);-	+		 		if (style.fill_gradient != null) { 			apply_gradient (cr, (!) style.fill_gradient); 		} else if (style.fill != null) {diff --git a/libsvgbird/Points.vala b/libsvgbird/Points.valaindex 890cd90..4971eec 100644--- a/libsvgbird/Points.vala+++ b/libsvgbird/Points.vala@@ -21,7 +21,12 @@ public class Points : GLib.Object { 	public double x = 0; 	public double y = 0; 	public bool closed = false;-+	public int size {+		get {+			return point_data.size;+		}+	}+	 	public void add (double p) { 		point_data.add (p); 	}diff --git a/libsvgbird/Selector.vala b/libsvgbird/Selector.valaindex 927f4a7..493781c 100644--- a/libsvgbird/Selector.vala+++ b/libsvgbird/Selector.vala@@ -19,11 +19,10 @@ namespace SvgBird {  public class Selector : GLib.Object { 	-	Gee.ArrayList<SelectorPattern> patterns;+	Gee.ArrayList<SelectorPattern> patterns = new Gee.ArrayList<SelectorPattern> (); 	public SvgStyle style { get; set; }  	public Selector (string pattern, SvgStyle style) {-		this.patterns = new Gee.ArrayList<SelectorPattern> (); 		string[] selector_patterns = pattern.split (","); 		 		for (int i = 0; i < selector_patterns.length; i++) {@@ -33,6 +32,18 @@ public class Selector : GLib.Object { 		this.style = style; 	} 	+	public Selector.copy_constructor (Selector selector) {+		style = selector.style.copy ();+		+		foreach (SelectorPattern pattern in selector.patterns) {+			selector.patterns.add (pattern.copy ());+		}+	}+	+	public Selector copy () {+		return new Selector.copy_constructor (this);+	}+	 	public bool match (XmlElement tag, string? id, string? css_class) { 		foreach (SelectorPattern pattern in patterns) { 			if (pattern.match (tag, id, css_class)) {diff --git a/libsvgbird/SelectorPattern.vala b/libsvgbird/SelectorPattern.valaindex bae64c6..e8ec52a 100644--- a/libsvgbird/SelectorPattern.vala+++ b/libsvgbird/SelectorPattern.vala@@ -19,13 +19,15 @@ namespace SvgBird {  /** A CSS selector pattern. */ public class SelectorPattern : GLib.Object {-	Gee.ArrayList<SelectorTag> tags;+	Gee.ArrayList<SelectorTag> tags = new Gee.ArrayList<SelectorTag> (); +	public SelectorPattern.empty () {+	}+	 	public SelectorPattern (string pattern) { 		string p = pattern.strip (); 		string[] elements = p.split (" ");-		tags = new Gee.ArrayList<SelectorTag> ();-		+ 		foreach (string element in elements) { 			if (element != "") { 				tags.add (new SelectorTag (element));@@ -33,12 +35,23 @@ public class SelectorPattern : GLib.Object { 		} 	} 	+	public SelectorPattern copy () {+		SelectorPattern pattern = new SelectorPattern.empty ();+		+		foreach (SelectorTag tag in tags) {+			pattern.tags.add (tag.copy ());+		}+		+		return pattern;+	}+	 	public bool match (XmlElement tag, string? id, string? css_class) { 		for (int i = tags.size - 1; i >= 0; i--) { 			SelectorTag pattern = tags.get (i); 						 			if (pattern.name == ">") { 				if (i - 1 < 0) {+					 					return false; 				} 				diff --git a/libsvgbird/SelectorTag.vala b/libsvgbird/SelectorTag.valaindex cd2c7ee..8329c65 100644--- a/libsvgbird/SelectorTag.vala+++ b/libsvgbird/SelectorTag.vala@@ -24,6 +24,9 @@ public class SelectorTag : GLib.Object { 	string? css_class = null; 	Gee.ArrayList<AttributePattern>? attribute_patterns = null; +	public SelectorTag.empty () {+	}+	 	public SelectorTag (string pattern) { 		string tag_pattern = pattern.strip (); 		int id_separator = tag_pattern.index_of ("#");@@ -61,6 +64,22 @@ public class SelectorTag : GLib.Object { 		} 	} +	public SelectorTag copy () {+		SelectorTag tag = new SelectorTag.empty();+		+		tag.name = name;+		tag.id = id;+		tag.css_class = css_class;+		+		if (attribute_patterns != null) { +			foreach (AttributePattern p in (!) attribute_patterns) {+				((!) attribute_patterns).add (p.copy ());+			}+		}+		+		return tag;+	}+	 	void parse_attributes (string attributes) { 		int index = 0; 		Gee.ArrayList<AttributePattern> patterns = new Gee.ArrayList<AttributePattern> ();@@ -139,7 +158,6 @@ public class SelectorTag : GLib.Object { 			}  			 			if (((!) this.css_class) != ((!) css_class)) {-				print(@"class \"$((!)this.css_class)\"=\"$((!)css_class)\"\n"); 				return false; 			} 		}diff --git a/libsvgbird/StyleSheet.vala b/libsvgbird/StyleSheet.valaindex cd7fc59..2062300 100644--- a/libsvgbird/StyleSheet.vala+++ b/libsvgbird/StyleSheet.vala@@ -17,6 +17,7 @@ using Math;  namespace SvgBird { + public class StyleSheet : GLib.Object { 	 	public Gee.ArrayList<Selector> styles;@@ -25,6 +26,27 @@ public class StyleSheet : GLib.Object { 		styles = new Gee.ArrayList<Selector> (); 	} 	+	/** Copy references to all selectors in this style sheet. */+	public StyleSheet shallow_copy () {+		StyleSheet style_sheet = new StyleSheet ();+		+		foreach (Selector selector in styles) {+			style_sheet.styles.add (selector);+		}+		+		return style_sheet;+	}++	public StyleSheet copy () {+		StyleSheet style_sheet = new StyleSheet ();+		+		foreach (Selector selector in styles) {+			style_sheet.styles.add (selector.copy ());+		}+		+		return style_sheet;+	}+	 	public void inherit_style (XmlElement tag, SvgStyle style) { 		string? id = null; 		string? css_class = null;@@ -46,6 +68,12 @@ public class StyleSheet : GLib.Object { 		} 	} 	+	public void merge (StyleSheet style_sheet) {+		foreach (Selector selector in style_sheet.styles) {+			styles.add (selector);+		}+	}+	 	public static StyleSheet parse (Defs defs, XmlElement style_tag) { 		StyleSheet style_sheet = new StyleSheet (); 		string css = style_tag.get_content ();diff --git a/libsvgbird/SvgArc.vala b/libsvgbird/SvgArc.valaindex 4cff89d..35cfe63 100644--- a/libsvgbird/SvgArc.vala+++ b/libsvgbird/SvgArc.vala@@ -138,9 +138,6 @@ public static void get_arc_arguments (double x0, double y0, 	} else if (sweepFlag && angleExtent < 0) { 		angleExtent += 2 * PI; 	}-		-	//center_x = cx - rx;-	//center_y = cy - ry;  	center_x = cx; 	center_y = cy;@@ -150,8 +147,6 @@ public static void get_arc_arguments (double x0, double y0,  	angle_start = angleStart; 	angle_extent = angleExtent;--	print (@"angleStart: $angle_start  angleExtent $angle_extent\n"); }  diff --git a/libsvgbird/SvgFile.vala b/libsvgbird/SvgFile.valaindex 5936b18..28ffe5f 100644--- a/libsvgbird/SvgFile.vala+++ b/libsvgbird/SvgFile.vala@@ -24,6 +24,11 @@ public class SvgFile : GLib.Object {  	public SvgFile () {		 	}++	public SvgDrawing parse_svg_data (string xml_data) {+		XmlTree tree = new XmlTree (xml_data);+		return parse_svg_file (tree.get_root ());+	} 	 	public SvgDrawing parse_svg_file (XmlElement svg_tag) { 		drawing = new SvgDrawing ();@@ -43,7 +48,7 @@ public class SvgFile : GLib.Object { 				 		foreach (XmlElement t in svg_tag) { 			string name = t.get_name ();-			+ 			if (name == "g") { 				parse_layer (drawing.root_layer, style, t); 			}@@ -642,8 +647,19 @@ public class SvgFile : GLib.Object { 		for (int i = 0; i < points_size; i++) { 			// FIXME: add more types 			if (bezier_points[i].type == 'M') {-				points.x = bezier_points[i].x0;-				points.y = bezier_points[i].y0;+				if (i == 0) {+					points.x = bezier_points[i].x0;+					points.y = bezier_points[i].y0;+				} else {+					points.add_type (LINE);+					points.add (bezier_points[i].x0);+					points.add (bezier_points[i].y0);+					points.add (0);+					points.add (0);+					points.add (0);+					points.add (0);+					points.add (0);+				} 			} else if (bezier_points[i].type == 'C') { 				points.add_type (CUBIC); 				points.add (bezier_points[i].x0);@@ -694,10 +710,10 @@ public class SvgFile : GLib.Object { 			} 		} -		if (points.point_data.size > 0) {+		if (points.size > 0) { 			path_data.add (points); 		}-+		 		return path_data; 	} diff --git a/libsvgbird/SvgPath.vala b/libsvgbird/SvgPath.valaindex a2122d2..908c61c 100644--- a/libsvgbird/SvgPath.vala+++ b/libsvgbird/SvgPath.vala@@ -70,7 +70,6 @@ public class SvgPath : Object { 			case LINE: 				cr.line_to (points[i + 1].value, points[i + 2].value); 				break;- 			} 		}		 	}diff --git a/libsvgbird/SvgStyle.vala b/libsvgbird/SvgStyle.valaindex a9807b8..a923ba8 100644--- a/libsvgbird/SvgStyle.vala+++ b/libsvgbird/SvgStyle.vala@@ -36,6 +36,16 @@ public class SvgStyle : GLib.Object { 		set_style_properties (defs, this); 	} +	public string? get_css_property (string key) {+		return style.get (key);+	}++	public SvgStyle copy () {+		SvgStyle style = new SvgStyle ();+		style.inherit (this);+		return style;+	}+ 	public LineCap get_line_cap () { 		string l; 		@@ -169,10 +179,34 @@ public class SvgStyle : GLib.Object { 				k = pair[0].strip (); 				v = pair[1].strip (); 				-				style.set (k, v);+				if (k == "padding") {+					parse_padding_shorthand (v);+				} else {+					style.set (k, v);+				} 			} 		} 	}+	+	void parse_padding_shorthand (string arguments) {+		string[] args = StyleSheet.replace_whitespace (arguments).split (" ");+		+		if (args.length > 0) {+			style.set ("padding-top", args[0]);+		}+		+		if (args.length > 1) {+			style.set ("padding-right", args[1]);+		}++		if (args.length > 2) {+			style.set ("padding-bottom", args[2]);+		}++		if (args.length > 3) {+			style.set ("padding-left", args[3]);+		}+	} }  }

Contact

Email: johan.mattsson.m@gmail.com
Mastodon: typo.social/@birdfont
GitHub: johanmattssonm
LinkedIn: johan-mattsson-2ab324112