Revision: 4861
          http://sourceforge.net/p/vexi/code/4861
Author:   clrg
Date:     2016-06-17 23:35:53 +0000 (Fri, 17 Jun 2016)
Log Message:
-----------
Refactor colorpicker
- clearer, commented code
- refactor to work using a colour "model"
- relabel 'intensity'->'value' as it was HSV logic, not HSI
- add spinners for colour components
- eliminate code fighting (functions calling each other unnecessarily)
- take out pointless threading

Modified Paths:
--------------
    branches/vexi3/org.vexi-vexi.widgets/src_main/org/vexi/tool/colorpicker.t

Modified: 
branches/vexi3/org.vexi-vexi.widgets/src_main/org/vexi/tool/colorpicker.t
===================================================================
--- branches/vexi3/org.vexi-vexi.widgets/src_main/org/vexi/tool/colorpicker.t   
2016-06-17 22:18:09 UTC (rev 4860)
+++ branches/vexi3/org.vexi-vexi.widgets/src_main/org/vexi/tool/colorpicker.t   
2016-06-17 23:35:53 UTC (rev 4861)
@@ -1,12 +1,14 @@
-<!-- Copyright 2009 - see COPYING for details [LGPL] -->
+<!-- Copyright 2016 - see COPYING for details [LGPL] -->
 
 <vexi xmlns:ui="vexi://ui" 
-      xmlns:meta="vexi://meta" 
-      xmlns="org.vexi.tool"
-      xmlns:lay="vexi.layout" 
+      xmlns:meta="vexi://meta"
+      xmlns:lay="vexi.layout"
       xmlns:util="vexi.util" 
+      xmlns:theme="vexi.theme"
+      xmlns:role="org.vexi.lib.role"
       xmlns:wi="vexi.widget"
-      xmlns:sync="vexi.util.sync">
+      xmlns="org.vexi.tool">
+    
     <meta:doc>
          <name>Colorpicker</name>
          <desc>A 24-bit colorpicker</desc>
@@ -20,7 +22,7 @@
          </todo>
     </meta:doc>
     
-    <ui:box>
+    <ui:box align="top">
         <ui:box orient="vertical" shrink="true">
             <ui:box>
                 <ui:box fill=":.image.colorpicker" id="colorpicker" 
layout="layer" shrink="true">
@@ -38,9 +40,9 @@
             <ui:box height="5"/>
             <ui:box>
                 <wi:bevel thickness="2" form="down" width="135" height="30">
-                    <ui:box fill="#000000" id="colorleft"/>
+                    <ui:box fill="#000000" id="colorleft"><ui:box/><ui:box 
id="alphaleft" display="false"/></ui:box>
                     <ui:box fill="#000000" id="targetcolor"/>
-                    <ui:box fill="#FFFFFF" id="colorright"/>
+                    <ui:box fill="#FFFFFF" id="colorright"><ui:box 
id="alpharight" display="false"/><ui:box/></ui:box>
                 </wi:bevel>
                 <ui:box width="35"/>
             </ui:box>
@@ -53,337 +55,470 @@
                 <ui:box width="30"/>
             </ui:box>
         </ui:box>
-        <ui:box width="20" shrink="true" />
-        <ui:box orient="vertical" shrink="true">
+        <ui:box id="trackers" orient="vertical" shrink="true">
             <ui:box>
-                <ui:box align="left" width="60" vshrink="true" text="Hue:" />
-                <wi:slider minvalue="0" maxvalue="1" interval="0.001" 
width="100" id="Hue" margin="5" />
-                <ui:box align="left" width="55" id="HueValue" />
+                <role:tooltipable align="right" width="60" text="H:" 
tooltip="Hue" />
+                <wi:slider id="Hue" part="hue" minwidth="100" margin="5 10"
+                       minvalue="0" maxvalue="360" interval="1" value="0" />
+                <wi:spin id="HueValue" part="hue" align="left" width="55"
+                       minvalue="0" maxvalue="360" interval="1" value="0" />
             </ui:box>
             <ui:box>
