1 /* 2 * Copyright (C) 2010 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "PlatformThemeChromiumGtk.h" 33 34 namespace WebCore { 35 36 unsigned PlatformThemeChromiumGtk::s_thumbInactiveColor = 0xeaeaea; 37 unsigned PlatformThemeChromiumGtk::s_thumbActiveColor = 0xf4f4f4; 38 unsigned PlatformThemeChromiumGtk::s_trackColor = 0xd3d3d3; 39 40 void PlatformThemeChromiumGtk::setScrollbarColors( 41 SkColor inactiveColor, SkColor activeColor, SkColor trackColor) 42 { 43 s_thumbInactiveColor = inactiveColor; 44 s_thumbActiveColor = activeColor; 45 s_trackColor = trackColor; 46 } 47 48 static SkScalar clamp(SkScalar value, SkScalar min, SkScalar max) 49 { 50 return std::min(std::max(value, min), max); 51 } 52 53 SkColor PlatformThemeChromiumGtk::saturateAndBrighten(const SkScalar hsv[3], SkScalar saturateAmount, SkScalar brightenAmount) 54 { 55 SkScalar color[3]; 56 color[0] = hsv[0]; 57 color[1] = clamp(hsv[1] + saturateAmount, 0.0, 1.0); 58 color[2] = clamp(hsv[2] + brightenAmount, 0.0, 1.0); 59 return SkHSVToColor(color); 60 } 61 62 SkColor PlatformThemeChromiumGtk::outlineColor(const SkScalar hsv1[3], const SkScalar hsv2[3]) 63 { 64 // GTK Theme engines have way too much control over the layout of 65 // the scrollbar. We might be able to more closely approximate its 66 // look-and-feel, if we sent whole images instead of just colors 67 // from the browser to the renderer. But even then, some themes 68 // would just break. 69 // 70 // So, instead, we don't even try to 100% replicate the look of 71 // the native scrollbar. We render our own version, but we make 72 // sure to pick colors that blend in nicely with the system GTK 73 // theme. In most cases, we can just sample a couple of pixels 74 // from the system scrollbar and use those colors to draw our 75 // scrollbar. 76 // 77 // This works fine for the track color and the overall thumb 78 // color. But it fails spectacularly for the outline color used 79 // around the thumb piece. Not all themes have a clearly defined 80 // outline. For some of them it is partially transparent, and for 81 // others the thickness is very unpredictable. 82 // 83 // So, instead of trying to approximate the system theme, we 84 // instead try to compute a reasonable looking choice based on the 85 // known color of the track and the thumb piece. This is difficult 86 // when trying to deal both with high- and low-contrast themes, 87 // and both with positive and inverted themes. 88 // 89 // The following code has been tested to look OK with all of the 90 // default GTK themes. 91 SkScalar minDiff = clamp((hsv1[1] + hsv2[1]) * 1.2, 0.28, 0.5); 92 SkScalar diff = clamp(fabs(hsv1[2] - hsv2[2]) / 2, minDiff, 0.5); 93 94 if (hsv1[2] + hsv2[2] > 1.0) 95 diff = -diff; 96 97 return saturateAndBrighten(hsv2, -0.2, diff); 98 } 99 100 void PlatformThemeChromiumGtk::paintArrowButton(GraphicsContext* gc, const IntRect& rect, ArrowDirection direction, ControlStates states) 101 { 102 SkCanvas* const canvas = gc->platformContext()->canvas(); 103 int widthMiddle, lengthMiddle; 104 SkPaint paint; 105 if (direction == North || direction == South) { 106 widthMiddle = rect.width() / 2 + 1; 107 lengthMiddle = rect.height() / 2 + 1; 108 } else { 109 lengthMiddle = rect.width() / 2 + 1; 110 widthMiddle = rect.height() / 2 + 1; 111 } 112 113 // Calculate button color. 114 SkScalar trackHSV[3]; 115 SkColorToHSV(trackColor(), trackHSV); 116 SkColor buttonColor = saturateAndBrighten(trackHSV, 0, 0.2); 117 SkColor backgroundColor = buttonColor; 118 if (states & PressedState) { 119 SkScalar buttonHSV[3]; 120 SkColorToHSV(buttonColor, buttonHSV); 121 buttonColor = saturateAndBrighten(buttonHSV, 0, -0.1); 122 } else if (states & HoverState) { 123 SkScalar buttonHSV[3]; 124 SkColorToHSV(buttonColor, buttonHSV); 125 buttonColor = saturateAndBrighten(buttonHSV, 0, 0.05); 126 } 127 128 SkIRect skrect; 129 skrect.set(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height()); 130 // Paint the background (the area visible behind the rounded corners). 131 paint.setColor(backgroundColor); 132 canvas->drawIRect(skrect, paint); 133 134 // Paint the button's outline and fill the middle 135 SkPath outline; 136 switch (direction) { 137 case North: 138 outline.moveTo(rect.x() + 0.5, rect.y() + rect.height() + 0.5); 139 outline.rLineTo(0, -(rect.height() - 2)); 140 outline.rLineTo(2, -2); 141 outline.rLineTo(rect.width() - 5, 0); 142 outline.rLineTo(2, 2); 143 outline.rLineTo(0, rect.height() - 2); 144 break; 145 case South: 146 outline.moveTo(rect.x() + 0.5, rect.y() - 0.5); 147 outline.rLineTo(0, rect.height() - 2); 148 outline.rLineTo(2, 2); 149 outline.rLineTo(rect.width() - 5, 0); 150 outline.rLineTo(2, -2); 151 outline.rLineTo(0, -(rect.height() - 2)); 152 break; 153 case East: 154 outline.moveTo(rect.x() - 0.5, rect.y() + 0.5); 155 outline.rLineTo(rect.width() - 2, 0); 156 outline.rLineTo(2, 2); 157 outline.rLineTo(0, rect.height() - 5); 158 outline.rLineTo(-2, 2); 159 outline.rLineTo(-(rect.width() - 2), 0); 160 break; 161 case West: 162 outline.moveTo(rect.x() + rect.width() + 0.5, rect.y() + 0.5); 163 outline.rLineTo(-(rect.width() - 2), 0); 164 outline.rLineTo(-2, 2); 165 outline.rLineTo(0, rect.height() - 5); 166 outline.rLineTo(2, 2); 167 outline.rLineTo(rect.width() - 2, 0); 168 break; 169 } 170 outline.close(); 171 172 paint.setStyle(SkPaint::kFill_Style); 173 paint.setColor(buttonColor); 174 canvas->drawPath(outline, paint); 175 176 paint.setAntiAlias(true); 177 paint.setStyle(SkPaint::kStroke_Style); 178 SkScalar thumbHSV[3]; 179 SkColorToHSV(thumbInactiveColor(), thumbHSV); 180 paint.setColor(outlineColor(trackHSV, thumbHSV)); 181 canvas->drawPath(outline, paint); 182 183 // If the button is disabled or read-only, the arrow is drawn with the outline color. 184 if (states & EnabledState && !(states & ReadOnlyState)) 185 paint.setColor(SK_ColorBLACK); 186 187 paint.setAntiAlias(false); 188 paint.setStyle(SkPaint::kFill_Style); 189 190 SkPath path; 191 // The constants in this block of code are hand-tailored to produce good 192 // looking arrows without anti-aliasing. 193 switch (direction) { 194 case North: 195 path.moveTo(rect.x() + widthMiddle - 4, rect.y() + lengthMiddle + 2); 196 path.rLineTo(7, 0); 197 path.rLineTo(-4, -4); 198 break; 199 case South: 200 path.moveTo(rect.x() + widthMiddle - 4, rect.y() + lengthMiddle - 3); 201 path.rLineTo(7, 0); 202 path.rLineTo(-4, 4); 203 break; 204 case East: 205 path.moveTo(rect.x() + lengthMiddle - 3, rect.y() + widthMiddle - 4); 206 path.rLineTo(0, 7); 207 path.rLineTo(4, -4); 208 break; 209 case West: 210 path.moveTo(rect.x() + lengthMiddle + 1, rect.y() + widthMiddle - 5); 211 path.rLineTo(0, 9); 212 path.rLineTo(-4, -4); 213 break; 214 } 215 path.close(); 216 217 canvas->drawPath(path, paint); 218 } 219 220 } // namespace WebCore 221 222