1/* 2 * Copyright (C) 2008 Apple 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 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "ScrollbarThemeGtk.h" 28 29#include "PlatformMouseEvent.h" 30#include "RenderThemeGtk.h" 31#include "ScrollView.h" 32#include "Scrollbar.h" 33 34namespace WebCore { 35 36static HashSet<ScrollbarThemeClient*>* gScrollbars; 37 38ScrollbarTheme* ScrollbarTheme::nativeTheme() 39{ 40 static ScrollbarThemeGtk theme; 41 return &theme; 42} 43 44ScrollbarThemeGtk::~ScrollbarThemeGtk() 45{ 46} 47 48void ScrollbarThemeGtk::registerScrollbar(ScrollbarThemeClient* scrollbar) 49{ 50 if (!gScrollbars) 51 gScrollbars = new HashSet<ScrollbarThemeClient*>; 52 gScrollbars->add(scrollbar); 53} 54 55void ScrollbarThemeGtk::unregisterScrollbar(ScrollbarThemeClient* scrollbar) 56{ 57 gScrollbars->remove(scrollbar); 58 if (gScrollbars->isEmpty()) { 59 delete gScrollbars; 60 gScrollbars = 0; 61 } 62} 63 64void ScrollbarThemeGtk::updateScrollbarsFrameThickness() 65{ 66 if (!gScrollbars) 67 return; 68 69 // Update the thickness of every interior frame scrollbar widget. The 70 // platform-independent scrollbar them code isn't yet smart enough to get 71 // this information when it paints. 72 HashSet<ScrollbarThemeClient*>::iterator end = gScrollbars->end(); 73 for (HashSet<ScrollbarThemeClient*>::iterator it = gScrollbars->begin(); it != end; ++it) { 74 ScrollbarThemeClient* scrollbar = (*it); 75 76 // Top-level scrollbar i.e. scrollbars who have a parent ScrollView 77 // with no parent are native, and thus do not need to be resized. 78 if (!scrollbar->parent() || !scrollbar->parent()->parent()) 79 return; 80 81 int thickness = scrollbarThickness(scrollbar->controlSize()); 82 if (scrollbar->orientation() == HorizontalScrollbar) 83 scrollbar->setFrameRect(IntRect(0, scrollbar->parent()->height() - thickness, scrollbar->width(), thickness)); 84 else 85 scrollbar->setFrameRect(IntRect(scrollbar->parent()->width() - thickness, 0, thickness, scrollbar->height())); 86 } 87} 88 89bool ScrollbarThemeGtk::hasThumb(ScrollbarThemeClient* scrollbar) 90{ 91 // This method is just called as a paint-time optimization to see if 92 // painting the thumb can be skipped. We don't have to be exact here. 93 return thumbLength(scrollbar) > 0; 94} 95 96IntRect ScrollbarThemeGtk::backButtonRect(ScrollbarThemeClient* scrollbar, ScrollbarPart part, bool) 97{ 98 if (part == BackButtonEndPart && !m_hasBackButtonEndPart) 99 return IntRect(); 100 if (part == BackButtonStartPart && !m_hasBackButtonStartPart) 101 return IntRect(); 102 103 int x = scrollbar->x() + m_troughBorderWidth; 104 int y = scrollbar->y() + m_troughBorderWidth; 105 IntSize size = buttonSize(scrollbar); 106 if (part == BackButtonStartPart) 107 return IntRect(x, y, size.width(), size.height()); 108 109 // BackButtonEndPart (alternate button) 110 if (scrollbar->orientation() == HorizontalScrollbar) 111 return IntRect(scrollbar->x() + scrollbar->width() - m_troughBorderWidth - (2 * size.width()), y, size.width(), size.height()); 112 113 // VerticalScrollbar alternate button 114 return IntRect(x, scrollbar->y() + scrollbar->height() - m_troughBorderWidth - (2 * size.height()), size.width(), size.height()); 115} 116 117IntRect ScrollbarThemeGtk::forwardButtonRect(ScrollbarThemeClient* scrollbar, ScrollbarPart part, bool) 118{ 119 if (part == ForwardButtonStartPart && !m_hasForwardButtonStartPart) 120 return IntRect(); 121 if (part == ForwardButtonEndPart && !m_hasForwardButtonEndPart) 122 return IntRect(); 123 124 IntSize size = buttonSize(scrollbar); 125 if (scrollbar->orientation() == HorizontalScrollbar) { 126 int y = scrollbar->y() + m_troughBorderWidth; 127 if (part == ForwardButtonEndPart) 128 return IntRect(scrollbar->x() + scrollbar->width() - size.width() - m_troughBorderWidth, y, size.width(), size.height()); 129 130 // ForwardButtonStartPart (alternate button) 131 return IntRect(scrollbar->x() + m_troughBorderWidth + size.width(), y, size.width(), size.height()); 132 } 133 134 // VerticalScrollbar 135 int x = scrollbar->x() + m_troughBorderWidth; 136 if (part == ForwardButtonEndPart) 137 return IntRect(x, scrollbar->y() + scrollbar->height() - size.height() - m_troughBorderWidth, size.width(), size.height()); 138 139 // ForwardButtonStartPart (alternate button) 140 return IntRect(x, scrollbar->y() + m_troughBorderWidth + size.height(), size.width(), size.height()); 141} 142 143IntRect ScrollbarThemeGtk::trackRect(ScrollbarThemeClient* scrollbar, bool) 144{ 145 // The padding along the thumb movement axis includes the trough border 146 // plus the size of stepper spacing (the space between the stepper and 147 // the place where the thumb stops). There is often no stepper spacing. 148 int movementAxisPadding = m_troughBorderWidth + m_stepperSpacing; 149 150 // The fatness of the scrollbar on the non-movement axis. 151 int thickness = scrollbarThickness(scrollbar->controlSize()); 152 153 int startButtonsOffset = 0; 154 int buttonsWidth = 0; 155 if (m_hasForwardButtonStartPart) { 156 startButtonsOffset += m_stepperSize; 157 buttonsWidth += m_stepperSize; 158 } 159 if (m_hasBackButtonStartPart) { 160 startButtonsOffset += m_stepperSize; 161 buttonsWidth += m_stepperSize; 162 } 163 if (m_hasBackButtonEndPart) 164 buttonsWidth += m_stepperSize; 165 if (m_hasForwardButtonEndPart) 166 buttonsWidth += m_stepperSize; 167 168 if (scrollbar->orientation() == HorizontalScrollbar) { 169 // Once the scrollbar becomes smaller than the natural size of the 170 // two buttons, the track disappears. 171 if (scrollbar->width() < 2 * thickness) 172 return IntRect(); 173 return IntRect(scrollbar->x() + movementAxisPadding + startButtonsOffset, scrollbar->y(), 174 scrollbar->width() - (2 * movementAxisPadding) - buttonsWidth, thickness); 175 } 176 177 if (scrollbar->height() < 2 * thickness) 178 return IntRect(); 179 return IntRect(scrollbar->x(), scrollbar->y() + movementAxisPadding + startButtonsOffset, 180 thickness, scrollbar->height() - (2 * movementAxisPadding) - buttonsWidth); 181} 182 183IntRect ScrollbarThemeGtk::thumbRect(ScrollbarThemeClient* scrollbar, const IntRect& unconstrainedTrackRect) 184{ 185 IntRect trackRect = constrainTrackRectToTrackPieces(scrollbar, unconstrainedTrackRect); 186 int thumbPos = thumbPosition(scrollbar); 187 if (scrollbar->orientation() == HorizontalScrollbar) 188 return IntRect(trackRect.x() + thumbPos, trackRect.y() + (trackRect.height() - m_thumbFatness) / 2, thumbLength(scrollbar), m_thumbFatness); 189 190 // VerticalScrollbar 191 return IntRect(trackRect.x() + (trackRect.width() - m_thumbFatness) / 2, trackRect.y() + thumbPos, m_thumbFatness, thumbLength(scrollbar)); 192} 193 194bool ScrollbarThemeGtk::paint(ScrollbarThemeClient* scrollbar, GraphicsContext* graphicsContext, const IntRect& damageRect) 195{ 196 if (graphicsContext->paintingDisabled()) 197 return false; 198 199 // Create the ScrollbarControlPartMask based on the damageRect 200 ScrollbarControlPartMask scrollMask = NoPart; 201 202 IntRect backButtonStartPaintRect; 203 IntRect backButtonEndPaintRect; 204 IntRect forwardButtonStartPaintRect; 205 IntRect forwardButtonEndPaintRect; 206 if (hasButtons(scrollbar)) { 207 backButtonStartPaintRect = backButtonRect(scrollbar, BackButtonStartPart, true); 208 if (damageRect.intersects(backButtonStartPaintRect)) 209 scrollMask |= BackButtonStartPart; 210 backButtonEndPaintRect = backButtonRect(scrollbar, BackButtonEndPart, true); 211 if (damageRect.intersects(backButtonEndPaintRect)) 212 scrollMask |= BackButtonEndPart; 213 forwardButtonStartPaintRect = forwardButtonRect(scrollbar, ForwardButtonStartPart, true); 214 if (damageRect.intersects(forwardButtonStartPaintRect)) 215 scrollMask |= ForwardButtonStartPart; 216 forwardButtonEndPaintRect = forwardButtonRect(scrollbar, ForwardButtonEndPart, true); 217 if (damageRect.intersects(forwardButtonEndPaintRect)) 218 scrollMask |= ForwardButtonEndPart; 219 } 220 221 IntRect trackPaintRect = trackRect(scrollbar, true); 222 if (damageRect.intersects(trackPaintRect)) 223 scrollMask |= TrackBGPart; 224 225 if (m_troughUnderSteppers && (scrollMask & BackButtonStartPart 226 || scrollMask & BackButtonEndPart 227 || scrollMask & ForwardButtonStartPart 228 || scrollMask & ForwardButtonEndPart)) 229 scrollMask |= TrackBGPart; 230 231 bool thumbPresent = hasThumb(scrollbar); 232 IntRect currentThumbRect; 233 if (thumbPresent) { 234 IntRect track = trackRect(scrollbar, false); 235 currentThumbRect = thumbRect(scrollbar, track); 236 if (damageRect.intersects(currentThumbRect)) 237 scrollMask |= ThumbPart; 238 } 239 240 ScrollbarControlPartMask allButtons = BackButtonStartPart | BackButtonEndPart 241 | ForwardButtonStartPart | ForwardButtonEndPart; 242 if (scrollMask & TrackBGPart || scrollMask & ThumbPart || scrollMask & allButtons) 243 paintScrollbarBackground(graphicsContext, scrollbar); 244 paintTrackBackground(graphicsContext, scrollbar, trackPaintRect); 245 246 // Paint the back and forward buttons. 247 if (scrollMask & BackButtonStartPart) 248 paintButton(graphicsContext, scrollbar, backButtonStartPaintRect, BackButtonStartPart); 249 if (scrollMask & BackButtonEndPart) 250 paintButton(graphicsContext, scrollbar, backButtonEndPaintRect, BackButtonEndPart); 251 if (scrollMask & ForwardButtonStartPart) 252 paintButton(graphicsContext, scrollbar, forwardButtonStartPaintRect, ForwardButtonStartPart); 253 if (scrollMask & ForwardButtonEndPart) 254 paintButton(graphicsContext, scrollbar, forwardButtonEndPaintRect, ForwardButtonEndPart); 255 256 // Paint the thumb. 257 if (scrollMask & ThumbPart) 258 paintThumb(graphicsContext, scrollbar, currentThumbRect); 259 260 return true; 261} 262 263bool ScrollbarThemeGtk::shouldCenterOnThumb(ScrollbarThemeClient*, const PlatformMouseEvent& event) 264{ 265 return (event.shiftKey() && event.button() == LeftButton) || (event.button() == MiddleButton); 266} 267 268int ScrollbarThemeGtk::scrollbarThickness(ScrollbarControlSize) 269{ 270 return m_thumbFatness + (m_troughBorderWidth * 2); 271} 272 273IntSize ScrollbarThemeGtk::buttonSize(ScrollbarThemeClient* scrollbar) 274{ 275 if (scrollbar->orientation() == VerticalScrollbar) 276 return IntSize(m_thumbFatness, m_stepperSize); 277 278 // HorizontalScrollbar 279 return IntSize(m_stepperSize, m_thumbFatness); 280} 281 282int ScrollbarThemeGtk::minimumThumbLength(ScrollbarThemeClient* scrollbar) 283{ 284 return m_minThumbLength; 285} 286 287} 288 289