-                <ui:box align="left" width="60" vshrink="true" 
text="Saturation:" />
-                <wi:slider minvalue="0" maxvalue="1" interval="0.001" 
width="100" id="Saturation" margin="5" />
-                <ui:box align="left" width="55" id="SaturationValue" />
+                <role:tooltipable align="right" width="60" text="S:" 
tooltip="Saturation" />
+                <wi:slider id="Satur" part="saturation" minwidth="100" 
margin="5 10"
+                       minvalue="0" maxvalue="255" interval="1" value="0" />
+                <wi:spin id="SaturValue" part="saturation" align="left" 
width="55"
+                       minvalue="0" maxvalue="255" interval="1" value="0" />
             </ui:box>
             <ui:box>
-                <ui:box align="left" width="60" vshrink="true" 
text="Intensity:" />
-                <wi:slider minvalue="0" maxvalue="1" interval="0.001" 
width="100" id="Intensity" margin="5" />
-                <ui:box align="left" width="55" id="IntensityValue"/>
+                <role:tooltipable align="right" width="60" text="V:" 
tooltip="Value" />
+                <wi:slider id="Value" part="value" minwidth="100" margin="5 10"
+                       minvalue="0" maxvalue="255" interval="1" value="0" />
+                <wi:spin id="ValueValue" part="value" align="left" width="55"
+                       minvalue="0" maxvalue="255" interval="1" value="0" />
             </ui:box>
             <ui:box>
-                <ui:box align="left" width="60" vshrink="true" text="Red:" />
-                <wi:slider minvalue="0" maxvalue="255" interval="1" 
vshrink="true" width="100" id="Red" margin="5" />
-                <ui:box align="left" width="55" id="RedValue" />
+                <role:tooltipable align="right" width="60" text="R:" 
tooltip="Red" />
+                <wi:slider id="Red" part="red" minwidth="100" margin="5 10"
+                       minvalue="0" maxvalue="255" interval="1" value="0" />
+                <wi:spin id="RedValue" part="red" width="55"
+                       minvalue="0" maxvalue="255" interval="1" value="0" />
             </ui:box>
             <ui:box>
-                <ui:box align="left" width="60" vshrink="true" text="Green:" />
-                <wi:slider minvalue="0" maxvalue="255" interval="1" 
vshrink="true" width="100" id="Green" margin="5" />
-                <ui:box align="left" width="55" id="GreenValue" />
+                <role:tooltipable align="right" width="60" text="G:" 
tooltip="Green" />
+                <wi:slider id="Green" part="green" minwidth="100" margin="5 10"
+                       minvalue="0" maxvalue="255" interval="1" value="0" />
+                <wi:spin id="GreenValue" part="green" align="left" width="55"
+                       minvalue="0" maxvalue="255" interval="1" value="0" />
             </ui:box>
             <ui:box>
-                <ui:box align="left" width="60" vshrink="true" text="Blue:" />
-                <wi:slider minvalue="0" maxvalue="255" interval="1" 
vshrink="true" width="100" id="Blue" margin="5" />
-                <ui:box align="left" width="55" id="BlueValue" />
+                <role:tooltipable align="right" width="60" text="B:" 
tooltip="Blue" />
+                <wi:slider id="Blue" part="blue" minwidth="100" margin="5 10"
+                       minvalue="0" maxvalue="255" interval="1"value="0"  />
+                <wi:spin id="BlueValue" part="blue" align="left" width="55"
+                       minvalue="0" maxvalue="255" interval="1" value="0" />
             </ui:box>
+            <ui:box id="AlphaUI" display="false">
+                <role:tooltipable align="right" width="60" text="A:" 
tooltip="Alpha" />
+                <wi:slider id="Alpha" part="alpha" minwidth="100" margin="5 10"
+                       minvalue="0" maxvalue="255" interval="1" value="0" />
+                <wi:spin id="AlphaValue" part="alpha" align="left" width="55"
+                       minvalue="0" maxvalue="255" interval="1" value="0" />
+            </ui:box>
         </ui:box>
 
