Settings API – generating field value based on a different field?

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
1

First of all you need function to calculate lighter and darker colors.

Once you have values in hex form a fast way is:

  1. convert hex to rgb
  2. conver rgb to hsl
  3. lighter and darker hsl (that is easy)
  4. 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:

  1. hook update_option_{$option} and add_option_{$option} and run a function that:
  2. get current saved option for main color
  3. create lighter and darker (steps above)
  4. 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.

Leave a Comment