Home | History | Annotate | Download | only in platform
      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 "ScrollbarThemeComposite.h"
     28 
     29 #include "Chrome.h"
     30 #include "ChromeClient.h"
     31 #include "Frame.h"
     32 #include "FrameView.h"
     33 #include "GraphicsContext.h"
     34 #include "Page.h"
     35 #include "PlatformMouseEvent.h"
     36 #include "Scrollbar.h"
     37 #include "ScrollbarClient.h"
     38 #include "Settings.h"
     39 
     40 namespace WebCore {
     41 
     42 #if PLATFORM(WIN)
     43 static Page* pageForScrollView(ScrollView* view)
     44 {
     45     if (!view)
     46         return 0;
     47     if (!view->isFrameView())
     48         return 0;
     49     FrameView* frameView = static_cast<FrameView*>(view);
     50     if (!frameView->frame())
     51         return 0;
     52     return frameView->frame()->page();
     53 }
     54 #endif
     55 
     56 bool ScrollbarThemeComposite::paint(Scrollbar* scrollbar, GraphicsContext* graphicsContext, const IntRect& damageRect)
     57 {
     58     // Create the ScrollbarControlPartMask based on the damageRect
     59     ScrollbarControlPartMask scrollMask = NoPart;
     60 
     61     IntRect backButtonStartPaintRect;
     62     IntRect backButtonEndPaintRect;
     63     IntRect forwardButtonStartPaintRect;
     64     IntRect forwardButtonEndPaintRect;
     65     if (hasButtons(scrollbar)) {
     66         backButtonStartPaintRect = backButtonRect(scrollbar, BackButtonStartPart, true);
     67         if (damageRect.intersects(backButtonStartPaintRect))
     68             scrollMask |= BackButtonStartPart;
     69         backButtonEndPaintRect = backButtonRect(scrollbar, BackButtonEndPart, true);
     70         if (damageRect.intersects(backButtonEndPaintRect))
     71             scrollMask |= BackButtonEndPart;
     72         forwardButtonStartPaintRect = forwardButtonRect(scrollbar, ForwardButtonStartPart, true);
     73         if (damageRect.intersects(forwardButtonStartPaintRect))
     74             scrollMask |= ForwardButtonStartPart;
     75         forwardButtonEndPaintRect = forwardButtonRect(scrollbar, ForwardButtonEndPart, true);
     76         if (damageRect.intersects(forwardButtonEndPaintRect))
     77             scrollMask |= ForwardButtonEndPart;
     78     }
     79 
     80     IntRect startTrackRect;
     81     IntRect thumbRect;
     82     IntRect endTrackRect;
     83     IntRect trackPaintRect = trackRect(scrollbar, true);
     84     if (damageRect.intersects(trackPaintRect))
     85         scrollMask |= TrackBGPart;
     86     bool thumbPresent = hasThumb(scrollbar);
     87     if (thumbPresent) {
     88         IntRect track = trackRect(scrollbar);
     89         splitTrack(scrollbar, track, startTrackRect, thumbRect, endTrackRect);
     90         if (damageRect.intersects(thumbRect))
     91             scrollMask |= ThumbPart;
     92         if (damageRect.intersects(startTrackRect))
     93             scrollMask |= BackTrackPart;
     94         if (damageRect.intersects(endTrackRect))
     95             scrollMask |= ForwardTrackPart;
     96     }
     97 
     98 #if PLATFORM(WIN)
     99     // FIXME: This API makes the assumption that the custom scrollbar's metrics will match
    100     // the theme's metrics.  This is not a valid assumption.  The ability for a client to paint
    101     // custom scrollbars should be removed once scrollbars can be styled via CSS.
    102     if (Page* page = pageForScrollView(scrollbar->parent())) {
    103         if (page->settings()->shouldPaintCustomScrollbars()) {
    104             float proportion = static_cast<float>(scrollbar->visibleSize()) / scrollbar->totalSize();
    105             float value = scrollbar->currentPos() / static_cast<float>(scrollbar->maximum());
    106             ScrollbarControlState s = 0;
    107             if (scrollbar->client()->isActive())
    108                 s |= ActiveScrollbarState;
    109             if (scrollbar->enabled())
    110                 s |= EnabledScrollbarState;
    111             if (scrollbar->pressedPart() != NoPart)
    112                 s |= PressedScrollbarState;
    113             if (page->chrome()->client()->paintCustomScrollbar(graphicsContext,
    114                                                                scrollbar->frameRect(),
    115                                                                scrollbar->controlSize(),
    116                                                                s,
    117                                                                scrollbar->pressedPart(),
    118                                                                scrollbar->orientation() == VerticalScrollbar,
    119                                                                value,
    120                                                                proportion,
    121                                                                scrollMask))
    122                 return true;
    123         }
    124     }
    125 #endif
    126 
    127     // Paint the scrollbar background (only used by custom CSS scrollbars).
    128     paintScrollbarBackground(graphicsContext, scrollbar);
    129 
    130     // Paint the back and forward buttons.
    131     if (scrollMask & BackButtonStartPart)
    132         paintButton(graphicsContext, scrollbar, backButtonStartPaintRect, BackButtonStartPart);
    133     if (scrollMask & BackButtonEndPart)
    134         paintButton(graphicsContext, scrollbar, backButtonEndPaintRect, BackButtonEndPart);
    135     if (scrollMask & ForwardButtonStartPart)
    136         paintButton(graphicsContext, scrollbar, forwardButtonStartPaintRect, ForwardButtonStartPart);
    137     if (scrollMask & ForwardButtonEndPart)
    138         paintButton(graphicsContext, scrollbar, forwardButtonEndPaintRect, ForwardButtonEndPart);
    139 
    140     if (scrollMask & TrackBGPart)
    141         paintTrackBackground(graphicsContext, scrollbar, trackPaintRect);
    142 
    143     if ((scrollMask & ForwardTrackPart) || (scrollMask & BackTrackPart)) {
    144         // Paint the track pieces above and below the thumb.
    145         if (scrollMask & BackTrackPart)
    146             paintTrackPiece(graphicsContext, scrollbar, startTrackRect, BackTrackPart);
    147         if (scrollMask & ForwardTrackPart)
    148             paintTrackPiece(graphicsContext, scrollbar, endTrackRect, ForwardTrackPart);
    149 
    150         paintTickmarks(graphicsContext, scrollbar, trackPaintRect);
    151     }
    152 
    153     // Paint the thumb.
    154     if (scrollMask & ThumbPart)
    155         paintThumb(graphicsContext, scrollbar, thumbRect);
    156 
    157     return true;
    158 }
    159 
    160 ScrollbarPart ScrollbarThemeComposite::hitTest(Scrollbar* scrollbar, const PlatformMouseEvent& evt)
    161 {
    162     ScrollbarPart result = NoPart;
    163     if (!scrollbar->enabled())
    164         return result;
    165 
    166     IntPoint mousePosition = scrollbar->convertFromContainingWindow(evt.pos());
    167     mousePosition.move(scrollbar->x(), scrollbar->y());
    168 
    169     if (!scrollbar->frameRect().contains(mousePosition))
    170         return NoPart;
    171 
    172     result = ScrollbarBGPart;
    173 
    174     IntRect track = trackRect(scrollbar);
    175     if (track.contains(mousePosition)) {
    176         IntRect beforeThumbRect;
    177         IntRect thumbRect;
    178         IntRect afterThumbRect;
    179         splitTrack(scrollbar, track, beforeThumbRect, thumbRect, afterThumbRect);
    180         if (thumbRect.contains(mousePosition))
    181             result = ThumbPart;
    182         else if (beforeThumbRect.contains(mousePosition))
    183             result = BackTrackPart;
    184         else if (afterThumbRect.contains(mousePosition))
    185             result = ForwardTrackPart;
    186         else
    187             result = TrackBGPart;
    188     } else if (backButtonRect(scrollbar, BackButtonStartPart).contains(mousePosition))
    189         result = BackButtonStartPart;
    190     else if (backButtonRect(scrollbar, BackButtonEndPart).contains(mousePosition))
    191         result = BackButtonEndPart;
    192     else if (forwardButtonRect(scrollbar, ForwardButtonStartPart).contains(mousePosition))
    193         result = ForwardButtonStartPart;
    194     else if (forwardButtonRect(scrollbar, ForwardButtonEndPart).contains(mousePosition))
    195         result = ForwardButtonEndPart;
    196     return result;
    197 }
    198 
    199 void ScrollbarThemeComposite::invalidatePart(Scrollbar* scrollbar, ScrollbarPart part)
    200 {
    201     if (part == NoPart)
    202         return;
    203 
    204     IntRect result;
    205     switch (part) {
    206         case BackButtonStartPart:
    207             result = backButtonRect(scrollbar, BackButtonStartPart, true);
    208             break;
    209         case BackButtonEndPart:
    210             result = backButtonRect(scrollbar, BackButtonEndPart, true);
    211             break;
    212         case ForwardButtonStartPart:
    213             result = forwardButtonRect(scrollbar, ForwardButtonStartPart, true);
    214             break;
    215         case ForwardButtonEndPart:
    216             result = forwardButtonRect(scrollbar, ForwardButtonEndPart, true);
    217             break;
    218         case TrackBGPart:
    219             result = trackRect(scrollbar, true);
    220             break;
    221         case ScrollbarBGPart:
    222             result = scrollbar->frameRect();
    223             break;
    224         default: {
    225             IntRect beforeThumbRect, thumbRect, afterThumbRect;
    226             splitTrack(scrollbar, trackRect(scrollbar), beforeThumbRect, thumbRect, afterThumbRect);
    227             if (part == BackTrackPart)
    228                 result = beforeThumbRect;
    229             else if (part == ForwardTrackPart)
    230                 result = afterThumbRect;
    231             else
    232                 result = thumbRect;
    233         }
    234     }
    235     result.move(-scrollbar->x(), -scrollbar->y());
    236     scrollbar->invalidateRect(result);
    237 }
    238 
    239 void ScrollbarThemeComposite::splitTrack(Scrollbar* scrollbar, const IntRect& unconstrainedTrackRect, IntRect& beforeThumbRect, IntRect& thumbRect, IntRect& afterThumbRect)
    240 {
    241     // This function won't even get called unless we're big enough to have some combination of these three rects where at least
    242     // one of them is non-empty.
    243     IntRect trackRect = constrainTrackRectToTrackPieces(scrollbar, unconstrainedTrackRect);
    244     int thickness = scrollbar->orientation() == HorizontalScrollbar ? scrollbar->height() : scrollbar->width();
    245     int thumbPos = thumbPosition(scrollbar);
    246     if (scrollbar->orientation() == HorizontalScrollbar) {
    247         thumbRect = IntRect(trackRect.x() + thumbPos, trackRect.y() + (trackRect.height() - thickness) / 2, thumbLength(scrollbar), thickness);
    248         beforeThumbRect = IntRect(trackRect.x(), trackRect.y(), thumbPos + thumbRect.width() / 2, trackRect.height());
    249         afterThumbRect = IntRect(trackRect.x() + beforeThumbRect.width(), trackRect.y(), trackRect.right() - beforeThumbRect.right(), trackRect.height());
    250     } else {
    251         thumbRect = IntRect(trackRect.x() + (trackRect.width() - thickness) / 2, trackRect.y() + thumbPos, thickness, thumbLength(scrollbar));
    252         beforeThumbRect = IntRect(trackRect.x(), trackRect.y(), trackRect.width(), thumbPos + thumbRect.height() / 2);
    253         afterThumbRect = IntRect(trackRect.x(), trackRect.y() + beforeThumbRect.height(), trackRect.width(), trackRect.bottom() - beforeThumbRect.bottom());
    254     }
    255 }
    256 
    257 int ScrollbarThemeComposite::thumbPosition(Scrollbar* scrollbar)
    258 {
    259     if (scrollbar->enabled())
    260         return scrollbar->currentPos() * (trackLength(scrollbar) - thumbLength(scrollbar)) / scrollbar->maximum();
    261     return 0;
    262 }
    263 
    264 int ScrollbarThemeComposite::thumbLength(Scrollbar* scrollbar)
    265 {
    266     if (!scrollbar->enabled())
    267         return 0;
    268 
    269     float proportion = (float)scrollbar->visibleSize() / scrollbar->totalSize();
    270     int trackLen = trackLength(scrollbar);
    271     int length = proportion * trackLen;
    272     length = max(length, minimumThumbLength(scrollbar));
    273     if (length > trackLen)
    274         length = 0; // Once the thumb is below the track length, it just goes away (to make more room for the track).
    275     return length;
    276 }
    277 
    278 int ScrollbarThemeComposite::minimumThumbLength(Scrollbar* scrollbar)
    279 {
    280     return scrollbarThickness(scrollbar->controlSize());
    281 }
    282 
    283 int ScrollbarThemeComposite::trackPosition(Scrollbar* scrollbar)
    284 {
    285     IntRect constrainedTrackRect = constrainTrackRectToTrackPieces(scrollbar, trackRect(scrollbar));
    286     return (scrollbar->orientation() == HorizontalScrollbar) ? constrainedTrackRect.x() - scrollbar->x() : constrainedTrackRect.y() - scrollbar->y();
    287 }
    288 
    289 int ScrollbarThemeComposite::trackLength(Scrollbar* scrollbar)
    290 {
    291     IntRect constrainedTrackRect = constrainTrackRectToTrackPieces(scrollbar, trackRect(scrollbar));
    292     return (scrollbar->orientation() == HorizontalScrollbar) ? constrainedTrackRect.width() : constrainedTrackRect.height();
    293 }
    294 
    295 void ScrollbarThemeComposite::paintScrollCorner(ScrollView* view, GraphicsContext* context, const IntRect& cornerRect)
    296 {
    297     FrameView* frameView = static_cast<FrameView*>(view);
    298     Page* page = frameView->frame() ? frameView->frame()->page() : 0;
    299     if (page && page->settings()->shouldPaintCustomScrollbars()) {
    300         if (!page->chrome()->client()->paintCustomScrollCorner(context, cornerRect))
    301             context->fillRect(cornerRect, Color::white, DeviceColorSpace);
    302     }
    303 }
    304 
    305 }
    306