I store my themes settings using Settings API.
If I create a new setting using add_settings_field() is it possible to create another setting that will be based on the first one?
Why? Imagine I have three settings:
main_color
lighter_color
darker_color
User opens Theme Options page and sets main_color to #444. Now I want lighter_color and darker_color to automagically change values based on main_color value (#444) so after all it looks like this:
main_color = #444
lighter_color = #777
darker_color = #111
Of course I could make 3 different settings and let user pick all the colors, but it won’t be that user-friendly and the whole theme will look messy if he sets lighter_color to something darker than main_color. etc.
Any ideas? 🙂 Maybe using a callback?
UPDATE
Starting bounty.
In short words – I have some settings stored in Settings API and I want to store their slightly modified versions somewhere else, I need full and easy access to these modified values, and they should update every time their Settings API “parents” do.
1 Answer
First of all you need function to calculate lighter and darker colors.
Once you have values in hex form a fast way is:
- convert hex to rgb
- conver rgb to hsl
- lighter and darker hsl (that is easy)
- revert lighter and darker colors from to hsl to hex (hsl > rgb > hex)
Settings API use update_option
to store data, and so a update_option_{$option}
is triggered after saving.
Assuming you are saving main color to an option called ‘main_color’ I created a class that implement this steps:
- hook
update_option_{$option}
andadd_option_{$option}
and run a function that: - get current saved option for main color
- create lighter and darker (steps above)
- save options also for these colors
Here the class:
class MyColorSettingsClass {
var $default_main_color;
var $lighther_perc;
var $darker_perc;
function __construct( $default_main_color="#444444", $lighther_perc = 33, $darker_perc = 33 ) {
$this->default_main_color = $default_main_color;
$this->lighther_perc = $lighther_perc;
$this->darker_perc = $darker_perc;
add_action('add_option_main_color', array($this, 'set_conversion'), 20, 2 );
add_action('update_option_main_color', array($this, 'set_conversion'), 20, 2 );
// if not exists the main color option, set colors to default
if ( ! get_option('main_color') ) update_option('main_color', $default_main_color );
}
function set_conversion($option_name_or_old_value="", $newvalue = null ) {
$main = $newvalue ? : $this->default_main_color;
$this->lighther_and_darker( $main );
}
function lighther_and_darker( $color = null ) {
$main_hls = self::rgbToHsl( self::hexToRgb($color) );
if ( is_array($main_hls) && count($main_hls) == 3 ) {
$main_hls = array_values($main_hls);
$l = floor( $main_hls[2] * 100 );
$lighther_l = $l + round( $l * ( $this->lighther_perc / 100 ) );
if ( $lighther_l > 100 ) $lighther_l = 100;
$darker_l = $l - round( $l * ( $this->darker_perc / 100) ) ? : 0;
$lighther_hls = array('h' => $main_hls[0], 's' => $main_hls[1], 'l' => round($lighther_l/100, 2) );
$darker_hls = array('h' => $main_hls[0], 's' => $main_hls[1], 'l' => round($darker_l/100, 2) );
$lighther = self::rgbToHex( self::hslToRgb( $lighther_hls ) );
$darker = self::rgbToHex( self::hslToRgb( $darker_hls ) );
update_option('lighter_color', $lighther);
update_option('darker_color', $darker);
}
}
static function hexToRgb( $hex = '000000') {
if ( empty($hex) || ! is_string($hex) ) return;
// strip '#' if present
$hex = str_replace('#', '', $hex);
// accept only hexadecimal strings in 3 and 6 digit format
if ( ( strlen($hex) != 3 && strlen($hex) != 6 ) || ! ctype_xdigit ( $hex ) ) return;
if ( strlen($hex) == 3) {
$r = $hex{0} . $hex{0};
$g = $hex{1} . $hex{1};
$b = $hex{2} . $hex{2};
} else {
$r = substr($hex, 0, 2);
$g = substr($hex, 2, 2);
$b = substr($hex, 4, 2);
}
$rgb = array_map('hexdec', compact('r', 'g', 'b'));
return $rgb;
}
static function rgbToHex( $rgb = array(0, 0, 0) ) {
if ( ! is_array($rgb) || count($rgb) != 3 ) return;
$rgb = array_map('intval', $rgb);
$hex = implode('', array_map('dechex', $rgb) );
if ( ! ctype_xdigit ( $hex ) ) $hex = $this->default_main_color;
return '#' . $hex;
}
// coming from http://www.brandonheyer.com/2013/03/27/convert-hsl-to-rgb-and-rgb-to-hsl-via-php/
static function rgbToHsl( $rgb = array(0, 0, 0) ) {
if ( ! is_array($rgb) || count($rgb) != 3 ) return;
$rgb = array_values( array_map('intval', $rgb) );
$oldR = $r = $rgb[0];
$oldG = $g = $rgb[1];
$oldB = $b = $rgb[2];
$r /= 255;
$g /= 255;
$b /= 255;
$max = max( $r, $g, $b );
$min = min( $r, $g, $b );
$h = $s = 0;
$l = ( $max + $min ) / 2;
$d = $max - $min;
if( $d != 0 ) {
$s = $d / ( 1 - abs( 2 * $l - 1 ) );
switch( $max ){
case $r :
$h = 60 * fmod( ( ( $g - $b ) / $d ), 6 );
break;
case $g:
$h = 60 * ( ( $b - $r ) / $d + 2 );
break;
case $b:
$h = 60 * ( ( $r - $g ) / $d + 4 );
break;
}
}
$hsl = array();
foreach ( compact('h', 's', 'l') as $i => $var ) $hsl[$i] = round($var, 2);
return $hsl;
}
// coming from http://www.brandonheyer.com/2013/03/27/convert-hsl-to-rgb-and-rgb-to-hsl-via-php/
static function hslToRgb( $hsl = array(0, 0, 0) ) {
if ( ! is_array($hsl) || count($hsl) != 3 ) return;
$hsl = array_values($hsl);
$h = (float)$hsl[0];
$s = (float)$hsl[1];
$l = (float)$hsl[2];
$r = null;
$g = null;
$b = null;
$c = ( 1 - abs( 2 * $l - 1 ) ) * $s;
$x = $c * ( 1 - abs( fmod( ( $h / 60 ), 2 ) - 1 ) );
$m = $l - ( $c / 2 );
if ( $h < 60 ) {
$r = $c;
$g = $x;
$b = 0;
} else if ( $h < 120 ) {
$r = $x;
$g = $c;
$b = 0;
} else if ( $h < 180 ) {
$r = 0;
$g = $c;
$b = $x;
} else if ( $h < 240 ) {
$r = 0;
$g = $x;
$b = $c;
} else if ( $h < 300 ) {
$r = $x;
$g = 0;
$b = $c;
} else {
$r = $c;
$g = 0;
$b = $x;
}
$r = ( $r + $m ) * 255;
$g = ( $g + $m ) * 255;
$b = ( $b + $m ) * 255;
$rgb = array();
foreach ( compact('r', 'g', 'b') as $i => $var ) $rgb[$i] = floor($var);
return $rgb;
}
}
Once you have this class included in theme or plugin just use it like so:
new MyColorSettingsClass('#444444', 30, 30);
Where 1st argument is default main color 2nd is the lighter percentual and 3rd is darker percentual.
First argument accept hex colors in both 3 and 6 digits form and with or without ‘#’.
Bonus functionality is that this code create the default option for all 3 colors based of 1st argument when the user has not setted nothing yet.
Convert function from rgb to hsl and reverse are taken from here and just a little bit adapted.
Edit
On stackoverflow I found an function that convert lighter and darker hex colors direcly. I think is a bit slower than my solution (maybe), but is much easier to implement.
Code is here https://stackoverflow.com/questions/3512311/how-to-generate-lighter-darker-color-with-php#11951022 If you want, edit my class to generate lighter and darker colors using that function.
Only note that stackoverflow function accept ligh increase/decrease in a form from -255 to 255 (negative = darker / positive = lighter) my class accepts percentual values that maybe are more intuitive.