Convert Color of Color Well to Hex String

First, how do you get the current value of a color well? If I could figure that out, then I’d try to get a hex string myself.

This should end up being a preference for an HTML related app. If I could tell when the color well has changed, then turn the new color into a hex string (placed inside a text field), that would be great.

Edit: Something like this: exColor

Edit: I found an old post in Google’s cache, as well as a sub-routine from Apple.

Edit: I found the original post mentioned above. I searched for all sorts of phrases here and got nothing. I searched for users named “Kadis” and his only thread came up almost instantly.

Edit: I can get the color now (the heat is really getting to me today). I still want to know how to tell when the color well/panel has changed. (Kadis didn’t figure it out either.)

Edit: Thanks to everyone that contributed to this. :cool:

Say you had a color well named “color_well” in your project, just attach an “on clicked” handler to it and then catch the click:

on clicked the_object
    set object_name to the_object's name as Unicode text
    if object_name = "color_well" then
        set the_color to the_object's color 
        log the_color
    end if
end clicked

Here’s a bit of Obj-C that you can add to your app to easily get the HEX value for the color (save this as a file called “methods.m” and add it to your project):

[code]#import <Cocoa/Cocoa.h>

@interface methods : NSObject

  • (NSString *)getHEXValue:(NSColorWell *)colorWell;
    @end

