Using SVG with XBL in XUL

From MozillaZine Knowledge Base
Revision as of 19:45, 20 May 2005 by Edanuff (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search

The following example shows how to create an XBL component that uses SVG to render graphics. The code can be adapted as the basis of any number of vector-graphics based widgets.


svg_example.xul

<?xml version="1.0"?>
<?xml-stylesheet href="svg_example.css" type="text/css"?>
<window title="Test"
	orient="horizontal"
	xmlns:html="http://www.w3.org/1999/xhtml" 
	xmlns:svg="http://www.w3.org/2000/svg" 
	xmlns:xlink="http://www.w3.org/1999/xlink"
	xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
>
	<stack flex="1">
		<vbox flex="1">
			<svg-shape id="background-circle" flex="1" type="circle" />
		</vbox>
		<vbox flex="1">
			<spacer flex="2"/>
			<svg-shape flex="1" id="svg-button" type="rect" radius="12" label="Box"/>
			<spacer flex="2"/>
			<hbox flex="4">
				<svg-shape flex="1" id="circ1" type="circle" label="1"/>
				<svg-shape flex="1" id="circ2" type="circle" label="2" />
				<svg-shape flex="1" id="circ3" type="circle" label="3" />
			</hbox>
			<spacer flex="1"/>
		</vbox>
	</stack>

</window>

svg_example.css

window {
	background: #0D0D5A;
}

svg-shape {
	-moz-binding: url("svg-shape.xml#shape");
	-moz-user-focus: normal;
	stroke-width: 4px;
	font-family: Lucida Grande,Geneva,Verdana,Arial,Helvetica,sans-serif;
	font-style: bold;
	font-variant: normal;
	line-height: normal;
        font-size: 32px;
}

svg-shape .svg-shape-text {
	fill: white;
}

svg-shape .svg-shape-rect {
	fill: red;
	stroke: white;
}

svg-shape:focus .svg-shape-rect {
	fill: green;
	stroke: white;
}

svg-shape .svg-shape-circle {
	fill-opacity: .20;
	stroke: white;
}

svg-shape:focus .svg-shape-circle {
	fill-opacity: 1;
	stroke: white;
}

#circ1 .svg-shape-circle {
	fill: yellow;
}

#circ2 .svg-shape-circle {
	fill: green;
}

#circ3 .svg-shape-circle {
	fill: blue;
}

#svg-button {
        min-width: 48px;
        min-height: 48px;
        max-height: 48px;
}

#background-circle .svg-shape-circle {
	fill: lightslategray;
}


svg-shape.xml

<?xml version="1.0"?>
<bindings
	xmlns="http://www.mozilla.org/xbl" 
	xmlns:xbl="http://www.mozilla.org/xbl" 
	xmlns:html="http://www.w3.org/1999/xhtml" 
	xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
	xmlns:svg="http://www.w3.org/2000/svg" 
	xmlns:xlink="http://www.w3.org/1999/xlink"