-        ////////
-        // api
-        thisbox.h ++= function() { return $Hue.value; } 
-        thisbox.h ++= function(v) { $Hue.value = v; cascade = v; } 
-        thisbox.s ++= function() { return $Saturation.value; } 
-        thisbox.s ++= function(v) { $Saturation.value = v; cascade = v; } 
-        thisbox.n ++= function() { return $Intensity.value; } 
-        thisbox.n ++= function(v) { $Intensity.value = v; cascade = v; } 
-        thisbox.r ++= function() { return $Red.value; } 
-        thisbox.r ++= function(v) { $Red.value = v; cascade = v; } 
-        thisbox.g ++= function() { return $Green.value; } 
-        thisbox.g ++= function(v) { $Green.value = v; cascade = v; } 
-        thisbox.b ++= function() { return $Blue.value; } 
-        thisbox.b ++= function(v) { $Blue.value = v; cascade = v; } 
-        thisbox.color_left ++= function() { return $colorleft.fill; } 
-        thisbox.color_left ++= function(v) { $colorleft.fill = v; cascade = v; 
} 
-        thisbox.color_right ++= function() { return $colorright.fill; } 
-        thisbox.color_right ++= function(v) { $colorright.fill = v; cascade = 
v; } 
+
         
-        // deliberately shadowing api on box, set internal values without 
triggering traps
-        var r, g, b;
-        var h, s, n;
+        ////////////////
+        // Color Model
         
