File indexing completed on 2024-04-28 05:32:18

0001 /*
0002     this file is part of the oxygen gtk engine
0003     SPDX-FileCopyrightText: 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "oxygenstylehelper.h"
0009 #include "oxygencairocontext.h"
0010 #include "oxygencairoutils.h"
0011 #include "oxygencolorutils.h"
0012 #include "oxygenrgba.h"
0013 
0014 #include <cmath>
0015 #include <gdk/gdk.h>
0016 
0017 namespace Oxygen
0018 {
0019 
0020     //__________________________________________________________________
0021     const double StyleHelper::_slabThickness = 0.45;
0022     const double StyleHelper::_shadowGain = 1.5;
0023     const double StyleHelper::_glowBias = 0.6;
0024 
0025     //__________________________________________________________________
0026     StyleHelper::StyleHelper( void )
0027     {
0028         #if OXYGEN_DEBUG
0029         std::cerr << "Oxygen::StyleHelper::StyleHelper" << std::endl;
0030         #endif
0031     }
0032 
0033     //__________________________________________________________________
0034     StyleHelper::~StyleHelper( void )
0035     {
0036         #if OXYGEN_DEBUG
0037         std::cerr << "Oxygen::StyleHelper::~StyleHelper" << std::endl;
0038         #endif
0039     }
0040 
0041     //__________________________________________________________________
0042     void StyleHelper::initializeRefSurface( void )
0043     {
0044 
0045         // make sure that surface is not already initialized
0046         if( _refSurface.isValid() ) return;
0047 
0048         /*
0049         create dummy widget, check its associated screen;
0050         if found create surface for it and save as reference surface
0051         */
0052         GdkScreen* screen( gdk_screen_get_default() );
0053         GdkWindow* window( screen ? gdk_screen_get_root_window( screen ):0x0 );
0054         if( screen && window )
0055         {
0056 
0057             #if OXYGEN_DEBUG
0058             std::cerr
0059                 << "Oxygen::StyleHelper::initializeRefSurface - "
0060                 << " screen: " << screen
0061                 << " window: " << window
0062                 << std::endl;
0063             #endif
0064 
0065             Cairo::Context context( window );
0066             _refSurface = Cairo::Surface( cairo_surface_create_similar( cairo_get_target( context ), CAIRO_CONTENT_ALPHA, 1, 1 ) );
0067 
0068         } else {
0069 
0070             /*
0071             no screen found.
0072             Destroy widget (since gtk_widget_realize would otherwise crash)
0073             Fallback to ImageSurface
0074             */
0075             #if OXYGEN_DEBUG
0076             std::cerr
0077                 << "Oxygen::StyleHelper::initializeRefSurface - "
0078                 << " No valid screen found to create X11 surface."
0079                 << " Falling back to Cairo Image surface."
0080                 << std::endl;
0081             #endif
0082 
0083             _refSurface.set( cairo_image_surface_create( CAIRO_FORMAT_ARGB32, 1, 1 ) );
0084 
0085         }
0086 
0087     }
0088 
0089     //__________________________________________________________________
0090     void StyleHelper::drawSeparator( Cairo::Context& context, const ColorUtils::Rgba& base, int x, int y, int w, int h, bool vertical )
0091     {
0092 
0093         // get surface
0094         const Cairo::Surface& surface( separator( base, vertical, vertical ? h:w ) );
0095         if(!surface) return;
0096 
0097         // translate
0098         cairo_save( context );
0099         if( vertical ) {
0100 
0101             cairo_translate( context, x+w/2-1, y );
0102             cairo_rectangle( context, 0, 0, 3, h );
0103 
0104         } else {
0105 
0106             cairo_translate( context, x, y+h/2 );
0107             cairo_rectangle( context, 0, 0, w, 2 );
0108 
0109         }
0110 
0111         cairo_set_source_surface( context, surface, 0, 0 );
0112         cairo_fill( context );
0113         cairo_restore( context );
0114 
0115     }
0116 
0117     //__________________________________________________________________
0118     const Cairo::Surface& StyleHelper::separator( const ColorUtils::Rgba& base, bool vertical, int size )
0119     {
0120 
0121         const SeparatorKey key( base, vertical, size );
0122 
0123         // try find in cache and return
0124         if( const Cairo::Surface& surface = _separatorCache.value(key) )
0125         { return surface; }
0126 
0127         // for invalid sizes return a null surface
0128         if( size <= 0 )
0129         { return _separatorCache.insert( key, 0L ); }
0130 
0131         // cached not found, create new
0132         Cairo::Surface surface( vertical ? createSurface( 3, size ):createSurface( size, 2 ) );
0133 
0134         int xStart( 0 );
0135         int yStart( 0 );
0136         int xStop( vertical ? 0 : size );
0137         int yStop( vertical ? size : 0 );
0138         int xOffset( vertical ? 1:0 );
0139         int yOffset( vertical ? 0:1 );
0140 
0141         Cairo::Context context( surface );
0142         cairo_set_line_width( context, 1.0 );
0143 
0144         if( vertical ) cairo_translate( context, 0.5, 0 );
0145         else cairo_translate( context, 0, 0.5 );
0146 
0147         {
0148             ColorUtils::Rgba light( ColorUtils::lightColor( base ) );
0149             Cairo::Pattern pattern( cairo_pattern_create_linear( xStart, yStart, xStop, yStop ) );
0150             if( vertical ) light.setAlpha( 0.7 );
0151 
0152             cairo_pattern_add_color_stop( pattern, 0.3, light );
0153             cairo_pattern_add_color_stop( pattern, 0.7, light );
0154             light.setAlpha( 0 );
0155             cairo_pattern_add_color_stop( pattern, 0, light );
0156             cairo_pattern_add_color_stop( pattern, 1, light );
0157             cairo_set_source( context, pattern );
0158 
0159             if( vertical )
0160             {
0161                 cairo_move_to( context, xStart, yStart );
0162                 cairo_line_to( context, xStop, yStop );
0163                 cairo_move_to( context, xStart+2*xOffset, yStart+2*yOffset );
0164                 cairo_line_to( context, xStop+2*xOffset, yStop+2*yOffset );
0165 
0166             } else {
0167 
0168                 cairo_move_to( context, xStart+xOffset, yStart+yOffset );
0169                 cairo_line_to( context, xStop+xOffset, yStop+yOffset );
0170 
0171             }
0172 
0173             cairo_stroke( context );
0174         }
0175 
0176         {
0177             ColorUtils::Rgba dark( ColorUtils::darkColor( base ) );
0178 
0179             Cairo::Pattern pattern( cairo_pattern_create_linear( xStart, yStart, xStop, yStop ) );
0180             cairo_pattern_add_color_stop( pattern, 0.3, dark );
0181             cairo_pattern_add_color_stop( pattern, 0.7, dark );
0182             dark.setAlpha(0);
0183             cairo_pattern_add_color_stop( pattern, 0, dark );
0184             cairo_pattern_add_color_stop( pattern, 1, dark );
0185             cairo_set_source( context, pattern );
0186 
0187             if( vertical )
0188             {
0189 
0190                 cairo_move_to( context, xStart+xOffset, yStart+yOffset );
0191                 cairo_line_to( context, xStop+xOffset, yStop+yOffset );
0192 
0193             } else {
0194 
0195                 cairo_move_to( context, xStart, yStart );
0196                 cairo_line_to( context, xStop, yStop );
0197             }
0198 
0199             cairo_stroke( context );
0200         }
0201 
0202         // note: we can't return the surface directly, because it is a temporary
0203         // we have to return the inserted object instead
0204         return _separatorCache.insert( key, surface );
0205 
0206     }
0207 
0208     //______________________________________________________________________________
0209     const Cairo::Surface& StyleHelper::windecoButton(const ColorUtils::Rgba &base, bool pressed, int size)
0210     {
0211 
0212         const WindecoButtonKey key( base, size, pressed );
0213 
0214         // try find in cache and return
0215         if( const Cairo::Surface& surface = _windecoButtonCache.value(key) )
0216         { return surface; }
0217 
0218         // cached not found, create new
0219         Cairo::Surface surface( createSurface( size, size ) );
0220 
0221         // calculate colors
0222         ColorUtils::Rgba light = ColorUtils::lightColor(base);
0223         ColorUtils::Rgba dark = ColorUtils::darkColor(base);
0224 
0225         // create cairo context
0226         Cairo::Context context( surface );
0227         const double u = size/18.0;
0228         cairo_translate( context, 0.5*u, (0.5-0.668)*u );
0229 
0230         {
0231 
0232             // plain background
0233             Cairo::Pattern pattern( cairo_pattern_create_linear( 0, u*1.665, 0, u*(12.33+1.665) ) );
0234             if( pressed )
0235             {
0236                 cairo_pattern_add_color_stop( pattern, 1, light );
0237                 cairo_pattern_add_color_stop( pattern, 0, dark );
0238             } else {
0239                 cairo_pattern_add_color_stop( pattern, 0, light );
0240                 cairo_pattern_add_color_stop( pattern, 1, dark );
0241             }
0242 
0243             cairo_ellipse( context, u*0.5*(17-12.33), u*1.665, u*12.33, u*12.33 );
0244             cairo_set_source( context, pattern );
0245             cairo_fill( context );
0246 
0247         }
0248 
0249         {
0250             // outline circle
0251             const double penWidth( 0.7 );
0252             Cairo::Pattern pattern( cairo_pattern_create_linear( 0, u*1.665, 0, u*(2.0*12.33+1.665) ) );
0253             cairo_pattern_add_color_stop( pattern, 0, light );
0254             cairo_pattern_add_color_stop( pattern, 1, dark );
0255 
0256             cairo_ellipse( context, u*0.5*(17-12.33+penWidth), u*(1.665+penWidth), u*(12.33-penWidth), u*(12.33-penWidth) );
0257             cairo_set_source( context, pattern );
0258             cairo_set_line_width( context, penWidth );
0259             cairo_stroke( context );
0260         }
0261 
0262         // note: we can't return the surface directly, because it is a temporary
0263         // we have to return the inserted object instead
0264         return _windecoButtonCache.insert( key, surface );
0265 
0266     }
0267 
0268     //_______________________________________________________________________
0269     const Cairo::Surface& StyleHelper::windecoButtonGlow(const ColorUtils::Rgba &base, int size)
0270     {
0271 
0272         const WindecoButtonGlowKey key( base, size );
0273 
0274         // try find in cache and return
0275         if( const Cairo::Surface& surface = _windecoButtonGlowCache.value(key) )
0276         { return surface; }
0277 
0278         // cached not found, create new
0279         Cairo::Surface surface( createSurface( size, size ) );
0280 
0281         // right now the same color is used for the two shadows
0282         const ColorUtils::Rgba& light( base );
0283         const ColorUtils::Rgba& dark( base );
0284 
0285         Cairo::Context context( surface );
0286         const double u = size/18.0;
0287         cairo_translate( context, 0.5*u, (0.5-0.668)*u );
0288 
0289         {
0290 
0291             // outer shadow
0292             Cairo::Pattern pattern( cairo_pattern_create_radial( u*8.5, u*8.5, u*8.5 ) );
0293 
0294             static const int nPoints( 5 );
0295             double x[5] = { 0.61, 0.72, 0.81, 0.9, 1};
0296             double values[5] = { 255-172, 255-178, 255-210, 255-250, 0 };
0297             ColorUtils::Rgba c = dark;
0298             for( int i = 0; i<nPoints; i++ )
0299             { c.setAlpha( values[i]/255 ); cairo_pattern_add_color_stop( pattern, x[i], c ); }
0300 
0301             cairo_set_source( context, pattern );
0302             cairo_rectangle( context, 0, 0, size, size );
0303             cairo_fill( context );
0304         }
0305 
0306         {
0307             // inner shadow
0308             Cairo::Pattern pattern( cairo_pattern_create_radial( u*8.5, u*8.5, u*8.5 ) );
0309 
0310             static const int nPoints(6);
0311             const double x[6] = { 0.61, 0.67, 0.7, 0.74, 0.78, 1 };
0312             const double values[6] = { 255-92, 255-100, 255-135, 255-205, 255-250, 0 };
0313             ColorUtils::Rgba c( light );
0314             for( int i = 0; i<nPoints; i++ )
0315             { c.setAlpha( values[i]/255 ); cairo_pattern_add_color_stop( pattern, x[i], c ); }
0316 
0317             cairo_set_source( context, pattern );
0318             cairo_rectangle( context, 0, 0, size, size );
0319             cairo_fill( context );
0320         }
0321 
0322         // note: we can't return the surface directly, because it is a temporary
0323         // we have to return the inserted object instead
0324         return _windecoButtonGlowCache.insert( key, surface );
0325 
0326     }
0327 
0328     //_________________________________________________
0329     const Cairo::Surface& StyleHelper::verticalGradient( const ColorUtils::Rgba& base, int height )
0330     {
0331 
0332         const VerticalGradientKey key( base, height );
0333 
0334         // try find in cache and return
0335         if( const Cairo::Surface& surface = _verticalGradientCache.value(key) )
0336         { return surface; }
0337 
0338         // cached not found, create new
0339         Cairo::Surface surface( createSurface( 32, height ) );
0340 
0341         {
0342             ColorUtils::Rgba top( ColorUtils::backgroundTopColor( base ) );
0343             ColorUtils::Rgba bottom( ColorUtils::backgroundBottomColor( base ) );
0344             Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 0, 0, height ) );
0345             cairo_pattern_add_color_stop( pattern, 0, top );
0346             cairo_pattern_add_color_stop( pattern, 0.5, base );
0347             cairo_pattern_add_color_stop( pattern, 1, bottom );
0348 
0349             Cairo::Context context( surface );
0350             cairo_set_source( context, pattern );
0351             cairo_rectangle( context, 0, 0, 32, height );
0352             cairo_fill( context );
0353         }
0354 
0355         return _verticalGradientCache.insert( key, surface );
0356     }
0357 
0358     //_________________________________________________
0359     const Cairo::Surface& StyleHelper::radialGradient( const ColorUtils::Rgba& base, int radius )
0360     {
0361 
0362         const RadialGradientKey key( base, radius );
0363 
0364         // try find in cache and return
0365         if( const Cairo::Surface& surface = _radialGradientCache.value(key) )
0366         { return surface; }
0367 
0368         // cached not found, create new
0369         Cairo::Surface surface( createSurface( 2*radius, radius ) );
0370 
0371         {
0372             // create radial pattern
0373             ColorUtils::Rgba radial( ColorUtils::backgroundRadialColor( base ) );
0374             Cairo::Pattern pattern( cairo_pattern_create_radial( radius, 0, radius ) );
0375             cairo_pattern_add_color_stop( pattern, 0, radial );
0376             cairo_pattern_add_color_stop( pattern, 0.5, ColorUtils::alphaColor( radial, 101.0/255 ) );
0377             cairo_pattern_add_color_stop( pattern, 0.75, ColorUtils::alphaColor( radial, 37.0/255 ) );
0378             cairo_pattern_add_color_stop( pattern, 1.0, ColorUtils::alphaColor( radial, 0 ) );
0379 
0380             Cairo::Context context( surface );
0381             cairo_set_source( context, pattern );
0382             cairo_rectangle( context, 0, 0, 2*radius, radius );
0383             cairo_fill( context );
0384         }
0385 
0386         return _radialGradientCache.insert( key, surface );
0387 
0388     }
0389 
0390     //_________________________________________________
0391     const TileSet& StyleHelper::slab(const ColorUtils::Rgba& base, const ColorUtils::Rgba& glow, double shade, int size)
0392     {
0393 
0394         const SlabKey key( base, glow, shade, size );
0395         const TileSet& tileSet( _slabCache.value( key ) );
0396         if( tileSet.isValid() ) return tileSet;
0397 
0398         // create surface and initialize
0399         const int w( 2*size );
0400         const int h( 2*size );
0401         Cairo::Surface surface( createSurface( w, h ) );
0402 
0403         {
0404             // create cairo context
0405             Cairo::Context context( surface );
0406             cairo_scale( context, size/7.0, size/7.0 );
0407             cairo_rectangle( context, 0, 0, 14, 14 );
0408             cairo_set_source( context, ColorUtils::Rgba::transparent( base ) );
0409             cairo_fill( context );
0410 
0411             if( base.isValid() ) drawShadow( context, ColorUtils::shadowColor( base ), 14 );
0412             if( glow.isValid() ) drawOuterGlow( context, glow, 14 );
0413             if( base.isValid() ) drawSlab( context, base, shade );
0414 
0415         }
0416 
0417         // create tileSet
0418         return _slabCache.insert( key, TileSet( surface,  size, size, size, size, size-1, size, 2, 1) );
0419 
0420     }
0421 
0422     //__________________________________________________________________
0423     const TileSet& StyleHelper::slabSunken( const ColorUtils::Rgba& base, int size )
0424     {
0425 
0426         const SlabKey key( base, 0.0, size );
0427         const TileSet& tileSet( _slabSunkenCache.value( key ) );
0428         if( tileSet.isValid() ) return tileSet;
0429 
0430         // create surface and initialize
0431         const int w( 2*size );
0432         const int h( 2*size );
0433         Cairo::Surface surface( createSurface( w, h ) );
0434 
0435         {
0436 
0437             // create cairo context
0438             Cairo::Context context( surface );
0439             cairo_set_line_width( context, 1.0 );
0440             cairo_scale( context, size/7.0, size/7.0 );
0441             cairo_rectangle( context, 0, 0, 14, 14 );
0442             cairo_set_source( context, ColorUtils::Rgba::transparent( base ) );
0443             cairo_fill( context );
0444 
0445             if( base.isValid() )
0446             {
0447                 drawInverseShadow( context, ColorUtils::shadowColor(base), 3, 8, 0.0);
0448 
0449                 // contrast
0450                 {
0451                     const ColorUtils::Rgba light( ColorUtils::lightColor( base ) );
0452                     Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 2, 0, 16 ) );
0453                     cairo_pattern_add_color_stop( pattern, 0.5, ColorUtils::Rgba::transparent( light ) );
0454                     cairo_pattern_add_color_stop( pattern, 1.0, light );
0455                     cairo_set_source( context, pattern );
0456                     cairo_rounded_rectangle( context, 2.5, 2.5, 9, 9, 4.0 );
0457                     cairo_stroke( context );
0458                 }
0459 
0460             }
0461 
0462         }
0463 
0464         // create tileSet
0465         return _slabSunkenCache.insert( key, TileSet( surface,  size, size, size, size, size-1, size, 2, 1) );
0466 
0467     }
0468 
0469     //______________________________________________________________________________
0470     const Cairo::Surface& StyleHelper::roundSlab(const ColorUtils::Rgba& base, const ColorUtils::Rgba& glow, double shade, int size)
0471     {
0472 
0473         SlabKey key( base, glow, shade, size );
0474 
0475         // try find in cache and return
0476         if( const Cairo::Surface& surface = _roundSlabCache.value( key ) )
0477         { return surface; }
0478 
0479         // cached not found, create new
0480         const int w( 3*size );
0481         const int h( 3*size );
0482         Cairo::Surface surface( createSurface( w, h ) );
0483 
0484         // create cairo context
0485         Cairo::Context context( surface );
0486         cairo_scale( context, size/7.0, size/7.0 );
0487 
0488         // shadow
0489         if( base.isValid() ) drawShadow( context, ColorUtils::shadowColor(base), 21 );
0490         if( glow.isValid() ) drawOuterGlow( context, glow, 21 );
0491         if( base.isValid() ) drawRoundSlab( context, base, shade );
0492 
0493         // note: we can't return the surface directly, because it is a temporary
0494         // we have to return the inserted object instead
0495         return _roundSlabCache.insert( key, surface );
0496 
0497     }
0498 
0499 
0500     //__________________________________________________________________________________________________________
0501     const Cairo::Surface&  StyleHelper::sliderSlab(const ColorUtils::Rgba& base, const ColorUtils::Rgba& glow, bool sunken, double shade, int size)
0502     {
0503 
0504         SliderSlabKey key( base, glow, sunken, shade, size );
0505 
0506         // try find in cache and return
0507         if( const Cairo::Surface& surface = _sliderSlabCache.value( key ) )
0508         { return surface; }
0509 
0510         // cached not found, create new
0511         const int w( 3*size );
0512         const int h( 3*size );
0513         Cairo::Surface surface( createSurface( w, h ) );
0514 
0515         {
0516             Cairo::Context context( surface );
0517             cairo_set_antialias( context, CAIRO_ANTIALIAS_SUBPIXEL );
0518 
0519             cairo_save( context );
0520             cairo_scale( context, 3.0*size/23, 3.0*size/23 );
0521             cairo_translate( context, 1, 1 );
0522 
0523             if( base.isValid() ) drawShadow( context, ColorUtils::alphaColor( ColorUtils::shadowColor(base), 0.8 ), 21 );
0524             if( glow.isValid() ) drawOuterGlow( context, glow, 21 );
0525             cairo_restore( context );
0526 
0527             cairo_scale( context, 3.0*size/25, 3.0*size/25 );
0528             cairo_translate( context, 2, 2 );
0529             drawSliderSlab( context, base, sunken, shade );
0530 
0531         }
0532 
0533         return _sliderSlabCache.insert( key, surface );
0534 
0535     }
0536 
0537     //________________________________________________________________________________________________________
0538     const TileSet &StyleHelper::slope( const ColorUtils::Rgba& base, double shade, int size )
0539     {
0540 
0541         const SlabKey key( base, shade, size );
0542         const TileSet& tileSet( _slopeCache.value( key ) );
0543         if( tileSet.isValid() ) return tileSet;
0544 
0545         const int w( 4*size );
0546         const int h( 4*size );
0547         Cairo::Surface surface( createSurface( w, h ) );
0548 
0549         {
0550             Cairo::Context context( surface );
0551             const TileSet &slabTileSet = slab( base, shade, size );
0552             slabTileSet.render( context, 0, 0, size*4, size*5, TileSet::Left | TileSet::Right | TileSet::Top );
0553 
0554         }
0555 
0556         return _slopeCache.insert( key, TileSet( surface,  size, size, size, size, size-1, size, 2, 1) );
0557 
0558     }
0559 
0560     //__________________________________________________________________
0561     void StyleHelper::fillSlab( cairo_t* context, int x, int y, int w, int h, const TileSet::Tiles& tiles ) const
0562     {
0563 
0564         const double s( 3.6 + ( 0.5 * _slabThickness )  );
0565         const double r( s/2 );
0566 
0567         Corners corners( CornersNone );
0568         if( tiles & TileSet::Top )
0569         {
0570             if( tiles & TileSet::Left ) corners |= CornersTopLeft;
0571             if( tiles & TileSet::Right ) corners |= CornersTopRight;
0572         }
0573 
0574         if( tiles & TileSet::Bottom )
0575         {
0576             if( tiles & TileSet::Left ) corners |= CornersBottomLeft;
0577             if( tiles & TileSet::Right ) corners |= CornersBottomRight;
0578         }
0579 
0580 
0581         cairo_rounded_rectangle( context, x+s, y+s, w-2*s, h-2*s, r, corners );
0582         cairo_fill( context );
0583     }
0584 
0585     //__________________________________________________________________
0586     GdkPixmap* StyleHelper::roundMask( int w, int h, int radius ) const
0587     {
0588 
0589         GdkPixmap* mask( gdk_pixmap_new( 0L, w, h, 1 ) );
0590 
0591         {
0592             Cairo::Context context( GDK_DRAWABLE(mask) );
0593 
0594             // clear the window
0595             cairo_set_operator( context, CAIRO_OPERATOR_SOURCE );
0596             cairo_set_source( context, ColorUtils::Rgba::transparent() );
0597             cairo_paint( context );
0598 
0599             // now draw roundrect mask
0600             cairo_set_operator( context, CAIRO_OPERATOR_OVER );
0601             cairo_set_source( context, ColorUtils::Rgba::black() );
0602 
0603             // FIXME: radius found empirically
0604             cairo_rounded_rectangle( context, 0, 0, w, h, radius );
0605             cairo_fill( context );
0606         }
0607 
0608         return mask;
0609 
0610     }
0611 
0612 
0613     //______________________________________________________________________________
0614     const TileSet& StyleHelper::holeFocused(
0615         const ColorUtils::Rgba &base, const ColorUtils::Rgba &fill, const ColorUtils::Rgba &glow,
0616         int size, bool contrast )
0617     {
0618 
0619         const HoleFocusedKey key( base, fill, glow, size, contrast );
0620         const TileSet& tileSet( _holeFocusedCache.value( key ) );
0621         if( tileSet.isValid() ) return tileSet;
0622 
0623         // first create shadow
0624         const int shadowSize( (size*5)/7 );
0625         Cairo::Surface shadowSurface( createSurface( 2*shadowSize, 2*shadowSize ) );
0626 
0627         {
0628             Cairo::Context context( shadowSurface );
0629             cairo_scale( context, 5.0/shadowSize, 5.0/shadowSize );
0630 
0631             // get alpha channel
0632             double alpha( glow.isValid() ? glow.alpha() : 0 );
0633             if( alpha < 1 )
0634             {
0635 
0636                 // shadow
0637                 drawInverseShadow( context, ColorUtils::alphaColor( ColorUtils::shadowColor( base ), 1.0 - alpha ), 1, 8, 0.0);
0638 
0639             }
0640 
0641             if( alpha > 0 )
0642             {
0643 
0644                 // glow
0645                 drawInverseGlow( context, glow, 1, 8, shadowSize );
0646 
0647             }
0648 
0649         }
0650 
0651         // create surface
0652         Cairo::Surface surface( createSurface( 2*size, 2*size ) );
0653         {
0654 
0655             Cairo::Context context( surface );
0656             cairo_scale( context, 7.0/size, 7.0/size );
0657             cairo_set_line_width( context, 1 );
0658 
0659             // inside
0660             if( fill.isValid() )
0661             {
0662                 cairo_rounded_rectangle( context, 1, 1, 12, 11, 2.5 );
0663                 cairo_set_source( context, fill );
0664                 cairo_fill( context );
0665             }
0666 
0667             // draw shadow
0668             TileSet(
0669                 shadowSurface, shadowSize, shadowSize, shadowSize, shadowSize,
0670                 shadowSize-1, shadowSize, 2, 1 ).
0671                 render( context, 0, 0, size*2, size*2 );
0672 
0673             // contrast pixel
0674             if( contrast )
0675             {
0676                 const ColorUtils::Rgba light( ColorUtils::lightColor( base ) );
0677                 Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 0, 0, 18 ) );
0678                 cairo_pattern_add_color_stop( pattern, 0.5, ColorUtils::Rgba::transparent( light ) );
0679                 cairo_pattern_add_color_stop( pattern, 1.0, light );
0680                 cairo_set_source( context, pattern );
0681                 cairo_rounded_rectangle( context, 0.5, 0.5, 13, 13, 4.0 );
0682                 cairo_stroke( context );
0683 
0684             }
0685 
0686         }
0687 
0688         return _holeFocusedCache.insert( key, TileSet( surface, size, size, size, size, size-1, size, 2, 1 ) );
0689 
0690     }
0691 
0692     //________________________________________________________________________________________________________
0693     const TileSet& StyleHelper::holeFlat( const ColorUtils::Rgba& base, double shade, bool fill, int size )
0694     {
0695 
0696         const HoleFlatKey key( base, shade, fill, size );
0697         const TileSet& tileSet( _holeFlatCache.value( key ) );
0698         if( tileSet.isValid() ) return tileSet;
0699 
0700         const int w( 2*size );
0701         const int h( 2*size );
0702 
0703         Cairo::Surface surface( createSurface( w, h ) );
0704 
0705         if( fill )
0706         {
0707 
0708             Cairo::Context context( surface );
0709             cairo_set_line_width( context, 1.0 );
0710 
0711             cairo_scale( context, 14.0/w, 14.0/h );
0712 
0713             // hole inside
0714             cairo_set_source( context, base );
0715             cairo_rounded_rectangle( context, 1, 0, 12, 13, 3.0 );
0716             cairo_fill( context );
0717 
0718             {
0719                 // shadow (top)
0720                 const ColorUtils::Rgba dark( ColorUtils::shade( ColorUtils::darkColor( base ), shade ) );
0721                 Cairo::Pattern pattern( cairo_pattern_create_linear( 0, -2, 0, 14 ) );
0722                 cairo_pattern_add_color_stop( pattern, 0, dark );
0723                 cairo_pattern_add_color_stop( pattern, 0.4, ColorUtils::Rgba::transparent( dark ) );
0724                 cairo_set_source( context, pattern );
0725                 cairo_rounded_rectangle( context, 1.5, 0.5, 11, 12, 2.5 );
0726                 cairo_stroke( context );
0727             }
0728 
0729             {
0730                 // contrast (bottom)
0731                 const ColorUtils::Rgba light( ColorUtils::shade( ColorUtils::lightColor( base ), shade ) );
0732                 Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 0, 0, 18 ) );
0733                 cairo_pattern_add_color_stop( pattern, 0.5, ColorUtils::Rgba::transparent( light ) );
0734                 cairo_pattern_add_color_stop( pattern, 1.0, light );
0735                 cairo_set_source( context, pattern );
0736                 cairo_rounded_rectangle( context, 0.5, 0.5, 13, 13, 3.5 );
0737                 cairo_stroke( context );
0738             }
0739 
0740         } else {
0741 
0742             Cairo::Context context( surface );
0743             cairo_set_line_width( context, 1.0 );
0744 
0745             cairo_scale( context, 14.0/w, 14.0/h );
0746 
0747             // hole inside
0748             cairo_set_source( context, base );
0749             cairo_rounded_rectangle( context, 1, 1, 12, 12, 3.0 );
0750             cairo_fill( context );
0751 
0752             {
0753                 // shadow (top)
0754                 const ColorUtils::Rgba dark( ColorUtils::shade( ColorUtils::darkColor( base ), shade ) );
0755                 Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 1, 0, 12 ) );
0756                 cairo_pattern_add_color_stop( pattern, 0, dark );
0757                 cairo_pattern_add_color_stop( pattern, 0.4, ColorUtils::Rgba::transparent( dark ) );
0758                 cairo_set_source( context, pattern );
0759                 cairo_rounded_rectangle( context, 1.5, 1.5, 11, 11, 2.5 );
0760                 cairo_stroke( context );
0761             }
0762 
0763             {
0764                 // contrast (bottom)
0765                 const ColorUtils::Rgba light( ColorUtils::shade( ColorUtils::lightColor( base ), shade ) );
0766                 Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 1, 0, 12 ) );
0767                 cairo_pattern_add_color_stop( pattern, 0.5, ColorUtils::Rgba::transparent( light ) );
0768                 cairo_pattern_add_color_stop( pattern, 1.0, light );
0769                 cairo_set_source( context, pattern );
0770                 cairo_rounded_rectangle( context, 1.5, 1.5, 11, 11, 2.5 );
0771                 cairo_stroke( context );
0772             }
0773 
0774 
0775         }
0776 
0777         return _holeFlatCache.insert( key, TileSet( surface, size, size, size, size, size-1, size, 2, 1 ) );
0778 
0779     }
0780 
0781     //______________________________________________________________________________
0782     const TileSet& StyleHelper::scrollHole( const ColorUtils::Rgba& base, bool vertical, bool smallShadow )
0783     {
0784 
0785         const ScrollHoleKey key( base, vertical, smallShadow );
0786         const TileSet& tileSet( _scrollHoleCache.value( key ) );
0787         if( tileSet.isValid() ) return tileSet;
0788 
0789         // define colors
0790         const ColorUtils::Rgba& dark( ColorUtils::darkColor( base ) );
0791         const ColorUtils::Rgba& light( ColorUtils::lightColor( base ) );
0792         const ColorUtils::Rgba& shadow( ColorUtils::shadowColor( base ) );
0793 
0794         // create pixmap
0795         const int w( 15 );
0796         const int h( 15 );
0797         Cairo::Surface surface( createSurface( w, h ) );
0798 
0799         {
0800             Cairo::Context context( surface );
0801 
0802             GdkRectangle r = { 0, 0, w, h };
0803             GdkRectangle rect = { 1, 0, w-2, h-1 };
0804 
0805             // radius
0806             const double radius( smallShadow ? 2.5:3.0 );
0807 
0808             // base
0809             {
0810                 cairo_set_source( context, dark );
0811                 gdk_cairo_rounded_rectangle( context, &rect, radius );
0812                 cairo_fill( context );
0813             }
0814 
0815             // light shadow across the whole hole
0816             {
0817                 Cairo::Pattern pattern;
0818                 if( vertical ) pattern.set( cairo_pattern_create_linear( rect.x, 0, rect.x + rect.width, 0 ) );
0819                 else pattern.set( cairo_pattern_create_linear( 0, rect.y, 0, rect.y + rect.height ) );
0820                 cairo_pattern_add_color_stop( pattern, 0, ColorUtils::alphaColor( shadow, 0.1 ) );
0821                 cairo_pattern_add_color_stop( pattern, 0.6, ColorUtils::Rgba::transparent( shadow ) );
0822 
0823                 cairo_set_source( context, pattern );
0824                 gdk_cairo_rounded_rectangle( context, &rect, radius );
0825                 cairo_fill( context );
0826             }
0827 
0828             // first create shadow
0829             const int shadowSize( 5 );
0830             Cairo::Surface shadowSurface( createSurface( 2*shadowSize, 2*shadowSize ) );
0831             {
0832                 Cairo::Context context( shadowSurface );
0833                 drawInverseShadow( context, ColorUtils::shadowColor( base ), 1, 8, 0.0);
0834             }
0835 
0836             // draw shadow
0837             TileSet(
0838                 shadowSurface, shadowSize, shadowSize, shadowSize, shadowSize,
0839                 shadowSize-1, shadowSize, 2, 1 ).
0840                 render( context, 0, -1, w, h+1, TileSet::Full );
0841 
0842             // light bottom border
0843             {
0844                 Cairo::Pattern pattern( cairo_pattern_create_linear( 0, r.y, 0, r.y + r.height ) );
0845 
0846                 if( smallShadow && vertical )
0847                 {
0848 
0849                     cairo_pattern_add_color_stop( pattern, 0.8, ColorUtils::Rgba::transparent( light ) );
0850                     cairo_pattern_add_color_stop( pattern, 1, ColorUtils::alphaColor( light, 0.5 ) );
0851 
0852                 } else {
0853 
0854                     cairo_pattern_add_color_stop( pattern, 0.5, ColorUtils::Rgba::transparent( light ) );
0855                     cairo_pattern_add_color_stop( pattern, 1, ColorUtils::alphaColor( light, 0.6 ) );
0856 
0857                 }
0858 
0859                 cairo_set_source( context, pattern );
0860                 cairo_set_line_width( context, 1.0 );
0861                 cairo_rounded_rectangle( context, 0.5+r.x, 0.5+r.y, r.width-1, r.height-1, radius+0.5 );
0862                 cairo_stroke( context );
0863             }
0864 
0865         }
0866 
0867         return _scrollHoleCache.insert( key, TileSet( surface, 7, 7, 1, 1 ) );
0868 
0869     }
0870 
0871     //______________________________________________________________________________
0872     const TileSet& StyleHelper::scrollHandle( const ColorUtils::Rgba& base, const ColorUtils::Rgba& glow, int size )
0873     {
0874 
0875         const ScrollHandleKey key( base, glow, size );
0876         const TileSet& tileSet( _scrollHandleCache.value( key ) );
0877         if( tileSet.isValid() ) return tileSet;
0878 
0879         // create pixmap
0880         const int w( 2*size );
0881         const int h( 2*size );
0882         Cairo::Surface surface( createSurface( w, h ) );
0883 
0884         {
0885             Cairo::Context context( surface );
0886             cairo_scale( context, (2.0*size)/14, (2.0*size)/14 );
0887 
0888             // first create shadow
0889             Cairo::Surface shadowSurface( createSurface( 10, 10 ) );
0890             {
0891                 Cairo::Context context( shadowSurface );
0892 
0893                 // shadow/glow
0894                 drawOuterGlow( context, glow, 10 );
0895 
0896             }
0897 
0898             // draw shadow
0899             TileSet( shadowSurface, 4, 4, 1, 1 ).
0900                 render( context, 0, 0, 14, 14, TileSet::Full );
0901 
0902             // outline
0903             {
0904                 const ColorUtils::Rgba mid( ColorUtils::midColor( base ) );
0905                 Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 3, 0, 11 ) );
0906                 cairo_pattern_add_color_stop( pattern, 0, base );
0907                 cairo_pattern_add_color_stop( pattern, 1, mid );
0908                 cairo_set_source( context, pattern );
0909                 cairo_rounded_rectangle( context, 3, 3, 8, 8, 2.5 );
0910                 cairo_fill( context );
0911             }
0912 
0913             // contrast
0914             {
0915                 const ColorUtils::Rgba light( ColorUtils::lightColor( base ) );
0916                 Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 3, 0, 11 ) );
0917                 cairo_pattern_add_color_stop( pattern, 0, ColorUtils::alphaColor( light, 0.9 ) );
0918                 cairo_pattern_add_color_stop( pattern, 0.5, ColorUtils::alphaColor( light, 0.44 ) );
0919                 cairo_set_source( context, pattern );
0920                 cairo_rounded_rectangle( context, 3, 3, 8, 8, 2.5 );
0921                 cairo_fill( context );
0922             }
0923 
0924         }
0925 
0926         return _scrollHandleCache.insert( key, TileSet( surface, 7, 7, 1, 1 ) );
0927 
0928     }
0929 
0930     //________________________________________________________________________________________________________
0931     const TileSet& StyleHelper::slitFocused( const ColorUtils::Rgba& glow )
0932     {
0933 
0934         const SlitFocusedKey key( glow );
0935         const TileSet& tileSet( _slitFocusedCache.value( key ) );
0936         if( tileSet.isValid() ) return tileSet;
0937 
0938         // create pixmap
0939         const int w( 9 );
0940         const int h( 9 );
0941         Cairo::Surface surface( createSurface( w, h ) );
0942         {
0943             Cairo::Context context( surface );
0944 
0945             cairo_pattern_t* pattern( cairo_pattern_create_radial( 4.5, 4.5, 3.5 ) );
0946             cairo_pattern_add_color_stop( pattern, 1.0, ColorUtils::alphaColor( glow, 180.0/255 ) );
0947             cairo_pattern_add_color_stop( pattern,  0.5, ColorUtils::Rgba::transparent( glow ) );
0948 
0949             cairo_set_source( context, pattern );
0950             cairo_ellipse( context, 1, 1, 7, 7 ) ;
0951             cairo_fill( context );
0952 
0953         }
0954 
0955         return _slitFocusedCache.insert( key, TileSet( surface, 4, 4, 1, 1 ) );
0956 
0957     }
0958 
0959     //____________________________________________________________________
0960     const TileSet& StyleHelper::dockFrame( const ColorUtils::Rgba &top, const ColorUtils::Rgba& bottom )
0961     {
0962 
0963         // key
0964         DockFrameKey key( top, bottom );
0965         const TileSet& tileSet( _dockFrameCache.value( key ) );
0966         if( tileSet.isValid() ) return tileSet;
0967 
0968         // fixed height
0969         const int size( 13 );
0970 
0971         Cairo::Surface surface( createSurface( size, size ) );
0972         {
0973 
0974             Cairo::Context context( surface );
0975             cairo_set_line_width( context, 1.0 );
0976 
0977             // local rectangle
0978             const double w( size );
0979             const double h( size );
0980 
0981             const ColorUtils::Rgba lightTop( ColorUtils::alphaColor( ColorUtils::lightColor( top ), 0.5 ) );
0982             const ColorUtils::Rgba lightBottom( ColorUtils::alphaColor( ColorUtils::lightColor( bottom ), 0.5 ) );
0983             const ColorUtils::Rgba darkTop( ColorUtils::alphaColor( ColorUtils::darkColor( top ), 0.5 ) );
0984             const ColorUtils::Rgba darkBottom( ColorUtils::alphaColor( ColorUtils::darkColor( bottom ), 0.5 ) );
0985 
0986             // dark frame
0987             {
0988                 Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 0.5, 0, h - 1.5 ) );
0989                 cairo_pattern_add_color_stop( pattern, 0, darkTop );
0990                 cairo_pattern_add_color_stop( pattern, 1, darkBottom );
0991                 cairo_set_source( context, pattern );
0992                 cairo_rounded_rectangle( context, 1.5, 0.5, w-3, h-2, 4 );
0993                 cairo_stroke( context );
0994             }
0995 
0996             // bottom contrast
0997             {
0998                 Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 0.5, 0, h - 0.5 ) );
0999                 cairo_pattern_add_color_stop( pattern, 0, ColorUtils::Rgba::transparent( lightBottom ) );
1000                 cairo_pattern_add_color_stop( pattern, 1, lightBottom );
1001                 cairo_set_source( context, pattern );
1002                 cairo_rounded_rectangle( context, 0.5, 0.5, w-1, h-1, 4.5 );
1003                 cairo_stroke( context );
1004             }
1005 
1006             // top contrast
1007             {
1008                 Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 1.5, 0, h - 2.5 ) );
1009                 cairo_pattern_add_color_stop( pattern, 0, lightTop );
1010                 cairo_pattern_add_color_stop( pattern, 1, ColorUtils::Rgba::transparent( lightTop ) );
1011                 cairo_rounded_rectangle( context, 2.5, 1.5, w-5, h-4, 3.5 );
1012                 cairo_set_source( context, pattern );
1013                 cairo_stroke( context );
1014             }
1015 
1016         }
1017 
1018         return _dockFrameCache.insert( key, TileSet( surface, (size-1)/2, (size-1)/2, 1, 1 ) );
1019     }
1020 
1021     //____________________________________________________________________
1022     const Cairo::Surface& StyleHelper::progressBarIndicator(const ColorUtils::Rgba& base, const ColorUtils::Rgba& highlight, int w, int h )
1023     {
1024 
1025         ProgressBarIndicatorKey key( base, highlight, w, h );
1026 
1027         // try find in cache and return
1028         if( const Cairo::Surface& surface = _progressBarIndicatorCache.value( key ) )
1029         { return surface; }
1030 
1031         // cached not found, create new
1032 
1033         // local rect
1034         int xl = 0;
1035         int yl = 0;
1036         int wl = w;
1037         int hl = h;
1038 
1039         Cairo::Surface surface( createSurface( wl, hl ) );
1040         Cairo::Context context( surface );
1041 
1042         // colors
1043         const ColorUtils::Rgba lhighlight( ColorUtils::lightColor( highlight ) );
1044         const ColorUtils::Rgba light( ColorUtils::lightColor( base ) );
1045         const ColorUtils::Rgba dark( ColorUtils::darkColor( base ) );
1046         const ColorUtils::Rgba shadow( ColorUtils::shadowColor( base ) );
1047 
1048         {
1049             // shadow
1050             cairo_rounded_rectangle( context, xl+0.5, yl+0.5, wl-1, hl, 3.0 );
1051             cairo_set_source( context, ColorUtils::alphaColor( shadow, 0.6 ) );
1052             cairo_set_line_width( context, 0.6 );
1053             cairo_stroke( context );
1054         }
1055 
1056         xl += 1;
1057         yl += 1;
1058         wl -= 2;
1059         hl -= 1;
1060 
1061         {
1062             // filling
1063             cairo_set_source( context, ColorUtils::mix( highlight, dark, 0.2 ) );
1064             cairo_rounded_rectangle( context, xl, yl, wl, hl, 2.5 );
1065             cairo_fill( context );
1066         }
1067 
1068         // create pattern pixbuf
1069         {
1070 
1071             Cairo::Pattern mask( cairo_pattern_create_linear( 0, 0, wl, 0 ) );
1072             cairo_pattern_add_color_stop( mask, 0, ColorUtils::Rgba::transparent() );
1073             cairo_pattern_add_color_stop( mask, 0.4, ColorUtils::Rgba::black() );
1074             cairo_pattern_add_color_stop( mask, 0.6, ColorUtils::Rgba::black() );
1075             cairo_pattern_add_color_stop( mask, 1.0, ColorUtils::Rgba::transparent() );
1076 
1077             const ColorUtils::Rgba mix( ColorUtils::mix( lhighlight, light, 0.3 ) );
1078             Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 0, 0, hl ) );
1079             cairo_pattern_add_color_stop( pattern, 0,  mix );
1080             cairo_pattern_add_color_stop( pattern, 0.5, ColorUtils::Rgba::transparent( mix ) );
1081             cairo_pattern_add_color_stop( pattern, 0.6, ColorUtils::Rgba::transparent( mix ) );
1082             cairo_pattern_add_color_stop( pattern, 1.0, mix );
1083 
1084             Cairo::Surface localSurface( createSurface( wl, hl ) );
1085             Cairo::Context localContext( localSurface );
1086             cairo_rectangle( localContext, 0, 0, wl, hl );
1087             cairo_set_source( localContext, pattern );
1088             cairo_mask( localContext, mask );
1089             localContext.free();
1090 
1091             cairo_save( context );
1092             cairo_translate( context, 1, 1 );
1093             cairo_rectangle( context, 0, 0, wl, hl );
1094             cairo_set_source_surface( context, localSurface, 0, 0 );
1095             cairo_fill( context );
1096             cairo_restore( context );
1097 
1098         }
1099 
1100         {
1101             // bevel
1102             Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 0, 0, hl ) );
1103             cairo_pattern_add_color_stop( pattern, 0.0, lhighlight );
1104             cairo_pattern_add_color_stop( pattern, 0.5, highlight );
1105             cairo_pattern_add_color_stop( pattern, 1.0, ColorUtils::darkColor(highlight) );
1106             cairo_set_line_width( context, 1.0 );
1107             cairo_set_source( context, pattern );
1108             cairo_rounded_rectangle( context, xl+0.5, yl+0.5, wl-1, hl-1, 2.5 );
1109             cairo_stroke( context );
1110         }
1111 
1112         {
1113             // bright top edge
1114             Cairo::Pattern pattern( cairo_pattern_create_linear( xl, 0, xl+wl, 0 ) );
1115             const ColorUtils::Rgba mix( ColorUtils::mix( lhighlight, light, 0.8 ) );
1116             cairo_pattern_add_color_stop( pattern, 0.0, ColorUtils::Rgba::transparent( mix ) );
1117             cairo_pattern_add_color_stop( pattern, 0.5, mix );
1118             cairo_pattern_add_color_stop( pattern, 1.0, ColorUtils::Rgba::transparent( mix ) );
1119             cairo_set_line_width( context, 1.0 );
1120             cairo_set_source( context, pattern );
1121             cairo_move_to( context, xl+0.5, yl+0.5 );
1122             cairo_line_to( context, xl+wl-0.5, yl+0.5 );
1123             cairo_stroke( context );
1124         }
1125 
1126         // note: we can't return the surface directly, because it is a temporary
1127         // we have to return the inserted object instead
1128         return _progressBarIndicatorCache.insert( key, surface );
1129 
1130     }
1131 
1132     //________________________________________________________________________________________________________
1133     const TileSet& StyleHelper::groove( const ColorUtils::Rgba &base, int size )
1134     {
1135 
1136         const GrooveKey key( base, size );
1137         const TileSet& tileSet( _grooveCache.value( key ) );
1138         if( tileSet.isValid() ) return tileSet;
1139 
1140         const int rsize( int( ceil( size * 3.0/7.0 ) ) );
1141         const int w( rsize*2 );
1142         const int h( rsize*2 );
1143         Cairo::Surface surface( createSurface( w, h ) );
1144 
1145         {
1146 
1147             Cairo::Context context( surface );
1148             cairo_scale( context, 6/w, 6/h );
1149 
1150             Cairo::Pattern pattern( inverseShadowGradient( ColorUtils::shadowColor( base ), 1, 4, 0.0 ) );
1151             cairo_set_source( context, pattern );
1152             cairo_ellipse( context, 1, 1, 4, 4 );
1153             cairo_ellipse_negative( context, 2, 2, 2, 2 );
1154             cairo_fill( context );
1155 
1156         }
1157 
1158         return _grooveCache.insert( key, TileSet( surface, rsize, rsize, rsize, rsize, rsize-1, rsize, 2, 1 ) );
1159 
1160     }
1161 
1162     //____________________________________________________________________
1163     const TileSet& StyleHelper::selection( const ColorUtils::Rgba& base, int h, bool custom )
1164     {
1165 
1166         const SelectionKey key( base, h, custom );
1167         const TileSet& tileSet( _selectionCache.value( key ) );
1168         if( tileSet.isValid() ) return tileSet;
1169 
1170         const int w = 32+16;
1171         Cairo::Surface surface( createSurface( w, h ) );
1172 
1173         {
1174 
1175             // adjust height
1176             const int x = 0;
1177             const int y = 0;
1178             const double rounding( 2.5 );
1179 
1180             Cairo::Context context( surface );
1181             cairo_set_line_width( context, 1.0 );
1182 
1183             {
1184                 // filling
1185                 const int lightenAmount( custom ? 110 : 130 );
1186                 const ColorUtils::Rgba light( base.light( lightenAmount ) );
1187                 Cairo::Pattern pattern( cairo_pattern_create_linear( 0, y, 0, y+h ) );
1188                 cairo_pattern_add_color_stop( pattern, 0, light );
1189                 cairo_pattern_add_color_stop( pattern, 1, base );
1190 
1191                 cairo_rounded_rectangle( context, x, y, w, h, rounding );
1192                 cairo_set_source( context, pattern );
1193                 cairo_fill( context );
1194             }
1195 
1196             {
1197                 // contrast
1198                 Cairo::Pattern pattern( cairo_pattern_create_linear( 0, y, 0, y+h ) );
1199                 cairo_pattern_add_color_stop( pattern, 0, base );
1200                 cairo_pattern_add_color_stop( pattern, 1, ColorUtils::Rgba::transparent() );
1201 
1202                 cairo_rounded_rectangle( context, x+0.5, y+0.5, w-1, h-1, rounding );
1203                 cairo_set_source( context, pattern );
1204                 cairo_stroke( context );
1205             }
1206 
1207         }
1208 
1209         return _selectionCache.insert( key, TileSet( surface, 8, 0, 32, h ) );
1210 
1211     }
1212 
1213     //____________________________________________________________________
1214     void StyleHelper::renderDot( cairo_t* context, const ColorUtils::Rgba& base, int x, int y ) const
1215     {
1216         // Reduce diameter to make dots look more like in Oxygen-Qt
1217         const double diameter( 1.8 - 0.35 );
1218         const ColorUtils::Rgba light( ColorUtils::lightColor( base ) );
1219         const ColorUtils::Rgba dark( ColorUtils::darkColor( base ).dark(130) );
1220 
1221         cairo_ellipse( context, x + 1.0 - diameter/2, y + 1.0 - diameter/2.0, diameter, diameter );
1222         cairo_set_source( context, light );
1223         cairo_fill( context );
1224 
1225         cairo_ellipse( context, x + 0.5 - diameter/2, y + 0.5 - diameter/2.0, diameter, diameter );
1226         cairo_set_source( context, dark );
1227         cairo_fill( context );
1228     }
1229 
1230     //______________________________________________________________________________________
1231     void StyleHelper::drawSlab( Cairo::Context& context, const ColorUtils::Rgba& color, double shade) const
1232     {
1233 
1234         const ColorUtils::Rgba light( ColorUtils::shade( lightColor(color), shade ) );
1235         const ColorUtils::Rgba base( ColorUtils::alphaColor( light, 0.85 ) );
1236         const ColorUtils::Rgba dark( ColorUtils::shade( darkColor(color), shade ) );
1237 
1238         // bevel, part 1
1239         {
1240 
1241             const double y( ColorUtils::luma(base) );
1242             const double yl( ColorUtils::luma(light) );
1243             const double yd( ColorUtils::luma(dark) );
1244 
1245             // create pattern
1246             Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 7, 0, 11 ) );
1247             cairo_pattern_add_color_stop( pattern, 0, light );
1248             if (y < yl && y > yd)
1249             {
1250                 // no middle when color is very light/dark
1251                 cairo_pattern_add_color_stop( pattern, 0.5, base );
1252             }
1253 
1254             cairo_pattern_add_color_stop( pattern, 0.9, base );
1255             cairo_set_source( context, pattern );
1256             cairo_rounded_rectangle( context, 3.0, 3.0, 8, 8, 3.5 );
1257             cairo_fill( context );
1258 
1259         }
1260 
1261         // bevel, part 2
1262         if( _slabThickness > 0.0 )
1263         {
1264 
1265             /*
1266             for some reason need to make ic a bit thicker
1267             this is likely due to differences between cairo and qpainter
1268             as a result, need to make the second pattern brighter
1269             */
1270             Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 6, 0, 19 ) );
1271             cairo_pattern_add_color_stop( pattern, 0, light );
1272             cairo_pattern_add_color_stop( pattern, 0.9, base );
1273             cairo_set_source( context, pattern );
1274             cairo_ellipse( context, 3.6, 3.6, 6.8, 6.8 );
1275             cairo_fill( context );
1276 
1277         }
1278 
1279         // inside mask
1280         cairo_save( context );
1281         cairo_set_operator( context, CAIRO_OPERATOR_DEST_OUT );
1282         cairo_set_source( context, ColorUtils::Rgba::black() );
1283 
1284         const double ic( 3.6 + 0.5*_slabThickness );
1285         const double is( 14.0 - 2.0*ic );
1286         cairo_ellipse( context, ic, ic, is, is );
1287         cairo_fill( context );
1288         cairo_restore( context );
1289 
1290     }
1291 
1292     //___________________________________________________________________________________________
1293     void StyleHelper::drawShadow( cairo_t* context, const ColorUtils::Rgba& base, int size ) const
1294     {
1295 
1296         const double m( 0.5*size-1 );
1297         const double offset( 0.8 );
1298         const double k0( (m-4.0) / m );
1299 
1300         const double x(m+1);
1301         const double y(m+offset+1);
1302 
1303         Cairo::Pattern pattern( cairo_pattern_create_radial(x, y, m ) );
1304         for (int i = 0; i < 8; i++)
1305         {
1306             // sinusoidal pattern
1307             const double k1( (k0 * (8 - i) + i) * 0.125 );
1308             const double a( (cos( M_PI * i * 0.125) + 1.0) * 0.30 );
1309             cairo_pattern_add_color_stop( pattern, k1, ColorUtils::alphaColor( base, a*_shadowGain ) );
1310         }
1311 
1312         cairo_pattern_add_color_stop( pattern, 1, ColorUtils::Rgba::transparent( base ) );
1313         cairo_set_source( context, pattern );
1314         cairo_ellipse( context, 0, 0, size, size );
1315         cairo_fill( context );
1316 
1317     }
1318 
1319     //_______________________________________________________________________
1320     void StyleHelper::drawOuterGlow( cairo_t* context, const ColorUtils::Rgba& base, int size ) const
1321     {
1322 
1323         const double m( size*0.5 );
1324         const double w( 3 );
1325 
1326         const double bias( _glowBias * 14.0/size );
1327 
1328         // k0 is located at w - bias from the outer edge
1329         const double gm( m + bias - 0.9 );
1330         const double k0( (m-w+bias) / gm );
1331 
1332         Cairo::Pattern pattern( cairo_pattern_create_radial( m, m, gm ) );
1333         for (int i = 0; i < 8; i++)
1334         {
1335             // parabolic pattern
1336             const double k1( k0 + i*(1.0-k0)/8.0 );
1337             const double a( 1.0 - sqrt(i/8.0) );
1338             cairo_pattern_add_color_stop( pattern, k1, ColorUtils::alphaColor( base, a ) );
1339         }
1340 
1341         cairo_pattern_add_color_stop( pattern, 1, ColorUtils::Rgba::transparent( base ) );
1342 
1343         cairo_set_source( context, pattern );
1344         cairo_ellipse( context, 0, 0, size, size );
1345         cairo_fill( context );
1346 
1347         cairo_save( context );
1348         cairo_set_operator( context, CAIRO_OPERATOR_DEST_OUT );
1349         cairo_set_source( context, ColorUtils::Rgba::black() );
1350         cairo_ellipse( context, w+0.5, w+0.5, size - 2*w-1, size - 2*w-1 );
1351         cairo_fill( context );
1352         cairo_restore( context );
1353 
1354     }
1355 
1356     //________________________________________________________________________________________________________
1357     void StyleHelper::drawInverseShadow(
1358         Cairo::Context& context, const ColorUtils::Rgba& base,
1359         int pad, int size, double fuzz ) const
1360     {
1361 
1362         Cairo::Pattern pattern( inverseShadowGradient( base, pad, size, fuzz ) );
1363 
1364         cairo_set_source( context, pattern );
1365         cairo_ellipse( context, pad-fuzz, pad-fuzz, size+fuzz*2.0, size+fuzz*2.0 );
1366         cairo_fill( context );
1367 
1368     }
1369 
1370     //________________________________________________________________________________________________________
1371     void StyleHelper::drawInverseGlow(
1372         Cairo::Context& context, const ColorUtils::Rgba& base,
1373         int pad, int size, int rsize ) const
1374     {
1375 
1376         const double m( size*0.5 );
1377 
1378         const double width( 3.5 );
1379         const double bias( _glowBias*7.0/rsize );
1380         const double k0( ( m-width )/( m-bias ) );
1381         Cairo::Pattern pattern( cairo_pattern_create_radial( pad+m, pad+m, m-bias ) );
1382         for ( int i = 0; i < 8; i++ )
1383         {
1384             // inverse parabolic gradient
1385             double k1 = ( k0 * i + 8 - i ) * 0.125;
1386             double a = 1.0 - sqrt( i * 0.125 );
1387             cairo_pattern_add_color_stop( pattern, k1, ColorUtils::alphaColor( base, a ) );
1388 
1389         }
1390 
1391         cairo_pattern_add_color_stop( pattern, k0, ColorUtils::Rgba::transparent( base ) );
1392         cairo_set_source( context, pattern );
1393         cairo_ellipse( context, pad, pad, size, size );
1394         cairo_fill( context );
1395         return;
1396     }
1397 
1398     //__________________________________________________________________________________________________________
1399     void StyleHelper::drawRoundSlab( Cairo::Context& context, const ColorUtils::Rgba& color, double shade ) const
1400     {
1401 
1402         // colors
1403         const ColorUtils::Rgba base( ColorUtils::shade(color, shade) );
1404         const ColorUtils::Rgba light( ColorUtils::shade( ColorUtils::lightColor(color), shade) );
1405 
1406         // bevel, part 1
1407         {
1408             Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 10, 0, 18 ) );
1409             cairo_pattern_add_color_stop( pattern, 0, light );
1410             cairo_pattern_add_color_stop( pattern, 0.9, ColorUtils::alphaColor( light, 0.85 ) );
1411             cairo_set_source( context, pattern );
1412             cairo_ellipse( context, 3, 3, 15, 15 );
1413             cairo_fill( context );
1414         }
1415 
1416         // bevel, part 2
1417         if( _slabThickness > 0 )
1418         {
1419             Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 7, 0, 28 ) );
1420             cairo_pattern_add_color_stop( pattern, 0, light );
1421             cairo_pattern_add_color_stop( pattern, 0.9, base );
1422             cairo_set_source( context, pattern );
1423             cairo_ellipse( context, 3.6, 3.6, 13.8, 13.8 );
1424             cairo_fill( context );
1425         }
1426 
1427         // inside
1428         {
1429             Cairo::Pattern pattern( cairo_pattern_create_linear( 0, -17, 0, 20 ) );
1430             cairo_pattern_add_color_stop( pattern, 0, light );
1431             cairo_pattern_add_color_stop( pattern, 1, base );
1432             cairo_set_source( context, pattern );
1433 
1434             const double ic( 3.6 + _slabThickness );
1435             const double is( 21 - 2.0*ic );
1436             cairo_ellipse( context, ic, ic, is, is );
1437             cairo_fill( context );
1438         }
1439 
1440         return;
1441 
1442     }
1443 
1444     //__________________________________________________________________________________________________________
1445     void StyleHelper::drawSliderSlab( Cairo::Context& context, const ColorUtils::Rgba& color, bool sunken, double shade ) const
1446     {
1447 
1448         const ColorUtils::Rgba light( ColorUtils::shade( ColorUtils::lightColor(color), shade) );
1449         const ColorUtils::Rgba dark( ColorUtils::shade( ColorUtils::darkColor( color ), shade) );
1450 
1451         {
1452             //plain background
1453             Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 3, 0, 21 ) );
1454             cairo_pattern_add_color_stop( pattern, 0, light );
1455             cairo_pattern_add_color_stop( pattern, 1, dark );
1456             cairo_set_source( context, pattern );
1457             cairo_ellipse( context, 3, 3, 15, 15 );
1458             cairo_fill( context );
1459 
1460         }
1461 
1462         if( sunken )
1463         {
1464 
1465             //plain background
1466             Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 3, 0, 21 ) );
1467             cairo_pattern_add_color_stop( pattern, 0, dark );
1468             cairo_pattern_add_color_stop( pattern, 1, light );
1469             cairo_set_source( context, pattern );
1470             cairo_ellipse( context, 5, 5, 11, 11 );
1471             cairo_fill( context );
1472 
1473 
1474         }
1475 
1476         {
1477             // outline circle
1478             Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 3, 0, 30 ) );
1479             cairo_pattern_add_color_stop( pattern, 0, light );
1480             cairo_pattern_add_color_stop( pattern, 1, dark );
1481             cairo_set_source( context, pattern );
1482 
1483             cairo_ellipse( context, 3.5, 3.5, 14, 14 );
1484             cairo_set_line_width( context, 1.0 );
1485             cairo_stroke( context );
1486         }
1487 
1488         return;
1489 
1490     }
1491 
1492     //________________________________________________________________________________________________________
1493     cairo_pattern_t* StyleHelper::inverseShadowGradient(
1494         const ColorUtils::Rgba& base,
1495         int pad, int size, double fuzz ) const
1496     {
1497 
1498         const double m( size*0.5 );
1499         const double offset( 0.8 );
1500         const double k0( (m-2) / (m+2.0) );
1501 
1502         const double x(pad+m);
1503         const double y(pad+m+offset);
1504 
1505         cairo_pattern_t* pattern( cairo_pattern_create_radial(x, y, m+2 ) );
1506         for (int i = 0; i < 8; i++)
1507         {
1508             // sinusoidal pattern
1509             const double k1( (8 - i + k0 * i) * 0.125 );
1510             const double a( (cos(3.14159 * i * 0.125) + 1.0) * 0.25 );
1511             cairo_pattern_add_color_stop( pattern, k1, ColorUtils::alphaColor( base, a*_shadowGain ) );
1512         }
1513 
1514         cairo_pattern_add_color_stop( pattern, k0, ColorUtils::Rgba::transparent( base ) );
1515         return pattern;
1516 
1517     }
1518 
1519 
1520     //________________________________________________________________________________________________________
1521     const Cairo::Surface& StyleHelper::dockWidgetButton(const ColorUtils::Rgba& base, bool pressed, int size)
1522     {
1523         const DockWidgetButtonKey key( base, pressed, size );
1524 
1525         // try find in cache and return
1526         if( const Cairo::Surface& surface = _dockWidgetButtonCache.value(key) )
1527         { return surface; }
1528 
1529         // cached not found, create new
1530         Cairo::Surface surface( createSurface( size, size ) );
1531 
1532         Cairo::Context context( surface );
1533         cairo_set_source( context, ColorUtils::Rgba::transparent( base ) );
1534         cairo_paint( context );
1535 
1536         const ColorUtils::Rgba light( ColorUtils::lightColor( base ) );
1537         const ColorUtils::Rgba dark( ColorUtils::darkColor( base ) );
1538 
1539         const double u( size/18.0 );
1540         cairo_translate( context, 0.5*u, ( 0.5-0.668 )*u );
1541 
1542         {
1543             // outline circle
1544             double penWidth = 1.2;
1545             Cairo::Pattern lg( cairo_pattern_create_linear( 0, u*( 1.665-penWidth ), 0, u*( 12.33+1.665-penWidth ) ) );
1546 
1547             cairo_pattern_add_color_stop( lg, 0, dark );
1548             cairo_pattern_add_color_stop( lg, 1, light );
1549             cairo_set_source( context, lg );
1550             cairo_set_line_width( context, penWidth*u );
1551             cairo_ellipse( context, u*0.5*( 17-12.33+penWidth ), u*( 1.665+penWidth ), u*( 12.33-penWidth ), u*( 12.33-penWidth ) );
1552             cairo_stroke( context );
1553         }
1554 
1555         return _dockWidgetButtonCache.insert(key, surface);
1556     }
1557 
1558 
1559 }