>
	<binding id="shape">
		<implementation>
			<field name="svg_shape_box"/>
			<field name="svg_shape_type"/>
			<field name="svg_shape_rect"/>
			<field name="svg_shape_circle"/>
			<field name="svg_shape_ellipse"/>
			<field name="svg_shape_text"/>
			<field name="svg_shape_stroke"/>
			<field name="svg_shape_corner"/>
			<field name="svg_shape_loaded"/>
			<property name="disabled">
				<getter>
	<![CDATA[
	return this.getAttribute('disabled') == 'true';
	]]>
				</getter>
				<setter>
	<![CDATA[
  	if (val) {
	  	this.setAttribute('disabled', 'true');
  	}
  	else {
  		this.removeAttribute('disabled');
  	}
	return val;
	]]>
				</setter>
			</property>
			<property name="label">
				<setter>
	<![CDATA[
	this.setText(val);
	this.setAttribute("label", val);
	return val;
	]]>
				</setter>
			</property>
			<constructor>
	<![CDATA[
	
	const STROKE_WIDTH = 4;
	const CORNER_RADIUS = 0;
	
	this.svg_shape_type = this.getAttribute("type");
	if (!this.svg_shape_type) this.svg_shape_type = "rect";
	
	this.svg_shape_corner = parseInt(this.getAttribute("radius"));
	if (isNaN(this.svg_shape_corner)) this.svg_shape_corner = CORNER_RADIUS;
	
	//This should not be necessary, but unfortunately getting the value
	//from the stylesheet programmatically is not possible in current builds
	
	this.svg_shape_stroke = parseInt(this.getAttribute("stroke-width"));
	if (isNaN(this.svg_shape_stroke)) this.svg_shape_stroke = STROKE_WIDTH;

	var my_this = this;
	
	this.svg_shape_loaded = false;

	this.svg_shape_box = document.getAnonymousElementByAttribute(this, "anonid", "svg-shape-box");
	this.svg_shape_rect = document.getAnonymousElementByAttribute(this, "anonid", "svg-shape-rect");
	this.svg_shape_circle = document.getAnonymousElementByAttribute(this, "anonid", "svg-shape-circle");
	this.svg_shape_ellipse = document.getAnonymousElementByAttribute(this, "anonid", "svg-shape-ellipse");
	this.svg_shape_text = document.getAnonymousElementByAttribute(this, "anonid", "svg-shape-text");
	
	my_func = function myFunction(event){ window.setTimeout(function(xbl){ xbl.click(); }, 10, my_this); }
	this.svg_shape_box.addEventListener("click", my_func, true);

	my_func = function myFunction(){ my_this.doLayout();}
	window.addEventListener("load", my_func, true);

	]]>
			</constructor>
			<method name="doLayout">
				<body>
	<![CDATA[
	
	if (this.svg_shape_loaded) return;
	
	this.svg_shape_loaded = true;
	
	var box_w = this.boxObject.width;
	var box_h = this.boxObject.height;
	var cx = box_w / 2;
	var cy = box_h / 2;
			
	var stroke_w = this.svg_shape_stroke;
	
	//Either of the following should work, but don't
  	//var stroke_w = document.defaultView.getComputedStyle(this.svg_shape_rect, "").getPropertyCSSValue("stroke-width").getFloatValue(CSSPrimitiveValue.CSS_PX);
  	//var stroke_w = this.svg_shape_rect.getPresentationAttribute("stroke-width").getFloatValue(CSSPrimitiveValue.CSS_PX);
	
	this.svg_shape_box.setAttribute("width", box_w);
	this.svg_shape_box.setAttribute("height", box_h);
		
	if (this.svg_shape_type == "circle") {
		var r = Math.min(cx, cy);
		
		this.svg_shape_circle.setAttribute("cx", cx);
		this.svg_shape_circle.setAttribute("cy", cy);
		this.svg_shape_circle.setAttribute("r", r - stroke_w);
	
		this.svg_shape_rect.setAttribute("style", "display: none");
		this.svg_shape_ellipse.setAttribute("style", "display: none");
	}
	else if (this.svg_shape_type == "ellipse") {
		
		this.svg_shape_ellipse.setAttribute("cx", cx);
		this.svg_shape_ellipse.setAttribute("cy", cy);
		this.svg_shape_ellipse.setAttribute("rx", cx);
		this.svg_shape_ellipse.setAttribute("ry", cy);
	
		this.svg_shape_circle.setAttribute("style", "display: none");
		this.svg_shape_rect.setAttribute("style", "display: none");
	}
	else {
		this.svg_shape_rect.setAttribute("x", stroke_w / 2);
		this.svg_shape_rect.setAttribute("y", stroke_w / 2);
		this.svg_shape_rect.setAttribute("width", box_w - ((stroke_w / 2) + stroke_w));
		this.svg_shape_rect.setAttribute("height", box_h - ((stroke_w / 2) + stroke_w));
		this.svg_shape_rect.setAttribute("rx", this.svg_shape_corner);
		this.svg_shape_rect.setAttribute("ry", this.svg_shape_corner);

		this.svg_shape_circle.setAttribute("style", "display: none");
		this.svg_shape_ellipse.setAttribute("style", "display: none");
	}
	  	
	
	this.svg_shape_text.setAttribute("x", cx);
	this.svg_shape_text.setAttribute("y", cy - stroke_w);
		
  	this.setText(this.getAttribute("label"));

	//Not used but useful to know
	//var text_w = this.svg_shape_text.getComputedTextLength();	
  	//var text_h = document.defaultView.getComputedStyle(this.svg_shape_text, "").getPropertyCSSValue("font-size").getFloatValue(CSSPrimitiveValue.CSS_PX);

	]]>
				</body>
			</method>
			<method name="setText">
				<parameter name="text" /> 
				<body>
	<![CDATA[
	
	if (!text) text = "";
	this.svg_shape_text.firstChild.nodeValue = text;

	]]>
				</body>
			</method>
		</implementation>
		<resources>
			<stylesheet src="svg-shape.css" />
		</resources>
		<content>
			<xul:hbox class="box-inherit" xbl:inherits="align,dir,pack,orient" align="center" pack="center" flex="1">
				<svg:svg anonid="svg-shape-box" width="10px" height="10px">
					<svg:g>
						<svg:rect anonid="svg-shape-rect" class="svg-shape-rect" x="2" y="2" width="10" height="10" rx="8" ry="8"/>
						<svg:circle anonid="svg-shape-circle" class="svg-shape-circle" cx="2" cy="2" r="10"/>
						<svg:ellipse anonid="svg-shape-ellipse" class="svg-shape-ellipse" cx="2" cy="2" rx="10" ry="10"/>
						<svg:text anonid="svg-shape-text" x="16" y="0" class="svg-shape-text">Text</svg:text>
					</svg:g>
				</svg:svg>
			</xul:hbox>
		</content>
	</binding>
</bindings>


svg-shape.css

.box-inherit {
	-moz-box-orient: inherit;
	-moz-box-pack: inherit;
	-moz-box-align: inherit;
	-moz-box-direction: inherit;
}

/* Text shape */

.svg-shape-text {
	font-weight: inherit;
	font-style: inherit;
	font-size: inherit;
	font-family: inherit;
	dominant-baseline: middle;
	text-anchor: middle;
}

/* Rect shape */

.svg-shape-rect {
	stroke-width: inherit;
}

/* Circle shape */

.svg-shape-circle {
	stroke-width: inherit;
}

/* Ellipse shape */

.svg-shape-ellipse {
	stroke-width: inherit;
}