@implementation methods

  • (NSString *)getHEXValue:(NSColorWell *)colorWell {
    NSColor *color = [colorWell color];
    float red = 0.0, green = 0.0, blue = 0.0;
    [color getRed:&red green:&green blue:&blue alpha:NULL];
    return [NSString stringWithFormat:@“#%02X%02X%02X”, (int)(red * 255), (int)(green * 255), (int)(blue * 255)];
    }

@end[/code]
Then call it like so:

on clicked the_object
    set object_name to the_object's name as Unicode text
    if object_name = "color_well" then
        set the_color to the_object's color
        log the_color
        set hex_color to (call method "getHEXValue:" of class "methods" with parameter the_object)
        log hex_color
    end if
end clicked

Jon

Thanks Jon! :slight_smile: I didn’t think it would be that simple.

Is it possible to make that Obj-C method handle the CMYK and Gray Scale sliders (which don’t return a RGB colorspace)?

Also [anyone], is it possible to enter a hex value into a text field, and have the color well next to it change to reflect the new value?

You can convert the color to the RGB color space by adding one line to the method I posted and you can go the opposite direction (get the color from a HEX value) by using the following code:

[code]#import <Cocoa/Cocoa.h>

@interface methods : NSObject

  • (NSString *)getHEXValue:(NSColorWell *)colorWell;
  • (NSColor *)setHEXValue:(NSColorWell *)colorWell toColor:(NSString *)HEXValue;
    @end

@implementation methods

  • (NSString *)getHEXValue:(NSColorWell *)colorWell {
    NSColor *color = [colorWell color];
    color = [color colorUsingColorSpaceName:@“NSCalibratedRGBColorSpace”];
    float red = 0.0, green = 0.0, blue = 0.0;
    [color getRed:&red green:&green blue:&blue alpha:NULL];
    return [NSString stringWithFormat:@“#%02X%02X%02X”, (int)(red * 255), (int)(green * 255), (int)(blue * 255)];
    }

  • (NSColor *)setHEXValue:(NSColorWell *)colorWell toColor:(NSString *)HEXValue {
    NSCharacterSet *hex = [NSCharacterSet characterSetWithCharactersInString:@“1234567890abcdefABCDEF”];
    NSScanner *scanner = [NSScanner scannerWithString:([HEXValue hasPrefix:@“#”] ? [HEXValue substringFromIndex:1] : HEXValue)];
    NSString *code = nil;
    [scanner scanCharactersFromSet:hex intoString:&code];
    if ([code length] == 6) {
    unsigned int color = 0;
    scanner = [NSScanner scannerWithString:code];
    if (![scanner scanHexInt:&color]) {
    return nil;
    }
    NSColor *newColor = [NSColor colorWithCalibratedRed:(((color >> 16) & 0xff) / 255.0) green:(((color >> 8) & 0xff) / 255.0) blue:((color & 0xff) / 255.0) alpha:1.0];
    [colorWell setColor];
    return newColor;
    }
    return nil;
    }

@end[/code]
And then, say you had an editable text field named “HEX_value” with an “on end editing” handler attached, set up your AS like so:

on clicked the_object
    set object_name to the_object's name as Unicode text
    if object_name = "color_well" then
        set the_color to the_object's color
        set hex_color to (call method "getHEXValue:" of class "methods" with parameter the_object)
        set string value of text field "HEX_value" of super view of the_object to hex_color
    end if
end clicked

on end editing the_object
    set object_name to the_object's name as Unicode text
    if object_name = "HEX_value" then
        call method "setHEXValue:toColor:" of class "methods" with parameters {color well "color_well" of super view of the_object, string value of the_object}
    end if
end end editing

Jon

Here’s a link to a project demonstrating this code:

http://homepage.mac.com/jonn8/as/html/misc_projects.html#Color_Picker

Jon

I’d like the text field to handle short hex strings (e.g., #0F0, short for #00FF00). Is there a better way to do this?

on end editing theObject
	if (content of theObject) starts with "#" and length of (get content of theObject) is 4 then
		set oldValue to characters 2 through 4 of (get content of theObject)
		set newValue to {"#"}
		
		repeat with i from 1 to 3
			set newValue's end to (item i of oldValue) & (item i of oldValue)
		end repeat
		
		set content of theObject to (newValue as text)
	end if
	
	call method "setHEXValue:toColor:" of class "methods" with parameters {color well (theObject's name) of (theObject's super view), string value of theObject}
end end editing

There are probably better ways of validating the input but try this:

on end editing the_object
	set object_name to the_object's name as Unicode text
	if object_name = "HEX_value" then
		set HEX_value to string value of the_object
		if (HEX_value's character 1 ≠ "#") then set HEX_value to "#" & HEX_value as Unicode text
		set HEX_value_count to (count HEX_value)
		if (HEX_value_count = 4) then
			set new_value to {}
			repeat with i from 1 to HEX_value_count
				set this_char to HEX_value's character i
				set end of new_value to this_char
				if this_char ≠ "#" then set end of new_value to this_char
			end repeat
			set HEX_value to (new_value as Unicode text)
		else if (HEX_value_count ≠ 7) then
			beep
			return (display dialog (""" & HEX_value's text 2 thru -1 & "" is not a valid HEX color value.") buttons {"OK"} default button 1 with icon 2 giving up after 5)
		end if
		set string value of the_object to HEX_value
		call method "setHEXValue:toColor:" of class "methods" with parameters {color well "color_well" of super view of the_object, HEX_value}
	end if
end end editing

Jon

Hi,

I’m not sure if the two numbers were #0f0 and #00ff00, but if you’re adding leading zeros, then here’s another way:

display dialog “Enter hex:” default answer “#”
set t to text returned of result
if t begins with “#” then
set t to rest of (text items of t) as string
end if
set t to text -6 thru -1 of (“000000” & t)

If I’m way off base, then disregard.

gl,

I’m not certain if it’s a good idea, but what if I wanted to accept common color names? That is, if a common color name is entered, convert it to a hex value, then change the color well.

kel, the HEX values don’t need leading zeros. As to using color names, try this:

property color_names : {"Black", "Green", "Silver", "Lime", "Gray", "Olive", "White", "Yellow", "Maroon", "Navy", "Red", "Blue", "Purple", "Teal", "Fuchsia", "Aqua"}
property color_values : {"#000000", "#008000", "#C0C0C0", "#00FF00", "#808080", "#808000", "#FFFFFF", "#FFFF00", "#800000", "#000080", "#FF0000", "#0000FF", "#800080", "#008080", "#FF00FF", "#00FFFF"}

on end editing the_object
	set object_name to the_object's name as Unicode text
	if object_name = "HEX_value" then
		set HEX_value to string value of the_object
		if HEX_value is in color_names then
			repeat with i from 1 to (count color_names)
				if item i of color_names = HEX_value then
					set HEX_value to item i of color_values
					exit repeat
				end if
			end repeat
		end if
		if (HEX_value's character 1 ≠ "#") then set HEX_value to "#" & HEX_value as Unicode text
		set HEX_value_count to (count HEX_value)
		if (HEX_value_count = 4) then
			set new_value to {}
			repeat with i from 1 to HEX_value_count
				set this_char to HEX_value's character i
				set end of new_value to this_char
				if this_char ≠ "#" then set end of new_value to this_char
			end repeat
			set HEX_value to (new_value as Unicode text)
		else if (HEX_value_count ≠ 7) then
			beep
			return (display dialog (""" & HEX_value's text 2 thru -1 & "" is not a valid HEX color value.") buttons {"OK"} default button 1 with icon 2 giving up after 5)
		end if
		set string value of the_object to HEX_value
		call method "setHEXValue:toColor:" of class "methods" with parameters {color well "color_well" of super view of the_object, HEX_value}
	end if
end end editing

Jon

A popup menu might be good for common color names.

Hi Jon and Guardian,

I don’t understand this. It seems like Guardian is trying to add leading zeros. From the post Guardian wants to change ofo to ooffoo so I thought it was a typo and Guardian actually wanted to change ffoo to ooffoo adding leading zeroes and forgot the other f. I’m still perpexed.

gl,

Kel,

HTML colors are defined using a hexadecimal notation (HEX) for the combination of red, green, and blue color values (RGB). The values go from 0 to 255 or, in HEX notation, #00 to #FF. If the HEX value of a color contains three component values whose HEX notation contains “even” matched values (like black, for instance, #000000 = RGB(0, 0, 0); white #FFFFFF = RGB(255, 255, 255); or blue #0000FF = RGB(0, 0, 255) but not grey #C0C0C0 = RGB(192, 192, 192)), that is, where each value in the triplet contains the same character twice, it is common to use the 3-character simplified HEX value. So, white would be shorted from #FFFFFF to #FFF and blue to #00F. When converted to an actual HEX color value, you would need to expand each value to the “double value”. The grey example couldn’t be shortened because you’d get either #CCC or #000 which would be expanded to #CCCCCC (RGB(204, 204, 204) = grey, though a different shade) or #000000 (RGB(0, 0, 0) = black) respectively. It isn’t a matter of adding leading zeros but of duplicating each character in the HEX value. I hope I’ve made it clear, but I doubt it. Try a Google search on “hex color rgb”.

Jon

Hi Jon,

I see the light! Why a person would want to double each hex digit was really bugging me.

Thanks a lot,

What would I need to do if I wanted to leave the text field as is (leaving a short hex string [#00F] or common color name) and still have the color well change?

You could always pass the ‘call method’ the expanded hex string while leaving the text field untouched. And in the inverse ‘call method’ that sets the text field, you could then collapse the hex string back to #xxx, if it needs it.

on expandHex(hexString)
	if (count of hexString) is 4 and character 1 of hexString is "#" then
		set newHex to {"#"}
		repeat with i from 2 to 4
			copy {character i of hexString, character i of hexString} to end of newHex
		end repeat
		set hexString to newHex as text
	end if
	return hexString
end expandHex

on collapseHex(hexString)
	if (count of hexString) is 7 and character 1 of hexString is "#" then
		set newHex to {"#"}
		set {a, b, c, d, e, f} to (characters 2 thru 7 of hexString)
		if a = b and c = d and e = f then
			copy {a, c, e} to end of newHex
			set hexString to newHex as text
		end if
	end if
	return hexString
end collapseHex

Oh, and thanks, jonn8. Great stuff.

For the color names you would have to do something similar but also check for missing value in case the color name or hex string passed to the below handlers aren’t in the properties. The below handlers also assume two properties listed previously: color_names, and color_values.

colorNameForHex("#008080") --> returns "Teal"
hexForColorName("Teal") --> returns "#008080"

on colorNameForHex(hexString)
	try
		return item indexOfMatch(color_values, hexString, 1) of color_names
	on error
		return missing value
	end try
end colorNameForHex

on hexForColorName(colorName)
	try
		return item indexOfMatch(color_names, colorName, 1) of color_values
	on error
		return missing value
	end try
end hexForColorName

on indexOfMatch(checkList, matchItem, instance)
	if instance = 0 then -- return every instance
		set match to {}
		repeat with i from 1 to count of checkList
			if item i of checkList = matchItem then copy i to end of match
		end repeat
	else if instance > 0 then -- pos instance
		set match to 0
		set found to 0
		repeat with i from 1 to count of checkList
			if item i of checkList = matchItem then
				set found to found + 1
				if found = instance then
					set match to i
				end if
			end if
		end repeat
	else if instance < 0 then -- neg instance
		set match to {}
		repeat with i from 1 to count of checkList
			if item i of checkList = matchItem then copy i to end of match
		end repeat
		try
			set match to item instance of match
		on error
			set match to 0
		end try
	end if
	return match
end indexOfMatch