Home | History | Annotate | Download | only in gtk
      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 
     34 namespace WebCore {
     35 
     36 static HashSet<Scrollbar*>* gScrollbars;
     37 
     38 ScrollbarTheme* ScrollbarTheme::nativeTheme()
     39 {
     40     static ScrollbarThemeGtk theme;
     41     return &theme;
     42 }
     43 
     44 ScrollbarThemeGtk::~ScrollbarThemeGtk()
     45 {
     46 }
     47 
     48 void ScrollbarThemeGtk::registerScrollbar(Scrollbar* scrollbar)
     49 {
     50     if (!gScrollbars)
     51         gScrollbars = new HashSet<Scrollbar*>;
     52     gScrollbars->add(scrollbar);
     53 }
     54 
     55 void ScrollbarThemeGtk::unregisterScrollbar(Scrollbar* scrollbar)
     56 {
     57     gScrollbars->remove(scrollbar);
     58     if (gScrollbars->isEmpty()) {
     59         delete gScrollbars;
     60         gScrollbars = 0;
     61     }
     62 }
     63 
     64 void 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<Scrollbar*>::iterator end = gScrollbars->end();
     73     for (HashSet<Scrollbar*>::iterator it = gScrollbars->begin(); it != end; ++it) {
     74         Scrollbar* 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 
     89 bool ScrollbarThemeGtk::hasThumb(Scrollbar* 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 
     96 IntRect ScrollbarThemeGtk::backButtonRect(Scrollbar* scrollbar, ScrollbarPart part, bool)
     97 {
     98     if (part == BackButtonEndPart && !m_hasBackButtonEndPart)
     99         return IntRect();
    100 
    101     int x = scrollbar->x() + m_troughBorderWidth;
    102     int y = scrollbar->y() + m_troughBorderWidth;
    103     IntSize size = buttonSize(scrollbar);
    104     if (part == BackButtonStartPart)
    105         return IntRect(x, y, size.width(), size.height());
    106 
    107     // BackButtonEndPart (alternate button)
    108     if (scrollbar->orientation() == HorizontalScrollbar)
    109         return IntRect(scrollbar->x() + scrollbar->width() - m_troughBorderWidth - (2 * size.width()), y, size.width(), size.height());
    110 
    111     // VerticalScrollbar alternate button
    112     return IntRect(x, scrollbar->y() + scrollbar->height() - m_troughBorderWidth - (2 * size.height()), size.width(), size.height());
    113 }
    114 
    115 IntRect ScrollbarThemeGtk::forwardButtonRect(Scrollbar* scrollbar, ScrollbarPart part, bool)
    116 {
    117     if (part == ForwardButtonStartPart && !m_hasForwardButtonStartPart)
    118         return IntRect();
    119 
    120     IntSize size = buttonSize(scrollbar);
    121     if (scrollbar->orientation() == HorizontalScrollbar) {
    122         int y = scrollbar->y() + m_troughBorderWidth;
    123         if (part == ForwardButtonEndPart)
    124             return IntRect(scrollbar->x() + scrollbar->width() - size.width() - m_troughBorderWidth, y, size.width(), size.height());
    125 
    126         // ForwardButtonStartPart (alternate button)
    127         return IntRect(scrollbar->x() + m_troughBorderWidth + size.width(), y, size.width(), size.height());
    128     }
    129 
    130     // VerticalScrollbar
    131     int x = scrollbar->x() + m_troughBorderWidth;
    132     if (part == ForwardButtonEndPart)
    133         return IntRect(x, scrollbar->y() + scrollbar->height() - size.height() - m_troughBorderWidth, size.width(), size.height());
    134 
    135     // ForwardButtonStartPart (alternate button)
    136     return IntRect(x, scrollbar->y() + m_troughBorderWidth + size.height(), size.width(), size.height());
    137 }
    138 
    139 IntRect ScrollbarThemeGtk::trackRect(Scrollbar* scrollbar, bool)
    140 {
    141     // The padding along the thumb movement axis (from outside to in)
    142     // is the size of trough border plus the size of the stepper (button)
    143     // plus the size of stepper spacing (the space between the stepper and
    144     // the place where the thumb stops). There is often no stepper spacing.
    145     int movementAxisPadding = m_troughBorderWidth + m_stepperSize + m_stepperSpacing;
    146 
    147     // The fatness of the scrollbar on the non-movement axis.
    148     int thickness = scrollbarThickness(scrollbar->controlSize());
    149 
    150     int alternateButtonOffset = 0;
    151     int alternateButtonWidth = 0;
    152     if (m_hasForwardButtonStartPart) {
    153         alternateButtonOffset += m_stepperSize;
    154         alternateButtonWidth += m_stepperSize;
    155     }
    156     if (m_hasBackButtonEndPart)
    157         alternateButtonWidth += m_stepperSize;
    158 
    159     if (scrollbar->orientation() == HorizontalScrollbar) {
    160         // Once the scrollbar becomes smaller than the natural size of the
    161         // two buttons, the track disappears.
    162         if (scrollbar->width() < 2 * thickness)
    163             return IntRect();
    164         return IntRect(scrollbar->x() + movementAxisPadding + alternateButtonOffset, scrollbar->y(),
    165                        scrollbar->width() - (2 * movementAxisPadding) - alternateButtonWidth, thickness);
    166     }
    167 
    168     if (scrollbar->height() < 2 * thickness)
    169         return IntRect();
    170     return IntRect(scrollbar->x(), scrollbar->y() + movementAxisPadding + alternateButtonOffset,
    171                    thickness, scrollbar->height() - (2 * movementAxisPadding) - alternateButtonWidth);
    172 }
    173 
    174 IntRect ScrollbarThemeGtk::thumbRect(Scrollbar* scrollbar, const IntRect& unconstrainedTrackRect)
    175 {
    176     IntRect trackRect = constrainTrackRectToTrackPieces(scrollbar, unconstrainedTrackRect);
    177     int thumbPos = thumbPosition(scrollbar);
    178     if (scrollbar->orientation() == HorizontalScrollbar)
    179         return IntRect(trackRect.x() + thumbPos, trackRect.y() + (trackRect.height() - m_thumbFatness) / 2, thumbLength(scrollbar), m_thumbFatness);
    180 
    181     // VerticalScrollbar
    182     return IntRect(trackRect.x() + (trackRect.width() - m_thumbFatness) / 2, trackRect.y() + thumbPos, m_thumbFatness, thumbLength(scrollbar));
    183 }
    184 
    185 bool ScrollbarThemeGtk::paint(Scrollbar* scrollbar, GraphicsContext* graphicsContext, const IntRect& damageRect)
    186 {
    187     if (graphicsContext->paintingDisabled())
    188         return false;
    189 
    190     // Create the ScrollbarControlPartMask based on the damageRect
    191     ScrollbarControlPartMask scrollMask = NoPart;
    192 
    193     IntRect backButtonStartPaintRect;
    194     IntRect backButtonEndPaintRect;
    195     IntRect forwardButtonStartPaintRect;
    196     IntRect forwardButtonEndPaintRect;
    197     if (hasButtons(scrollbar)) {
    198         backButtonStartPaintRect = backButtonRect(scrollbar, BackButtonStartPart, true);
    199         if (damageRect.intersects(backButtonStartPaintRect))
    200             scrollMask |= BackButtonStartPart;
    201         backButtonEndPaintRect = backButtonRect(scrollbar, BackButtonEndPart, true);
    202         if (damageRect.intersects(backButtonEndPaintRect))
    203             scrollMask |= BackButtonEndPart;
    204         forwardButtonStartPaintRect = forwardButtonRect(scrollbar, ForwardButtonStartPart, true);
    205         if (damageRect.intersects(forwardButtonStartPaintRect))
    206             scrollMask |= ForwardButtonStartPart;
    207         forwardButtonEndPaintRect = forwardButtonRect(scrollbar, ForwardButtonEndPart, true);
    208         if (damageRect.intersects(forwardButtonEndPaintRect))
    209             scrollMask |= ForwardButtonEndPart;
    210     }
    211 
    212     IntRect trackPaintRect = trackRect(scrollbar, true);
    213     if (damageRect.intersects(trackPaintRect))
    214         scrollMask |= TrackBGPart;
    215 
    216     if (m_troughUnderSteppers && (scrollMask & BackButtonStartPart
    217             || scrollMask & BackButtonEndPart
    218             || scrollMask & ForwardButtonStartPart
    219             || scrollMask & ForwardButtonEndPart))
    220         scrollMask |= TrackBGPart;
    221 
    222     bool thumbPresent = hasThumb(scrollbar);
    223     IntRect currentThumbRect;
    224     if (thumbPresent) {
    225         IntRect track = trackRect(scrollbar, false);
    226         currentThumbRect = thumbRect(scrollbar, track);
    227         if (damageRect.intersects(currentThumbRect))
    228             scrollMask |= ThumbPart;
    229     }
    230 
    231     ScrollbarControlPartMask allButtons = BackButtonStartPart | BackButtonEndPart
    232                                          | ForwardButtonStartPart | ForwardButtonEndPart;
    233     if (scrollMask & TrackBGPart || scrollMask & ThumbPart || scrollMask & allButtons)
    234         paintScrollbarBackground(graphicsContext, scrollbar);
    235         paintTrackBackground(graphicsContext, scrollbar, trackPaintRect);
    236 
    237     // Paint the back and forward buttons.
    238     if (scrollMask & BackButtonStartPart)
    239         paintButton(graphicsContext, scrollbar, backButtonStartPaintRect, BackButtonStartPart);
    240     if (scrollMask & BackButtonEndPart)
    241         paintButton(graphicsContext, scrollbar, backButtonEndPaintRect, BackButtonEndPart);
    242     if (scrollMask & ForwardButtonStartPart)
    243         paintButton(graphicsContext, scrollbar, forwardButtonStartPaintRect, ForwardButtonStartPart);
    244     if (scrollMask & ForwardButtonEndPart)
    245         paintButton(graphicsContext, scrollbar, forwardButtonEndPaintRect, ForwardButtonEndPart);
    246 
    247     // Paint the thumb.
    248     if (scrollMask & ThumbPart)
    249         paintThumb(graphicsContext, scrollbar, currentThumbRect);
    250 
    251     return true;
    252 }
    253 
    254 void ScrollbarThemeGtk::paintScrollCorner(ScrollView* view, GraphicsContext* context, const IntRect& cornerRect)
    255 {
    256     // ScrollbarThemeComposite::paintScrollCorner incorrectly assumes that the
    257     // ScrollView is a FrameView (see FramelessScrollView), so we cannot let
    258     // that code run.  For FrameView's this is correct since we don't do custom
    259     // scrollbar corner rendering, which ScrollbarThemeComposite supports.
    260     ScrollbarTheme::paintScrollCorner(view, context, cornerRect);
    261 }
    262 
    263 bool ScrollbarThemeGtk::shouldCenterOnThumb(Scrollbar*, const PlatformMouseEvent& event)
    264 {
    265     return (event.shiftKey() && event.button() == LeftButton) || (event.button() == MiddleButton);
    266 }
    267 
    268 int ScrollbarThemeGtk::scrollbarThickness(ScrollbarControlSize)
    269 {
    270     return m_thumbFatness + (m_troughBorderWidth * 2);
    271 }
    272 
    273 IntSize ScrollbarThemeGtk::buttonSize(Scrollbar* 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 
    282 int ScrollbarThemeGtk::minimumThumbLength(Scrollbar* scrollbar)
    283 {
    284     return m_minThumbLength;
    285 }
    286 
    287 }
    288 
    289