2

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.

7
  • So, lighter_color and darker_color are not options on the Theme options page? How are they displayed? How are they computed? Do you want the change to occur with refreshing (using PHP) or without refreshing (using JavaScript) the browser page? Edit your question with the new info. Commented Aug 22, 2013 at 16:03
  • Yes, I don't want them on the options page, I wasn't sure how to store them so decided Settings API would do the best. I'd love them to update every time user hits save on Options Page. Commented Aug 22, 2013 at 16:53
  • 1
    Why save them at all? If you've got a function to calculate the lighter/darker versions, do it when you output them.
    – Milo
    Commented Aug 25, 2013 at 14:20
  • I'm afraid it might hit performance if I'd perform multiple PHP funcions such as stackoverflow.com/questions/3512311/… and a few more. I'd rather store them somewhere than calculate everything on page load. I've even tried to calculate them in fake css (.php with modified header), but it slowed my blog a lot. Any hints? I'd love to not store them if I don't need to but it's worth noticing we're talking about 30-50 elements per page. Commented Aug 25, 2013 at 14:38
  • you might see a performance hit if you calculate it millions of times, but twice per page load will absolutely not be perceivable.
    – Milo
    Commented Aug 25, 2013 at 14:41

1 Answer 1

1
+50

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.