-        ////////
-        // internal functions
-        const hexdid = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", 
"A", "B", "C", "D", "E", "F" ];
-        const toHexColor = function(red, green, blue) {
-            // REMARK - should be, but is, necessary
-            // why are we receiving negative values?
-            if (0>red) red = 0;
-            if (0>green) green = 0;
-            if (0>blue) blue = 0;
-
-            return "#" + hexdid[vexi.math.floor(red/16)]   + 
hexdid[vexi.math.floor(red%16)]
-                       + hexdid[vexi.math.floor(green/16)] + 
hexdid[vexi.math.floor(green%16)]
-                       + hexdid[vexi.math.floor(blue/16)]  + 
hexdid[vexi.math.floor(blue%16)];
+        const model = {
+            hasAlpha:true,
+            alpha:0, red:0, green:0, blue:0, // ARGB all 0-255
+            hue:0, saturation:0, value:0, // H 0-360, SI 0-255
         }
-
-
-
-        var threadsleeping = false;
-        var lock_value = false;
-        var lock_text = false;
         
-        thisbox.update = function() {
-            var hexval = toHexColor(r,g,b);
-            // (not an input so no lock)
-            $targetcolor.fill = hexval;
+        const getColorRGB = function() { return static.toHexColor(model); }
+        
+        const updateColor = function() {
+            var hexcode = getColorRGB();
+            $targetcolor.fill = hexcode;
+            model.hexcode = hexcode;
+            return hexcode;
+        }
+        
+        
+        
+        ////////////////
+        // Internal
+        
+        var lock_HSV = false;
+        var lock_RGB = false;
+        
+        const max = vexi.math.max;
+        const min = vexi.math.min;
+        const floor = vexi.math.floor;
+        const round = vexi.math.round;
+        const PI = vexi.math.PI;
+        
+        const updateHSV = function() {
+            lock_HSV = true;
             
-            $hexcode.text = hexval;
+            // https://en.wikipedia.org/wiki/HSL_and_HSV#General_approach
+            var R = model.red, G = model.green, B = model.blue;
             
-            // REMARK - variable names possibly innaccurate but the math works
-            var distance_from_center = s * $colorpicker.width / 2;
-            var angle_from_center = (h + 0.25) * vexi.math.PI * 2;
-            $circle.x = distance_from_center * 
vexi.math.cos(angle_from_center);
-            $circle.y = - (distance_from_center * 
vexi.math.sin(angle_from_center));
-            $gradient.marker.y = 0 - vexi.math.round(n * $gradient.height);
+            var priMax = max(max(R, G), B); // maximum primary color
+            var priMin = min(min(R, G), B); // minimum primary color
+            var chroma = priMax - priMin;
             
-            // make sure there's a thread running, watching to see if rgb 
remain
-            // the same for more than one event cycle. If so, update the 
gradient.
-            if (!threadsleeping) {
-                threadsleeping = true;
-                vexi.thread = function() {
-                    while(true) {
-                        var r_ = r; var g_ = g; var b_ = b;
-                        vexi.thread.yield();
-                        if (r == r_ and g == g_ and b == b_) break;
-                    }
-
-                    // Recalculate the color from hsn, but this time with a 
full intensity.
-                    var i_ = vexi.math.floor(h*6);
-                    var f_ = h*6 - i_;
-                    if (i_ == 0) i_ = 6;        // Case 0 is the same as case 
6.
-                    if (i_%2 == 0) f_ = 1 - f_; // If i_ is even
-
-                    var p_ = vexi.math.min(255, vexi.math.round((1 - s) * 
255));
-                    var q_ = vexi.math.min(255, vexi.math.round((1 - s * f_) * 
255));
-                    
-                    switch(i_) {
-                        case 1:  $grad_back.fill = toHexColor(q_, 255, p_); 
break;
-                        case 2:  $grad_back.fill = toHexColor(p_, 255, q_); 
break;
-                        case 3:  $grad_back.fill = toHexColor(p_, q_, 255); 
break;
-                        case 4:  $grad_back.fill = toHexColor(q_, p_, 255); 
break;
-                        case 5:  $grad_back.fill = toHexColor(255, p_, q_); 
break;
-                        case 6:  $grad_back.fill = toHexColor(255, q_, p_); 
break;
-                        default: $grad_back.fill = "#FFFFFF"; 
vexi.log.error("ERROR: update() i_="+i_+", which is outside range 0-6.");
-                    }
-                    threadsleeping = false;
+            // value is the same as the maximum part
+            model.value = priMax;
+            
+            if (chroma == 0) {
+                // monochrome/greyscale
+                model.hue = model.saturation = 0;
+            } else {
+                model.saturation = (chroma / model.value) * 255;
+                
+                // works out hue as a number between 0-6
+                var hue6 = (R == priMax)
+                                ? (((G - B) / chroma) + 6) % 6 :
+                           (G == priMax)
+                                ? ((B - R) / chroma) + 2
+                           //B == priMax
+                                : ((R - G) / chroma) + 4;
+                
+                // store hue as an angle
+                model.hue = 60 * hue6;
+            }
+            //trace("R:"+R+", G:"+G+", B:"+B+" -> H:"+model.hue+", 
S:"+model.saturation+", V:"+model.value); 
+            lock_HSV = false;
+        }
+        
+        
+        
+        const updateRGB = function() {
+            lock_RGB = true;
+            
+            if (model.saturation == 0) {
+                // no color; greyscale
+                model.red =
+                model.green =
+                model.blue = model.value;
+            } else {
+                // https://en.wikipedia.org/wiki/HSL_and_HSV#Converting_to_RGB
+                var H = model.hue, S = model.saturation, V = model.value;
+                
+                var chroma = V * (S/255);
+                var hue6 = H / 60;
+                var middle = chroma * (1 - vexi.math.abs((hue6%2)-1));
+                var priMin = V - chroma;
+                //trace("C:"+chroma+", h6:"+hue6+" -> X:"+middle+" m:"+priMin);
+                
+                const setRGB = function(r, g, b) {
+                    model.red = r + priMin;
+                    model.green = g + priMin;
+                    model.blue = b + priMin;
+                    //trace("H:"+H+", S:"+S+", V:"+V+" -> R:"+model.red+" 
G:"+model.green+" B:"+model.blue);
                 }
+                
+                switch (floor(hue6)) {
+                    case 6: // same as 0; circular
+                    case 0: setRGB(chroma, middle, 0); break;
+                    case 1: setRGB(middle, chroma, 0); break;
+                    case 2: setRGB(0, chroma, middle); break;
+                    case 3: setRGB(0, middle, chroma); break;
+                    case 4: setRGB(middle, 0, chroma); break;
+                    case 5: setRGB(chroma, 0, middle); break;
+                    default: throw "Something went wrong! H:"+H+", S:"+S+", 
V:"+V+" - H/60:"+hue6;
+                }
             }
             
+            $hexcode.text = updateColor();
+            lock_RGB = false;
+        }
+        
+        
+        
+        const updateCircle = function() {
+            const PI = vexi.math.PI;
+            const H = model.hue/360;
+            const S = model.saturation/255;
+            const distance_from_center = S * $colorpicker.width / 2;
+            const angle_from_center = (H + 0.25) * PI * 2;
             
-            $Red.value = r;
-            $Green.value = g;
-            $Blue.value = b;      
+            $circle.x = distance_from_center * 
vexi.math.cos(angle_from_center);
+            $circle.y = - (distance_from_center * 
vexi.math.sin(angle_from_center));
+        }
         
-            $Hue.value = h;
-            $Saturation.value = s;
-            $Intensity.value = n;       
+        $colorpicker.width ++= function(v) {
+            cascade = v;
+            updateCircle();
+        }
+        
+        
+        
+        const updateGradient = function() {
+            const H = model.hue, S = model.saturation, V = model.value;
             
-            $RedValue.text = r; 
-            $GreenValue.text = g; 
-            $BlueValue.text = b;
+            // set marker arrows
+            $gradient.marker.y = 0 - round((V/255) * $gradient.height);
 
-            // REMARK - v>=0.001 check necessary to prevent really small 
numbers (e.g. 4.513E4) appearing as >1
-            $HueValue.text = (""+(h>=0.001?h:0)).substring(0,5);
-            $SaturationValue.text = (""+s).substring(0,5);
-            $IntensityValue.text = (""+n).substring(0,5);     
-        }
+            // Recalculate the color from HSV, but this time with a full value
+            const hue6 = H/60;
+            const hueN = floor(hue6);
+            // Invert hueD if hueN is even
+            const hueD = hueN%2 ? hue6-hueN : 1-(hue6-hueN);
 
-        hsn_to_rgb = function() {
-            if(lock_value) return;
-            lock_value = true;
-            try{
-                   if (s == 0) {
-                       // this line doesn't work with traps set on the 
variables??
-                       // r = g = b = n * 255;
-                       var x = n * 255;
-                       b = x;
-                       r = x;
-                       g = x;
-                   } else {
-                       var i_ = vexi.math.floor(h*6);
-                       var f_ = h*6 - i_;
-       
-                       if (i_%2 == 0) f_ = 1 - f_; // If i_ is even
-       
-                       var v_ = vexi.math.round(n * 255);
-                       var p_ = vexi.math.round(n * (1 - s) * 255);
-                       var q_ = vexi.math.round(n * (1 - s * f_) * 255);
-                       if (i_ == 0) i_ = 6; // Case 0 is the same as case 6.
-       
-                       switch (i_) {
-                           case 1: r = q_; g = v_; b = p_; break;
-                           case 2: r = p_; g = v_; b = q_; break;
-                           case 3: r = p_; g = q_; b = v_; break;
-                           case 4: r = q_; g = p_; b = v_; break;
-                           case 5: r = v_; g = p_; b = q_; break;
-                           case 6: r = v_; g = q_; b = p_; break;
-                           default: vexi.log.error("ERROR: i_="+i_+", which is 
outside range 0-6.");
-                       }
-                   }
-                   update();
-            }finally{
-                lock_value = false;
+            // Secondary RGB component
+            const sec = min(255, round(255 - S));
+            // Tertiary RGB component
+            const ter = min(255, round(255 - S * hueD));
+            
+            //trace("Gradient for H:"+H+", S:"+S);
+            //trace("h6:"+hue6+" hN:"+hueN+" hD:"+hueD+" -> sec:"+sec+" 
ter:"+ter);
+            
+            const getGradientHex = function(r, g, b) {
+                return static.toHexColor({red:r, green:g, blue:b});
             }
-        }
-
-        rgb_to_hsn = function() {
-            if(lock_value) return;
-            lock_value = true;
-            try{
-                   // both v_ and x_ are in range 0-1.
-                   var max = vexi.math.max(vexi.math.max(r, g), b);
-                   var min = vexi.math.min(vexi.math.min(r, g), b);
-                   var diff = (max - min)/255;
-                   var v_ = max/255; 
-                   if (max == min) {
-                       h = s = 0; n = v_;
-                   } else {
-                       // FIXME: have taken an algorithm designed to spit out h
-                       // in range 0-6 and added /6 on the end; remodel 
algorithm.
-                       var f_ = ( (r == min) ? g - b : ((g == min) ? b - r : r 
- g) )/255; // f now in range 0-1.
-                       var i_ =   (r == min) ? 3     : ((g == min) ? 5 : 1);
-       
-                       h = (i_ - f_ / diff) / 6;
-                       s = (max - min) / max;
-                       n = v_;
-                   }
-                   update();
-            }finally{
-                lock_value = false;
+            
+            switch(hueN) {
+                case 1:  $grad_back.fill = getGradientHex(ter, 255, sec); 
break;
+                case 2:  $grad_back.fill = getGradientHex(sec, 255, ter); 
break;
+                case 3:  $grad_back.fill = getGradientHex(sec, ter, 255); 
break;
+                case 4:  $grad_back.fill = getGradientHex(ter, sec, 255); 
break;
+                case 5:  $grad_back.fill = getGradientHex(255, sec, ter); 
break;
+                case 0:  // hue[N]==0 and hue==360 (hueN==6) are the same thing
+                case 6:  $grad_back.fill = getGradientHex(255, ter, sec); 
break;
+                default: $grad_back.fill = "#FFFFFF";
+                         vexi.log.error("ERROR: updateGradient() 
hueN="+hueN+"; only 0-6 valid");
+                         vexi.log.error("ERROR: updateGradient() hue="+H+", 
saturation="+S);
             }
         }
-
         
-        // HSN value updates
-        const hsnTrap = function(v){
-            cascade = v;
-            if(lock_value) return;
-
-            h = $Hue.value;
-            s = $Saturation.value;
-            n = $Intensity.value;
-            hsn_to_rgb();                
+        
+        
+        ////////////////
+        // Model / GUI interactions
+        
+        const passive = { };
+        for (var k in model) passive[k] = true;
+        
+        const sliders = {
+            alpha:$Alpha, red:$Red, green:$Green, blue:$Blue,
+            hue:$Hue, saturation:$Satur, value:$Value
         };
+        const spinners = {
+            alpha:$AlphaValue, red:$RedValue, green:$GreenValue, 
blue:$BlueValue,
+            hue:$HueValue, saturation:$SaturValue, value:$ValueValue
+        }
         
-        const rgbTrap  = function(v){
+        const modelHSV_put = function(v) {
+            const vR = round(v);
+            cascade = vR;
+            
+            if (sliders[trapname].value != vR)
+                sliders[trapname].value = vR;
+            if (spinners[trapname].value != vR)
+                spinners[trapname].value = vR;
+            if (lock_HSV) return;
+            
+            updateRGB();
+            updateCircle();
+            updateGradient();
+        }
+        
+        model.hue ++= modelHSV_put;
+        model.saturation ++= modelHSV_put;
+        model.value ++= modelHSV_put;
+        
+        const modelRGB_put = function(v) {
+            const vR = round(v);
+            cascade = vR;
+            
+            if (sliders[trapname].value != vR)
+                sliders[trapname].value = vR;
+            if (spinners[trapname].value != vR)
+                spinners[trapname].value = vR;
+            if (lock_RGB) return;
+            
+            updateHSV();
+            updateCircle();
+            updateGradient();
+        }
+        
+        model.red ++= modelRGB_put;
+        model.green ++= modelRGB_put;
+        model.blue ++= modelRGB_put;
+        
+
+        // slider/spin value updates - only update model if it differs 
+        const widgetValuePut = function(v) {
+            if (0>v or v>trapee.maxvalue)
+                throw "Illegal "+trapee.part+" value: "+v;
+            
             cascade = v;
-            if(lock_value) return;
-
-            // WORKAROUND slider bug, reading less than 0 even when 
-            // never set as such ...
-            r = vexi.math.max(0,$Red.value);
-            g = vexi.math.max(0,$Green.value);
-            b = vexi.math.max(0,$Blue.value);
-            rgb_to_hsn();                
+            
+            const part = trapee.part;
+            if (v == model[part]) return;
+            model[part] = v;
         };
-       
-        $Hue.value        ++= hsnTrap;
-        $Saturation.value ++= hsnTrap;
-        $Intensity.value  ++= hsnTrap; 
         
-        // RGB value updates
-        $Red.value   ++= rgbTrap;
-        $Green.value ++= rgbTrap;
-        $Blue.value  ++= rgbTrap;
+        for (var k,slider in sliders)
+            slider.value ++= widgetValuePut;
         
+        for (var k,spinner in spinners)
+            spinner.value ++= widgetValuePut;
         
-        $hexcode.text ++= function(v){ 
-            if(lock_text) return;
-            lock_text = true;
-            try{
+        
+        
+        $hexcode.text ++= function(v) {
+            cascade = v;
             
-                   cascade = v;
-                   if(lock_value) return;
-                   if(v==null){
-                       v = "#000000";
-                   } 
-                   if(v.length and v.charAt(0)!='#'){
-                       v = "#"+v;
-                   }
-                   while(7>v.length){
-                       v = v + "0";
-                   }
-                   
-                   var rgb = vexi.string.parseInt(v.substring(1), 16);
-                   r = (rgb>>16)%256;
-                   g = (rgb>>8)%256;
-                   b = rgb%256;
-                   rgb_to_hsn();
-            }finally{
-                lock_text = false;
-            }                                
-        };
+            // Verify
+            if (v==null) {
+                v = useAlpha ? "#ff000000" : "#000000";
+            }
+            
+            if (typeof v != "string")
+                throw "Color value must be a string but '"+(typeof v)+"' was 
put";
+            if (v.length and v.charAt(0)!='#')
+                throw "Color must be a hex formatted string";
+            
+            switch (v.length) {
+                case 4: // #RGB
+                case 5: // #ARGB
+                case 7: // #RRGGBB
+                case 9: // #AARRGGBB
+                    break;
+                default:
+                    throw "String '"+v+"' is not a valid color";
+            }
+                
+            if (lock_RGB) return;
+            try {
+                lock_RGB = true;
+                const fromHex = function(s) { return vexi.string.parseInt(s, 
16); }
+                var offset = 0;
+                
+                switch (v.length) {
+                    case 4:
+                        model.alpha = 255;
+                        model.red   = fromHex(v.substring(1,2)) * 16;
+                        model.green = fromHex(v.substring(2,3)) * 16;
+                        model.blue  = fromHex(v.substring(3,4)) * 16;
+                        break;
+                    case 5:
+                        model.alpha = fromHex(v.substring(1,2)) * 16;
+                        model.red   = fromHex(v.substring(2,3)) * 16;
+                        model.green = fromHex(v.substring(3,4)) * 16;
+                        model.blue  = fromHex(v.substring(4,5)) * 16;
+                        break;
+                    case 7:
+                        model.alpha = 255;
+                        model.red   = fromHex(v.substring(1,3));
+                        model.green = fromHex(v.substring(3,5));
+                        model.blue  = fromHex(v.substring(5,7));
+                        break;
+                    case 9:
+                        model.alpha = fromHex(v.substring(1,3));
+                        model.red   = fromHex(v.substring(3,5));
+                        model.green = fromHex(v.substring(5,7));
+                        model.blue  = fromHex(v.substring(7,9));
+                        break;
+                }
+                
+                updateHSV();
+                updateGradient();
+                updateCircle();
+                updateColor();
+            } finally {
+                lock_RGB = false;
+            }
+        }
         
         
+               
+        ////////////////
+        // Gradient Bar
         
-               
-        ////////
-        // gradient bar
         var eventObj;
-        const gradient_move = function(v) {
+        const gradientPick = function(v) {
             var pos = $gradient.mouse.y;
-            if (pos > $gradient.height) {pos = $gradient.height;}
-            else if (0 > pos) {pos = 0;}
-            n = 1 - (pos/$gradient.height);
-            hsn_to_rgb();
+            if (pos > $gradient.height)
+                pos = $gradient.height;
+            else if (0 > pos) pos = 0;
+            model.value = 255 - (255 * (pos/$gradient.height));
+            updateRGB();
             return;
         }
         
-        
         $gradient.Press1 ++= function(v) {
-            gradient_move(v);
+            gradientPick();
             eventObj = surface.event;
-            eventObj.addMoveTrap(gradient_move);
+            eventObj.addMoveTrap(gradientPick);
             eventObj.Release1 ++= function(v) { 
                 eventObj._Release1 --= callee;
-                eventObj.delMoveTrap(gradient_move); 
+                eventObj.delMoveTrap(gradientPick); 
                 return;
             }
             return;
         }
-       
-        ////////
-        // colour circle
-        const color_move = function(v) {
-            var cx = $colorpicker.mouse.x - $colorpicker.width / 2;
-            var cy = -1 * ($colorpicker.mouse.y - $colorpicker.height / 2);
-            h = (vexi.math.atan(cy / cx) / 2 + vexi.math.PI / 4) / 
vexi.math.PI + (cx > 0 ? 0.5 : 0);
-            s = vexi.math.min(1.0, vexi.math.sqrt(cy * cy + cx * cx) / 
($colorpicker.width / 2));
-            hsn_to_rgb();
+        
+        
+        
+        ////////////////
+        // Colour Circle
+        
+        const colorPick = function(v) {
+            lock_HSV = true;
+            
+            const PI = vexi.math.PI;
+            const cx = $colorpicker.mouse.x - $colorpicker.width / 2;
+            const cy = -1 * ($colorpicker.mouse.y - $colorpicker.height / 2);
+            model.hue = 360 * (vexi.math.atan(cy / cx) / 2 + PI / 4) / PI + 
(cx > 0 ? 180 : 0);
+            
+            const SX = vexi.math.sqrt(cy * cy + cx * cx) / ($colorpicker.width 
/ 2);
+            model.saturation = min(255, SX*255);
+            
+            updateCircle();
+            updateGradient();
+            updateRGB();
+            lock_HSV = false;
             return;
         }
 
         $colorpicker.Press1 ++= function(v) {
-            color_move(v);
+            colorPick();
             eventObj = surface.event;
-            eventObj.addMoveTrap(color_move);
+            eventObj.addMoveTrap(colorPick);
             eventObj.Release1 ++= function(v) { 
                 eventObj._Release1 --= callee;
-                eventObj.delMoveTrap(color_move);
+                eventObj.delMoveTrap(colorPick);
                 return;
             }
             return;
-        }        
+        }
         
-        $Blue.value ++= function(v){
+        
+
+        ////////////////
+        // Widget API
+        
+        thisbox.compareLeft  ++= function(v) { $alphaleft.fill = 
$colorleft.fill = v; cascade = v; }
+        thisbox.compareRight ++= function(v) { $alpharight.fill = 
$colorright.fill = v; cascade = v; }
+        
+        thisbox.alphaEnabled ++= function(v) {
             cascade = v;
-            if(0>v) trace(new vexi.js.Exception(v));
-        };
+            $Alpha.enabled = $AlphaValue.enabled = v;
+            $AlphaLeft.display = $AlphaRight.display = v;
+        }
         
+        // external value
+        util.redirect..addRedirect(thisbox, $hexcode, "text", "text");
+        util.sync..sync(thisbox, thisbox, "value", "text");
         
-        // external value
-        sync..sync(thisbox, $hexcode, "value", "text");
         // initialize values
-        value = "#7F7F7F";
+        text = arguments[0] ?: "#FFFFFFFF";
         
     </ui:box>
+    
+    const hexdid = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", 
"B", "C", "D", "E", "F" ];
+    const toHex = function(num) { return hexdid[vexi.math.floor(num/16)] + 
hexdid[vexi.math.floor(num%16)]; }
+    
+    static.toHexColor = function(model, withAlpha) {
+        const R = model.red, G = model.green, B = model.blue;
+        
+        // REMARK - should be, but is, necessary
+        // why are we receiving negative values?
+        if (0>R) throw "red! "+R;
+        if (0>G) throw "green! "+G;
+        if (0>B) throw "blue! "+B;
+        
+        var hex = "#";
+        if (withAlpha and model.hasAlpha)
+            hex += toHex(model.alpha);
+        return hex + toHex(R) + toHex(G) + toHex(B);
+    }
+    
 </vexi>

This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.


------------------------------------------------------------------------------
What NetFlow Analyzer can do for you? Monitors network bandwidth and traffic
patterns at an interface-level. Reveals which users, apps, and protocols are 
consuming the most bandwidth. Provides multi-vendor support for NetFlow, 
J-Flow, sFlow and other flows. Make informed decisions using capacity planning
reports. http://sdm.link/zohomanageengine
_______________________________________________
Vexi-svn mailing list
Vexi-svn@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/vexi-svn

Reply via email to