浏览代码

added ColorPicker and GradientEditor

ncannasse 12 年之前
父节点
当前提交
7f64f6af43
共有 4 个文件被更改,包括 1285 次插入0 次删除
  1. 642 0
      h2d/comp/ColorPicker.hx
  2. 622 0
      h2d/comp/GradientEditor.hx
  3. 4 0
      h2d/comp/Parser.hx
  4. 17 0
      h2d/css/default.css

+ 642 - 0
h2d/comp/ColorPicker.hx

@@ -0,0 +1,642 @@
+package h2d.comp;
+import h2d.css.Defs;
+import h2d.css.Fill;
+
+private enum RGBA {
+	R;
+	G;
+	B;
+	A;
+}
+
+private enum ChangeState {
+	SNone;
+	SColor;
+	SPalette;
+	SChart;
+	SRed;
+	SGreen;
+	SBlue;
+	SAlpha;
+}
+
+private enum CompStyle {
+	GaugeLabel;
+	GaugeInput;
+	ColorLabel;
+	ColorInput;
+}
+
+private class Style {
+	static public function new () {
+	}
+	
+	public static function get(kind:CompStyle) {
+		var style = new h2d.css.Style();
+		switch(kind) {
+			case GaugeLabel: 	style.fontSize = 11;
+			case GaugeInput: 	style.width = 24;
+								style.height = 10;
+								style.fontSize = 11;
+			case ColorLabel: 	style.fontSize = 14;
+			case ColorInput: 	style.width = 50;
+								style.height = 10;
+								style.fontSize = 11;
+		}
+		return style;
+	}
+}
+
+private class Arrow extends h2d.css.Fill {
+	public function new (parent, x:Float, y:Float, ang = 0., color = 0xff000000) {
+		super(parent);
+		addPoint(-5, -4, color);
+		addPoint(-5, 4, color);
+		addPoint(0, 0, color);
+		addPoint(0, 0, color);
+		rotation = ang;
+		this.x = x;
+		this.y = y;
+	}
+}
+
+private class Cross extends h2d.css.Fill {
+	var size:Float;
+	
+	public function new (parent, size:Float, color = 0xff000000) {
+		super(parent);
+		this.size = size;
+		lineRect(FillStyle.Color(color), 0, 0, size, size, 1);
+	}
+	
+	public function setColor(color:UInt) {
+		reset();
+		lineRect(FillStyle.Color(color), 0, 0, size, size, 1);
+	}
+}
+
+private class Color extends h2d.Sprite{
+	public var width :Float;
+	public var height :Float;
+	public var color(default, set):UInt = 0xFFFFFFFF;
+	public var preview(default, set):UInt = 0xFFFFFFFF;
+	public var alpha(default, set):Float = 1.;
+	
+	var canvas:h2d.css.Fill;
+	var label : h2d.comp.Label;
+	var input : h2d.comp.Input;
+	
+	
+	public function new (ix, iy, iw, ih, parent) {
+		super(parent);
+		x = ix;
+		y = iy;
+		width = iw;
+		height = ih;
+		init();
+	}
+	
+	function set_color(v:UInt) {
+		if(v != color) {
+			color = v;
+			drawAll();
+		}
+		return color;
+	}
+	
+	function set_preview(v:UInt) {
+		if(v != preview) {
+			preview = v;
+			drawAll();
+		}
+		return color;
+	}
+	
+	function set_alpha(v:Float) {
+		alpha = v;
+		drawAll();
+		return color;
+	}
+	
+	public function updateColor(v:UInt) {
+		color = v;
+		input.value = StringTools.hex(preview, 6).substr(2);
+	}
+	
+	function init() {
+		label = new h2d.comp.Label("#", this);
+		label.setStyle(Style.get(ColorLabel));
+		input = new h2d.comp.Input(this);
+		input.setStyle(Style.get(ColorInput));
+		input.value = "FFFFFF";
+		input.x = 15; input.y = 3;
+		input.onChange = function (e) {
+			input.value = input.value.toUpperCase();
+			if(input.value.length > 6) {
+				input.value = input.value.substr(0, 6);
+				return;
+			}
+			var v = Std.parseInt("0x" + input.value);
+			if (v != null) {
+				color = 255 << 24 | v;
+				ColorPicker.change = SColor;
+			}
+		};
+		
+		canvas = new h2d.css.Fill(this);
+		canvas.y = 2 + height * 0.5;
+		drawAll();
+	}
+	
+	public function drawAll() {
+		canvas.reset();
+		canvas.fillRectColor(0, 0, width, height * 0.5, preview);
+		canvas.fillRectColor(0, 0, width * 0.5, height * 0.5, color);
+		canvas.fillRectColor(0, height * 0.5 - 4, width, 4, 0xFF000000);
+		canvas.fillRectColor(0, height * 0.5 - 4, width * alpha, 4, 0xFFFFFFFF);
+		canvas.lineRect(FillStyle.Color(ColorPicker.borderColor), 0, 0, width, height * 0.5, 1);
+	}
+}
+
+private class Palette extends h2d.Sprite {
+	public var width :Float;
+	public var height :Float;
+	public var color(default, set):UInt;
+	
+	var canvas:h2d.css.Fill;
+	var interact:h2d.Interactive;
+	var cursor:h2d.Sprite;
+	
+	public function new (ix, iy, iw, ih, parent) {
+		super(parent);
+		x = ix;
+		y = iy;
+		width = iw;
+		height = ih;
+		init();
+	}
+	
+	function init() {
+		canvas = new h2d.css.Fill(this);
+		cursor = new h2d.Sprite(this);
+		var larrow = new Arrow(cursor, 0, 0, 0, 0xffcccccc);
+		var rarrow = new Arrow(cursor, width, 0, Math.PI, 0xffcccccc);
+		interact = new h2d.Interactive(width + 16, height, canvas);
+		interact.x = -8;
+		interact.onPush = function(e) {
+			setCursor(e.relY);
+			interact.startDrag(function(e) {
+				if( e.kind == EMove )
+					setCursor(e.relY);
+			});
+			ColorPicker.change = SPalette;
+		}
+		interact.onRelease = function(e) {
+			interact.stopDrag();
+			ColorPicker.change = SNone;
+		}
+		color = getColor(0);
+		drawAll();
+	}
+	
+	function set_color(v:UInt) {
+		color = v;
+		if(!ColorPicker.change.equals(SPalette))
+			updateCursor();
+		drawAll();
+		return color;
+	}
+	
+	function updateCursor() {
+		var hsl = ColorPicker.INTtoHSL(color);
+		cursor.y = Math.round(Math.max(0, Math.min(height, (1 - hsl[0]) * height)));
+	}
+	
+	public function drawAll() {
+		var s = 1;
+		var l = 0.5;
+		var seg = height / 6;
+		canvas.reset();
+		for (i in 0...6) {
+			var up = ColorPicker.HSLtoINT(1 - i / 6, s, l);
+			var down = ColorPicker.HSLtoINT(1 - (i + 1) / 6, s, l);
+			canvas.fillRectGradient(0, i * seg, width, seg, up, up, down, down);
+		}
+		canvas.lineRect(FillStyle.Color(ColorPicker.borderColor), 0, 0, width, height, 1);
+	}
+	
+	public function setCursor(dy:Float) {
+		cursor.y = Math.round(Math.max(0, Math.min(height, dy)));
+		color = getColor(cursor.y);
+	}
+	
+	public function getColor(py:Float) {
+		var h = 1 - (py / height);
+		var s = 1;
+		var l = 0.5;
+		return(ColorPicker.HSLtoINT(h, s, l));
+	}
+	
+	public function setColorFrom(newColor:UInt) {
+		var rgb = ColorPicker.INTtoRGB(newColor);
+		var hsl = ColorPicker.RGBtoHLS(rgb[0], rgb[1], rgb[2]);
+		hsl[1] = 1; hsl[2] = 0.5;
+		rgb = ColorPicker.HSLtoRGB(hsl[0], hsl[1], hsl[2]);
+		color = ColorPicker.RGBtoINT(rgb[0], rgb[1], rgb[2]);
+	}
+}
+
+private class Chart extends h2d.Sprite{
+	public var width :Int;
+	public var height :Int;
+	public var refColor(default, set):UInt = 0xffffffff;
+	public var color:UInt = 0xffffffff;
+	
+	var ray :Float;
+	var canvas:h2d.css.Fill;
+	var interact:h2d.Interactive;
+	var cursor:h2d.Sprite;
+	var lastPos:h3d.Vector;
+	var cross:Cross ;
+	
+	public function new (ix, iy, iw, ih, ray, parent) {
+		super(parent);
+		x = ix;
+		y = iy;
+		width = iw;
+		height = ih;
+		this.ray = ray;
+		init();
+	}
+	
+	function init() {
+		canvas = new h2d.css.Fill(this);
+		cursor = new h2d.Sprite(this);
+		cross = new Cross(cursor, ray * 2);
+		cross.x = cross.y = -ray;
+		interact = new h2d.Interactive(width, height, canvas);
+		interact.onPush = function(e) {
+			setCursor(e.relX, e.relY);
+			interact.startDrag(function(e) {
+				if( e.kind == EMove )
+					setCursor(e.relX, e.relY);
+			});
+			ColorPicker.change = SChart;
+		}
+		interact.onRelease = function(e) {
+			interact.stopDrag();
+			ColorPicker.change = SNone;
+		}
+		drawAll();
+		setCursor(0, 0);
+	}
+	
+	public function setCursor(dx:Float, dy:Float) {
+		cursor.x = Math.max(ray + 1, Math.min(width - ray - 1, dx));
+		cursor.y = Math.max(ray + 1, Math.min(height - ray - 1, dy));
+		lastPos = normalizePos(dx, dy);
+		color = getColor(lastPos.x, lastPos.y);
+		cross.setColor(ColorPicker.complementaryColor(color));
+	}
+	
+	function set_refColor(v:UInt) {
+		refColor = v;
+		color = getColor(lastPos.x, lastPos.y);
+		cross.setColor(ColorPicker.complementaryColor(color));
+		drawAll();
+		return refColor;
+	}
+	
+	function normalizePos(dx:Float, dy:Float) {
+		var px = 1 - Math.min(width, Math.max(0, dx)) / width;
+		var py = 1 - Math.min(height, Math.max(0, dy)) / height;
+		return new h3d.Vector(px, py);
+	}
+	
+	public function drawAll() {
+		canvas.reset();
+		var rgb = [(refColor >> 16) & 0xFF, (refColor >> 8) & 0xFF,  refColor & 0xFF];
+		for (i in 0...width>>1) {
+			for (j in 0...height>>1) {
+				var di = Math.max(0, Math.min(width, i * 2));
+				var dj = Math.max(0, Math.min(width, j * 2));
+				var dw = (1 - di / width);
+				var dh = (1 - dj / height);
+				var r = Math.round(rgb[0] * dh);
+				var g = Math.round(rgb[1] * dh);
+				var b = Math.round(rgb[2] * dh);
+				var max = Math.max(r, Math.max(g, b));
+				r = Math.round(r + (max - r) * dw);
+				g = Math.round(g + (max - g) * dw);
+				b = Math.round(b + (max - b) * dw);
+				var c = (255 << 24) | (r << 16) | (g << 8) | b;
+				canvas.fillRectColor(i * 2, j * 2, 2, 2, c);
+			}
+		}
+		canvas.lineRect(FillStyle.Color(ColorPicker.borderColor), 0, 0, width, height, 1);
+	}
+	
+	function getColor(dw:Float, dh:Float) {
+		var rgb = [(refColor >> 16) & 0xFF, (refColor >> 8) & 0xFF,  refColor & 0xFF];
+		var r = Math.round(rgb[0] * dh);
+		var g = Math.round(rgb[1] * dh);
+		var b = Math.round(rgb[2] * dh);
+		var max = Math.max(r, Math.max(g, b));
+		r = Math.round(r + (max - r) * dw);
+		g = Math.round(g + (max - g) * dw);
+		b = Math.round(b + (max - b) * dw);
+		return ColorPicker.RGBtoINT(r, g, b);
+	}
+	
+	public function setColorFrom(newColor:UInt) {
+		var rgb = ColorPicker.INTtoRGB(newColor);
+		var hsl = ColorPicker.RGBtoHLS(rgb[0], rgb[1], rgb[2]);
+		hsl[1] = 1; hsl[2] = 0.5;
+		rgb = ColorPicker.HSLtoRGB(hsl[0], hsl[1], hsl[2]);
+		refColor = ColorPicker.RGBtoINT(rgb[0], rgb[1], rgb[2]);
+		
+		var rgb = ColorPicker.INTtoRGB(newColor);
+		var min = Math.min(rgb[0], Math.min(rgb[1], rgb[2]));
+		var max = Math.max(rgb[0], Math.max(rgb[1], rgb[2]));
+		var dx = 1 - min / max;
+		var dy = 1 - max / 255;
+		setCursor(dx * width, dy * height);
+	}
+}
+
+private class ColorGauge extends h2d.Sprite{
+	public var width :Int;
+	public var height :Int;
+	public var color(default, set):UInt = 0xffffffff;
+	public var ratio(get, null):Float;
+	
+	var canvas:h2d.css.Fill;
+	var interact:h2d.Interactive;
+	var cursor:h2d.Sprite;
+	var bindTo:RGBA;
+	var label : h2d.comp.Label;
+	var input : h2d.comp.Input;
+	var isFinal:Bool;
+	public function new (ix, iy, iw, ih, rgba, parent) {
+		super(parent);
+		x = ix;
+		y = iy;
+		width = iw;
+		height = ih;
+		bindTo = rgba;
+		init();
+	}
+	
+	function init() {
+		label = new h2d.comp.Label(bindTo.getName(), this);
+		label.setStyle(Style.get(GaugeLabel));
+		label.x = -45;
+		label.y = 2;
+		input = new h2d.comp.Input(this);
+		input.setStyle(Style.get(GaugeInput));
+		input.value = "255";
+		input.x = -30; input.y = 3;
+		input.onChange = function(e) {
+			setCursor(cursor.x);
+		};
+		
+		canvas = new h2d.css.Fill(this);
+		cursor = new h2d.Sprite(this);
+		cursor.x = width;
+		var larrow = new Arrow(cursor, 0, 0, -Math.PI / 2, 0xffcccccc);
+		var rarrow = new Arrow(cursor, 0, height, Math.PI / 2, 0xffcccccc);
+		interact = new h2d.Interactive(width, height + 8, canvas);
+		interact.y = -4;
+		interact.onPush = function(e) {
+			setCursor(e.relX);
+			interact.startDrag(function(e) {
+				if( e.kind == EMove )
+					setCursor(e.relX);
+			});
+			setState();
+		}
+		interact.onRelease = function(e) {
+			interact.stopDrag();
+			ColorPicker.change = SNone;
+		}
+		drawAll();
+	}
+	
+	function set_color(v:UInt) {
+		color = v;
+		if(!bindTo.equals(RGBA.A))
+			updateCursor();
+		drawAll();
+		return color;
+	}
+	
+	function setState() {
+		ColorPicker.change = switch(bindTo) {
+			case RGBA.R: ColorPicker.change = SRed;
+			case RGBA.G: ColorPicker.change = SGreen;
+			case RGBA.B: ColorPicker.change = SBlue;
+			case RGBA.A: ColorPicker.change = SAlpha;
+		}
+	}
+	
+	public function get_ratio() {
+		return cursor.x / width;
+	}
+	
+	public function updateCursor() {
+		var a = color >>> 24;
+		var r = (color >> 16) & 0xFF;
+		var g =	(color >> 8) & 0xFF;
+		var b = color & 0xFF;
+		cursor.x = Math.round(switch(bindTo) {
+			case RGBA.R: r * width / 255;
+			case RGBA.G: g * width / 255;
+			case RGBA.B: b * width / 255;
+			case RGBA.A: a * width / 255;
+		});
+		input.value = Std.string(Std.int(255 * ratio));
+	}
+	
+	public function setCursor(dx:Float) {
+		cursor.x = Math.round(Math.max(0, Math.min(width, dx)));
+		var r = (color >> 16) & 0xFF;
+		var g =	(color >> 8) & 0xFF;
+		var b = color & 0xFF;
+		color = switch(bindTo) {
+			case RGBA.R: ColorPicker.RGBtoINT(Math.round(255 * ratio), g, b);
+			case RGBA.G: ColorPicker.RGBtoINT(r, Math.round(255 * ratio), b);
+			case RGBA.B: ColorPicker.RGBtoINT(r, g, Math.round(255 * ratio));
+			case RGBA.A: color;
+		}
+		input.value = Std.string(Math.round(255 * ratio));
+	}
+	
+	public function drawAll() {
+		var r = (color >> 16) & 0xFF;
+		var g =	(color >> 8) & 0xFF;
+		var b = color & 0xFF;
+		var left:UInt;
+		var right:UInt;
+		switch(bindTo) {
+			case RGBA.R: left = ColorPicker.RGBtoINT(0, g, b);	right = ColorPicker.RGBtoINT(255, g, b);
+			case RGBA.G: left = ColorPicker.RGBtoINT(r, 0, b);	right = ColorPicker.RGBtoINT(r, 255, b);
+			case RGBA.B: left = ColorPicker.RGBtoINT(r, g, 0);	right = ColorPicker.RGBtoINT(r, g, 255);
+			case RGBA.A: left = 0xFF000000;						right = 0xFFFFFFFF;
+		}
+		canvas.reset();
+		canvas.fillRectGradient(0, 0, width, height, left, right, left, right);
+		canvas.lineRect(FillStyle.Color(ColorPicker.borderColor), 0, 0, width, height, 1);
+	}
+}
+
+
+
+/////////////////////////////////////////////////////////////////
+
+class ColorPicker extends h2d.comp.Component {
+	
+	public static var borderColor = 0xFFaaaaaa;
+	public static var change : ChangeState;
+	
+	var finalColor : Color;
+	var palette : Palette;
+	var chart : Chart;
+	var gaugeRed : ColorGauge;
+	var gaugeGreen : ColorGauge;
+	var gaugeBlue : ColorGauge;
+	var gaugeAlpha : ColorGauge;
+	
+	public function new(?parent) {
+		super("colorpicker", parent);
+		init();
+	}
+	
+	inline function init() {
+		finalColor = new Color(15, 8, 175, 45, this);
+		palette = new Palette(16, 65, 20, 140, this);
+		chart = new Chart(50, 65, 140, 140, 3.5, this);
+		gaugeRed = new ColorGauge(50, 220, 140, 15, RGBA.R, this);
+		gaugeGreen = new ColorGauge(50, 245, 140, 15, RGBA.G, this);
+		gaugeBlue = new ColorGauge(50, 270, 140, 15, RGBA.B, this);
+		gaugeAlpha = new ColorGauge(50, 295, 140, 15, RGBA.A, this);
+		chart.refColor = palette.color;
+		change = SNone;
+		
+		flash.Lib.current.stage.addEventListener(flash.events.Event.ENTER_FRAME, doUpdate);
+	}
+	
+	function doUpdate(e:flash.events.Event) {
+		finalColor.preview = chart.color;
+		if(change.equals(SNone)) {
+			if(finalColor.color != chart.color) {
+				finalColor.updateColor(chart.color);
+				onChange(finalColor.color);
+			}
+			return;
+		}
+			
+		var p = new flash.geom.Point(flash.Lib.current.mouseX, flash.Lib.current.mouseY);
+		switch(change) {
+			case SColor:	palette.setColorFrom(finalColor.color);
+							chart.setColorFrom(finalColor.color);
+			case SPalette:	chart.refColor = palette.color;
+			case SRed:		chart.setColorFrom(gaugeRed.color);
+							palette.color = chart.refColor;
+			case SGreen:	chart.setColorFrom(gaugeGreen.color);
+							palette.color = chart.refColor;
+			case SBlue:		chart.setColorFrom(gaugeBlue.color);
+							palette.color = chart.refColor;
+			case SAlpha:	finalColor.alpha = gaugeAlpha.ratio;
+			default:
+		}
+		
+		gaugeRed.color = chart.color;
+		gaugeGreen.color = chart.color;
+		gaugeBlue.color = chart.color;
+	}
+	
+	public function show (color:UInt) {
+		finalColor.color = color;
+		palette.setColorFrom(finalColor.color);
+		chart.setColorFrom(finalColor.color);
+		visible = true;
+	}
+	public function hide () {
+		visible = false;
+	}
+	
+	public dynamic function onChange( value : UInt ) {
+	}
+	
+//////////////////
+	inline public static function INTtoRGB(color:UInt) {
+		return [(color >> 16) & 0xFF, (color >> 8) & 0xFF,  color & 0xFF];
+	}
+	
+	inline public static function INTtoHSL(color:UInt) {
+		var rgb = INTtoRGB(color);
+		return RGBtoHLS(rgb[0], rgb[1], rgb[2]);
+	}
+	
+	inline public static function RGBtoINT(r:Int, g:Int, b:Int, a:Int = 255) {
+		return (a << 24) | (r << 16) | (g << 8) | b;
+	}
+	
+	inline public static function RGBtoHLS(r:Float, g:Float, b:Float) {
+		r /= 255;
+		g /= 255;
+		b /= 255;
+		var max = Math.max(r, Math.max(g, b));
+		var min = Math.min(r, Math.min(g, b));
+		var med = (max + min) / 2;
+		var h = med;
+		var s = med;
+		var l = med;
+		if(max == min)
+			h = s = 0;
+		else {
+			var d = max - min;
+			s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
+			if(max == r) 		h = (g - b) / d + (g < b ? 6 : 0);
+			else if(max == g) 	h = (b - r) / d + 2;
+			else if(max == b) 	h = (r - g) / d + 4;
+			h /= 6;
+		}
+		return [h, s, l];
+	}
+	
+	inline public static function HSLtoINT(h:Float, s:Float, l:Float) {
+		var rgb = HSLtoRGB(h, s, l);
+		return RGBtoINT(rgb[0], rgb[1], rgb[2]);
+	}
+	
+	inline public static function HSLtoRGB(h:Float, s:Float, l:Float) {
+		var r, g, b;
+		if(s == 0)
+			r = g = b = l;
+		else {
+			function hue2rgb(p:Float, q:Float, t:Float) {
+				if(t < 0) t += 1;
+				if(t > 1) t -= 1;
+				if(t < 1 / 6) return p + (q - p) * 6 * t;
+				if(t < 1 / 2) return q;
+				if(t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
+				return p;
+			}
+			var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+			var p = 2 * l - q;
+			r = hue2rgb(p, q, h + 1 / 3);
+			g = hue2rgb(p, q, h);
+			b = hue2rgb(p, q, h - 1 / 3);
+		}
+		return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
+	}
+	
+	inline public static function complementaryColor (color:UInt) {
+		var rgb = INTtoRGB(color);
+		var r = rgb[0] ^ 0xFF;
+		var g = rgb[1] ^ 0xFF;
+		var b = rgb[2] ^ 0xFF;
+		return (255 << 24) | (r << 16) | (g << 8) | b;
+	}
+}

+ 622 - 0
h2d/comp/GradientEditor.hx

@@ -0,0 +1,622 @@
+package h2d.comp;
+import h2d.css.Defs;
+import h2d.css.Fill;
+
+private typedef Key = { x:Float, value:UInt };
+
+private enum KCursor {
+	KAlpha;
+	KColor;
+}
+
+private enum CompStyle {
+	CLabel;
+	CInput;
+	CInputSmall;
+}
+
+private class Style {
+	static public function new () {
+	}
+	
+	public static function get(kind:CompStyle) {
+		var style = new h2d.css.Style();
+		switch(kind) {
+			case CLabel: 		style.fontSize = 12;
+			case CInputSmall: 	style.width = 24;
+								style.height = 10;
+								style.fontSize = 11;
+			case CInput: 		style.width = 50;
+								style.height = 10;
+								style.fontSize = 11;
+		}
+		return style;
+	}
+}
+
+private class CFlag extends h2d.css.Fill {
+	var container:h2d.Sprite;
+	
+	public function new (parent, x:Float, y:Float, ang = 0., color = 0xff000000, border = 0xFFaaaaaa) {
+		super(parent);
+		container = new h2d.Sprite(parent);
+		container.rotation = ang;
+		container.x = x;
+		container.y = y;
+		//bg
+		var bgarrow = new Fill(container);
+		bgarrow.addPoint(-5, -4, border);
+		bgarrow.addPoint(5, -4, border);
+		bgarrow.addPoint(0, 0, border);
+		bgarrow.addPoint(0, 0, border);
+		var bgsquare = new Fill(container);
+		bgsquare.addPoint(-5, -9, border);
+		bgsquare.addPoint(5, -9, border);
+		bgsquare.addPoint(-5, 1, border);
+		bgsquare.addPoint(5, 1, border);
+		bgsquare.y = - 5;
+		//color
+		var arrow = new Fill(container);
+		arrow.addPoint(-4, -5, color);
+		arrow.addPoint(4, -5, color);
+		arrow.addPoint(0, 0, color);
+		arrow.addPoint(0, 0, color);
+		var square = new Fill(container);
+		square.addPoint(-4, -8, color);
+		square.addPoint(4, -8, color);
+		square.addPoint(-4, 0, color);
+		square.addPoint(4, 0, color);
+		square.y = - 5;
+	}
+}
+
+private class Cursor extends h2d.Sprite {
+	public var value(default, set):UInt;
+	public var coeff(get, null):Float;
+	public var color:UInt = 0xFFFFFFFF;
+	public var bgcolor:UInt = 0xFFFF00FF;
+	public var cursor:h2d.Sprite;
+	public var kind:KCursor;
+	var ang:Float;
+	var interact:h2d.Interactive;
+	var flag:CFlag;
+	
+	public function new (ix, iy, kind, value, ang, parent) {
+		super(parent);
+		x = ix;
+		y = iy;
+		this.value = value;
+		this.ang = ang;
+		this.kind = kind;
+		init();
+	}
+	
+	function set_value(v) {
+		value = v;
+		if(flag != null) {
+			switch(kind) {
+				case KColor: color = value;
+				case KAlpha: color = (255 << 24) | (value << 16) | (value << 8) | value;
+			}
+			flag.remove();
+			flag = new CFlag(cursor, 0, 0, ang, color, bgcolor);
+		}
+		return value;
+	}
+	
+	function get_coeff() {
+		return x / GradientEditor.boxWidth;
+	}
+	
+	function init() {
+		cursor = new h2d.Sprite(this);
+		switch(kind) {
+			case KColor: color = value;
+			case KAlpha: color = (255 << 24) | (value << 16) | (value << 8) | value;
+		}
+		flag = new CFlag(cursor, 0, 0, ang, color, bgcolor);
+		interact = new h2d.Interactive(10, 14, this);
+		interact.x = -5;
+		if(kind.equals(KAlpha))
+			interact.y = -14;
+			
+		interact.onPush = function(e) drag();
+		interact.onRelease = function(e) stopDrag();
+	}
+	
+	public function drag() {
+		interact.startDrag(function(e) {
+			if( e.kind == EMove ){
+				setCursor(e.relX);
+				if(e.relY < - 6 ||  e.relY > 16 + 6)
+					GradientEditor.dragOut = true;
+				else GradientEditor.dragOut = false;
+			}
+			GradientEditor.dragTarget = this ;
+		});
+			
+		GradientEditor.updateTarget = this;
+		this.parent.addChild(this);
+	}
+	
+	public function stopDrag() {
+		interact.stopDrag();
+		GradientEditor.dragTarget = null ;
+		GradientEditor.dragOut = false;
+	}
+	
+	public function select() {
+		bgcolor = 0xFFFF00FF;
+		flag.remove();
+		flag = new CFlag(cursor, 0, 0, ang, color, bgcolor);
+		
+		if(GradientEditor.colorPicker.visible)
+			GradientEditor.colorPicker.show(color);
+	}
+	
+	public function unselect() {
+		bgcolor = 0xFFAAAAAA;
+		flag.remove();
+		flag = new CFlag(cursor, 0, 0, ang, color, bgcolor);
+	}
+	
+	public function setCursor(px:Float) {
+		x = Math.max(0, Math.min(GradientEditor.boxWidth, x-5+px));
+	}
+}
+
+	
+private class AlphaSelector extends h2d.Sprite {
+	public var target(default, set):Cursor;
+	var title:h2d.comp.Label;
+	var slider:h2d.comp.Slider;
+	var alphaInput:h2d.comp.Input;
+	var locLabel:h2d.comp.Label;
+	var locInput:h2d.comp.Input;
+	
+	public function new (ix, iy, parent) {
+		super(parent);
+		x = ix;
+		y = iy;
+		init();
+	}
+	
+	function init() {
+		title = new h2d.comp.Label("Alpha", this);
+		title.addStyle(Style.get(CLabel));
+		
+		slider = new h2d.comp.Slider(this);
+		slider.x = 55; slider.y = 4;
+		slider.onChange = function(v) { updateSlider(v); };
+		slider.value = 1;
+		
+		alphaInput = new h2d.comp.Input(this);
+		alphaInput.addStyle(Style.get(CInputSmall));
+		alphaInput.x = 170;
+		alphaInput.value = "255";
+		alphaInput.onChange = function(e) {
+			var v = Std.parseFloat(alphaInput.value);
+			if (!Math.isNaN(v)) {
+				v = Math.min(255, v) / 255;
+				updateSlider(v);
+				slider.value = v;
+			}
+		};
+		
+		locLabel = new h2d.comp.Label("Location             %", this);
+		locLabel.setStyle(Style.get(CLabel));
+		locLabel.x = 255;
+		locInput = new h2d.comp.Input(this);
+		locInput.setStyle(Style.get(CInput));
+		locInput.x = 320;
+		locInput.value = "100.00";
+		locInput.onChange = function(e) {
+			var v = Std.parseFloat(locInput.value);
+			if (!Math.isNaN(v)) {
+				v = Math.min(100, v);
+				locInput.value = Std.string(Math.floor(v * 100) / 100);
+				target.setCursor( v * GradientEditor.boxWidth / 100 );
+			}
+		};
+	}
+	
+	public function update() {
+		if(target == null)
+			return;
+		locInput.value = Std.string(Math.floor(target.coeff * 100 * 100) / 100);
+	}
+	
+	function set_target(cursor:Cursor) {
+		target = cursor;
+		var v = target.value / 255;
+		slider.value = v;
+		locInput.value = Std.string(Math.floor(target.coeff * 100 * 100) / 100);
+		updateSlider(v);
+		return target;
+	}
+	
+	function updateSlider(v:Float) {
+		var alpha = Math.round(255 * v);
+		alphaInput.value = Std.string(alpha);
+		if(target == null)
+			return;
+		target.value = alpha;
+	}
+}
+
+private class ColorSelector extends h2d.Sprite {
+	public var target(default, set):Cursor;
+	var title:h2d.comp.Label;
+	var locLabel:h2d.comp.Label;
+	var locInput:h2d.comp.Input;
+	var colorInput:h2d.comp.Input;
+	var canvas:h2d.css.Fill;
+	var color:UInt = 0xFFFFFFFF;
+	var interact : h2d.Interactive;
+	
+	public function new (ix, iy, parent) {
+		super(parent);
+		x = ix;
+		y = iy;
+		init();
+	}
+	
+	public function update() {
+		if(target == null)
+			return;
+		locInput.value = Std.string(Math.floor(target.coeff * 100 * 100) / 100);
+	}
+	
+	function set_target(cursor:Cursor) {
+		target = cursor;
+		locInput.value = Std.string(Math.floor(target.coeff * 100 * 100) / 100);
+		color = target.value;
+		colorInput.value = StringTools.hex(color, 8).substr(2);
+		redraw();
+		return target;
+	}
+	
+	function init() {
+		title = new h2d.comp.Label("Color                         #", this);
+		title.setStyle(Style.get(CLabel));
+		
+		canvas = new Fill(this);
+		canvas.x = 45;
+		canvas.y = -8;
+		interact = new h2d.Interactive(110, 25, this);
+		interact.x = 50;
+		interact.y = -8;
+		interact.onPush = function(e) {
+			if(target == null)
+				return;
+			
+			if(!GradientEditor.colorPicker.visible){
+				GradientEditor.colorPicker.show(color);
+				GradientEditor.colorPicker.y = 220;
+				GradientEditor.colorPicker.onChange = function(v) {
+					color = target.value = v;
+					colorInput.value = StringTools.hex(v, 8).substr(2);
+					redraw();
+				}
+			}
+			else {
+				GradientEditor.colorPicker.onChange = function(v) { };
+				GradientEditor.colorPicker.hide();
+				GradientEditor.colorPicker.y = -500;
+			}
+		};
+		
+		colorInput = new h2d.comp.Input(this);
+		colorInput.setStyle(Style.get(CInput));
+		colorInput.x = 175;
+		colorInput.value = "FFFFFF";
+		colorInput.onChange = function (e) {
+			colorInput.value = colorInput.value.toUpperCase();
+			if(colorInput.value.length > 6) {
+				colorInput.value = colorInput.value.substr(0, 6);
+				return;
+			}
+			var v = Std.parseInt("0x" + colorInput.value);
+			if (v != null) {
+				color = target.value = 255 << 24 | v;
+				redraw();
+			}
+		};
+		
+		locLabel = new h2d.comp.Label("Location             %", this);
+		locLabel.setStyle(Style.get(CLabel));
+		locLabel.x = 265;
+		
+		locInput = new h2d.comp.Input(this);
+		locInput.setStyle(Style.get(CInput));
+		locInput.x = 330;
+		locInput.value = "100.00";
+		locInput.onChange = function(e) {
+			var v = Std.parseFloat(locInput.value);
+			if (!Math.isNaN(v)) {
+				v = Math.min(100, v);
+				locInput.value = Std.string(Math.floor(v * 100) / 100);
+				target.setCursor( v * GradientEditor.boxWidth / 100 );
+			}
+		};
+		
+		redraw();
+	}
+	
+	function redraw() {
+		canvas.reset();
+		canvas.fillRectColor(0, 0, 110, 25, color);
+		canvas.lineRect(FillStyle.Color(GradientEditor.borderColor), 0, 0, 110, 25, 1);
+	}
+}
+		
+		
+//////////////////////////////////////////////////////////
+
+class GradientEditor extends h2d.comp.Component {
+	
+	public static var borderColor = 0xFF888888;
+	public static var dragTarget:Cursor;
+	public static var dragOut:Bool;
+	public static var updateTarget:Cursor;
+	public static var boxWidth = 430;
+	var boxHeight = 100;
+	
+	var keys:Array<Key>;
+	var colorsKeys:Array<Cursor>;
+	var alphaKeys:Array<Cursor>;
+	
+	var box:h2d.Sprite;
+	var gradient:h2d.Sprite;
+	var hudAlpha: AlphaSelector;
+	var hudColor: ColorSelector;
+	
+	public static var colorPicker:ColorPicker;
+	
+	var interactUp:h2d.Interactive;
+	var interactDown:h2d.Interactive;
+	
+	var holdCursor:Cursor;
+	
+	public function new(?parent) {
+		super("gradienteditor", parent);
+		init();
+	}
+	
+	inline function init() {
+		colorsKeys = [];
+		alphaKeys = [];
+		
+		interactUp = new h2d.Interactive(boxWidth, 16, this);
+		interactUp.x = 10;
+		interactUp.y = 30 - 16;
+		interactUp.onPush =  function(e) createAlphaKey(e.relX, 0);
+		interactDown = new h2d.Interactive(boxWidth, 16, this);
+		interactDown.x = 10;
+		interactDown.y = 30 + boxHeight;
+		interactDown.onPush =  function(e) createColorKey(e.relX, boxHeight);
+		
+		box = new h2d.Sprite(this);
+		box.x = 10;
+		box.y = 30;
+		drawChecker();
+		
+		hudColor = new ColorSelector(20, box.y + boxHeight + 30, this);
+		hudAlpha = new AlphaSelector(20, box.y + boxHeight + 30, this);
+		hudAlpha.visible = false;
+		hudAlpha.y = 0;
+		
+		colorPicker = new ColorPicker(this);
+		colorPicker.y = -500;
+		colorPicker.hide();
+		
+		//
+		colorsKeys.push( new Cursor(0, boxHeight, KColor, 0xFFFFFFFF, Math.PI,  box));
+		colorsKeys.push( new Cursor(boxWidth, boxHeight, KColor, 0xFFFFFFFF, Math.PI, box));
+		alphaKeys.push( new Cursor(0, 0, KAlpha, 255, 0, box));
+		alphaKeys.push( new Cursor(boxWidth, 0, KAlpha, 255, 0, box));
+		updateTarget = colorsKeys[0];
+		updateKeys();
+		drawGradient();
+		
+		flash.Lib.current.stage.addEventListener(flash.events.Event.ENTER_FRAME, doUpdate);
+	}
+	
+	function doUpdate(e:flash.events.Event) {
+		if(dragTarget != null) {
+			if(dragOut) {
+				switch(dragTarget.kind) {
+					case KColor: if(colorsKeys.length > 1) {
+						colorsKeys.remove(dragTarget);
+						holdCursor = dragTarget;
+						dragTarget.visible = false;
+					}
+					case KAlpha: if(alphaKeys.length > 1) {
+						alphaKeys.remove(dragTarget);
+						holdCursor = dragTarget;
+						dragTarget.visible = false;
+					}
+				}
+			}
+			else if(holdCursor == dragTarget) {
+				holdCursor = null;
+				dragTarget.visible = true;
+				switch(dragTarget.kind) {
+					case KColor: colorsKeys.push(dragTarget);
+					case KAlpha: alphaKeys.push(dragTarget);
+				}
+			}
+			
+			hudAlpha.update();
+			hudColor.update();
+		}
+		else holdCursor = null;
+		
+		if(updateTarget != null) {
+			changeHud(updateTarget);
+			updateFlags(updateTarget);
+			updateTarget = null;
+		}
+		
+		updateKeys();
+		drawGradient();
+	}
+	
+	inline function createAlphaKey(px:Float, py:Float) {
+		var cursor = new Cursor(px, py, KAlpha, getAlphaAt(px / boxWidth), 0, box);
+		alphaKeys.push(cursor);
+		updateTarget = cursor;
+		cursor.drag();
+	}
+	
+	inline function createColorKey(px:Float, py:Float) {
+		var cursor = new Cursor(px, py, KColor, getColorAt(px / boxWidth), Math.PI, box);
+		colorsKeys.push(cursor);
+		updateTarget = cursor;
+		cursor.drag();
+	}
+	
+	inline function updateKeys() {
+		keys = [];
+		for (i in 0...colorsKeys.length) {
+			var k = colorsKeys[i];
+			var alpha = getAlphaAt(k.coeff);
+			var rgb = INTtoRGB(k.value);
+			keys.push( { x:k.coeff, value:RGBtoINT(rgb[0], rgb[1], rgb[2], alpha) } );
+		}
+		
+		for (i in 0...alphaKeys.length) {
+			var k = alphaKeys[i];
+			var alpha = k.value;
+			var rgb = INTtoRGB(getColorAt(k.coeff));
+			keys.push( { x:k.coeff, value:RGBtoINT(rgb[0], rgb[1], rgb[2], alpha) } );
+		}
+		
+		keys.sort(function(a, b) return Reflect.compare(a.x, b.x) );
+		if(keys[0].x != 0)
+			keys.insert(0, { x:0, value:keys[0].value } );
+		if(keys[keys.length - 1].x != 1)
+			keys.push( { x:1, value:keys[keys.length - 1].value } );
+	}
+	
+	function getARGBAt(x:Float) {
+		var alpha = getAlphaAt(x);
+		var rgb = INTtoRGB(getColorAt(x));
+		return RGBtoINT(rgb[0], rgb[1], rgb[2], alpha);
+	}
+	
+	function getAlphaAt(x:Float) {
+		alphaKeys.sort(function(a, b) return Reflect.compare(a.coeff, b.coeff) );
+		var prev = null;
+		var next = null;
+		for (i in 0...alphaKeys.length) {
+			var k = alphaKeys[i];
+			if (k.coeff == x)
+				return k.value;
+			else if (k.coeff < x)
+				prev = k;
+			else if (k.coeff > x) {
+				if(prev == null)
+					return k.value;
+				else next = k;
+				break;
+			}
+		}
+		if(next == null)
+			return prev.value;
+		var d = (x - prev.coeff) / (next.coeff - prev.coeff);
+		return Math.round(prev.value + (next.value - prev.value) * d);
+	}
+	
+	function getColorAt(x:Float) {
+		colorsKeys.sort(function(a, b) return Reflect.compare(a.coeff, b.coeff) );
+		var prev = null;
+		var next = null;
+		for (i in 0...colorsKeys.length) {
+			var k = colorsKeys[i];
+			if (k.coeff == x)
+				return k.value;
+			else if (k.coeff < x)
+				prev = k;
+			else if (k.coeff > x) {
+				if(prev == null)
+					return k.value;
+				else next = k;
+				break;
+			}
+		}
+		if(next == null)
+			return prev.value;
+			
+		var d = (x - prev.coeff) / (next.coeff - prev.coeff);
+		var pRGB = INTtoRGB(prev.value);
+		var nRGB = INTtoRGB(next.value);
+		var rgb = [];
+		for (i in 0...3)
+			rgb.push(Math.round(pRGB[i] + (nRGB[i] - pRGB[i]) * d));
+		return RGBtoINT(rgb[0], rgb[1], rgb[2]);
+	}
+	
+	inline function drawGradient() {
+		box.removeChild(gradient);
+		gradient = new h2d.Sprite(box);
+		for (i in 0...keys.length - 1) {
+			var c1 = keys[i];
+			var c2 = keys[i + 1];
+			var rect = new Fill(gradient);
+			rect.fillRectGradient(boxWidth * c1.x, 0, boxWidth * (c2.x - c1.x), boxHeight, c1.value, c2.value, c1.value, c2.value);
+		}
+		var borders = new Fill(gradient);
+		borders.lineRect(FillStyle.Color(borderColor), 0, 0, boxWidth, boxHeight, 2);
+	}
+	
+	inline function drawChecker() {
+		var checker = new h2d.Sprite(box);
+		var nb = 90;
+		var size = Math.ceil(boxWidth / nb);
+		for (i in 0...nb) {
+			for (j in 0...nb) {
+				if(i * size >= boxWidth) break;
+				if(j * size >= boxHeight) break;
+				var color = ((i + j) % 2 == 0) ? 0xFFFFFFFF:0xFFAAAAAA;
+				var rect = new Fill(checker);
+				rect.fillRect(FillStyle.Color(color), i * size, j * size, size, size);
+			}
+		}
+	}
+	
+	inline function changeHud(cursor:Cursor) {
+		switch(cursor.kind) {
+			case KAlpha: 	hudAlpha.target = cursor;
+							hudAlpha.visible = true;
+							hudAlpha.y = box.y + boxHeight + 30;
+							hudColor.visible = false;
+			case KColor:	hudColor.target = cursor;
+							hudColor.visible = true;
+							hudAlpha.visible = false;
+							hudAlpha.y =  0;
+		}
+	}
+	
+	inline function updateFlags(cursor:Cursor) {
+		for (c in alphaKeys) {
+			if (c == cursor)
+				c.select();
+			else c.unselect();
+		}
+		for (c in colorsKeys) {
+			if (c == cursor)
+				c.select();
+			else c.unselect();
+		}
+	}
+	
+	
+	inline public static function INTtoRGB(color:UInt) {
+		return [(color >> 16) & 0xFF, (color >> 8) & 0xFF,  color & 0xFF, color >>> 24];
+	}
+	
+	inline public static function RGBtoINT(r:Int, g:Int, b:Int, a:Int = 255) {
+		return (a << 24) | (r << 16) | (g << 8) | b;
+	}
+}

+ 4 - 0
h2d/comp/Parser.hx

@@ -45,6 +45,10 @@ class Parser {
 			c = new ItemList(parent);
 		case "input":
 			c = new Input(parent);
+		case "colorpicker":
+			c = new ColorPicker(parent);
+		case "gradienteditor":
+			c = new GradientEditor(parent);
 		case n:
 			var make = comps.get(n);
 			if( make != null )

+ 17 - 0
h2d/css/default.css

@@ -94,3 +94,20 @@ input:focus {
 	border : 1px solid white;
 	cursor-color : white;
 }
+
+colorpicker {
+	width : 200px;
+	height : 320px;
+	layout : absolute;
+	border : 1px solid gradient(#A0A0A0,#909090,#707070,#606060);
+	background-color : gradient(#303030,#252525,#252525,#252525);
+}
+
+gradienteditor {
+	width : 450px;
+	height : 200px;
+	layout : absolute;
+	border : 1px solid gradient(#A0A0A0,#909090,#707070,#606060);
+	background-color : gradient(#303030,#252525,#252525,#252525);
+}
+