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#ifdef GTK_API_VERSION_2 30 31#include "GtkVersioning.h" 32#include "PlatformMouseEvent.h" 33#include "RenderThemeGtk.h" 34#include "ScrollView.h" 35#include "Scrollbar.h" 36#include "WidgetRenderingContext.h" 37#include <gtk/gtk.h> 38 39namespace WebCore { 40 41static void gtkStyleSetCallback(GtkWidget* widget, GtkStyle* previous, ScrollbarThemeGtk* scrollbarTheme) 42{ 43 scrollbarTheme->updateThemeProperties(); 44} 45 46ScrollbarThemeGtk::ScrollbarThemeGtk() 47{ 48 updateThemeProperties(); 49 g_signal_connect(static_cast<RenderThemeGtk*>(RenderTheme::defaultTheme().get())->gtkHScrollbar(), 50 "style-set", G_CALLBACK(gtkStyleSetCallback), this); 51} 52 53void ScrollbarThemeGtk::updateThemeProperties() 54{ 55 GtkWidget* scrollbar = static_cast<RenderThemeGtk*>(RenderTheme::defaultTheme().get())->gtkHScrollbar(); 56 gtk_widget_style_get(scrollbar, 57 "slider_width", &m_thumbFatness, 58 "trough_border", &m_troughBorderWidth, 59 "stepper-size", &m_stepperSize, 60 "trough-under-steppers", &m_troughUnderSteppers, 61 "has-backward-stepper", &m_hasBackButtonStartPart, 62 "has-forward-stepper", &m_hasForwardButtonEndPart, 63 "has-secondary-forward-stepper", &m_hasForwardButtonStartPart, 64 "has-secondary-backward-stepper", &m_hasBackButtonEndPart, NULL); 65 m_minThumbLength = gtk_range_get_min_slider_size(GTK_RANGE(scrollbar)); 66 updateScrollbarsFrameThickness(); 67} 68 69static GtkWidget* getWidgetForScrollbar(ScrollbarThemeClient* scrollbar) 70{ 71 RenderThemeGtk* theme = static_cast<RenderThemeGtk*>(RenderTheme::defaultTheme().get()); 72 return scrollbar->orientation() == VerticalScrollbar ? theme->gtkVScrollbar() : theme->gtkHScrollbar(); 73} 74 75void ScrollbarThemeGtk::paintTrackBackground(GraphicsContext* context, ScrollbarThemeClient* scrollbar, const IntRect& rect) 76{ 77 // Paint the track background. If the trough-under-steppers property is true, this 78 // should be the full size of the scrollbar, but if is false, it should only be the 79 // track rect. 80 IntRect fullScrollbarRect(rect); 81 if (m_troughUnderSteppers) 82 fullScrollbarRect = IntRect(scrollbar->x(), scrollbar->y(), scrollbar->width(), scrollbar->height()); 83 84 WidgetRenderingContext widgetContext(context, fullScrollbarRect); 85 IntRect paintRect(IntPoint(), fullScrollbarRect.size()); 86 widgetContext.gtkPaintBox(paintRect, getWidgetForScrollbar(scrollbar), 87 GTK_STATE_ACTIVE, GTK_SHADOW_IN, "trough"); 88} 89 90void ScrollbarThemeGtk::paintScrollbarBackground(GraphicsContext* context, ScrollbarThemeClient* scrollbar) 91{ 92 IntRect fullScrollbarRect = IntRect(scrollbar->x(), scrollbar->y(), scrollbar->width(), scrollbar->height()); 93 94 WidgetRenderingContext widgetContext(context, fullScrollbarRect); 95 widgetContext.gtkPaintBox(fullScrollbarRect, getWidgetForScrollbar(scrollbar), 96 GTK_STATE_NORMAL, GTK_SHADOW_IN, "scrolled_window"); 97} 98 99void ScrollbarThemeGtk::paintThumb(GraphicsContext* context, ScrollbarThemeClient* scrollbar, const IntRect& rect) 100{ 101 GtkWidget* widget = getWidgetForScrollbar(scrollbar); 102 gboolean activateSlider; 103 gtk_widget_style_get(widget, "activate-slider", &activateSlider, NULL); 104 105 GtkStateType stateType = GTK_STATE_NORMAL; 106 GtkShadowType shadowType = GTK_SHADOW_OUT; 107 if (activateSlider && scrollbar->pressedPart() == ThumbPart) { 108 stateType = GTK_STATE_ACTIVE; 109 shadowType = GTK_SHADOW_IN; 110 } else if (scrollbar->pressedPart() == ThumbPart || scrollbar->hoveredPart() == ThumbPart) 111 stateType = GTK_STATE_PRELIGHT; 112 113 // The adjustment controls the rendering of the scrollbar thumb. If it's not set 114 // properly the theme may not draw the thumb borders properly. 115 GtkAdjustment* adjustment = gtk_range_get_adjustment(GTK_RANGE(widget)); 116 gtk_adjustment_set_value(adjustment, scrollbar->currentPos()); 117 gtk_adjustment_set_lower(adjustment, 0); 118 gtk_adjustment_set_upper(adjustment, scrollbar->maximum()); 119 120 GtkOrientation orientation = GTK_ORIENTATION_HORIZONTAL; 121 if (scrollbar->orientation() == VerticalScrollbar) { 122 gtk_adjustment_set_page_size(adjustment, rect.height()); 123 orientation = GTK_ORIENTATION_VERTICAL; 124 } else 125 gtk_adjustment_set_page_size(adjustment, rect.width()); 126 127 WidgetRenderingContext widgetContext(context, rect); 128 IntRect sliderRect(IntPoint(), rect.size()); 129 widgetContext.gtkPaintSlider(sliderRect, widget, stateType, shadowType, "slider", orientation); 130} 131 132void ScrollbarThemeGtk::paintButton(GraphicsContext* context, ScrollbarThemeClient* scrollbar, const IntRect& rect, ScrollbarPart part) 133{ 134 // The buttons will be disabled if the thumb is as the appropriate extreme. 135 GtkShadowType shadowType = GTK_SHADOW_OUT; 136 GtkStateType stateType = GTK_STATE_INSENSITIVE; 137 bool pressed = (part == scrollbar->pressedPart()); 138 139 if ((BackButtonStartPart == part && scrollbar->currentPos()) 140 || (BackButtonEndPart == part && scrollbar->currentPos()) 141 || (ForwardButtonEndPart == part && scrollbar->currentPos() != scrollbar->maximum()) 142 || (ForwardButtonStartPart == part && scrollbar->currentPos() != scrollbar->maximum())) { 143 stateType = GTK_STATE_NORMAL; 144 if (pressed) { 145 stateType = GTK_STATE_ACTIVE; 146 shadowType = GTK_SHADOW_IN; 147 } else if (part == scrollbar->hoveredPart()) 148 stateType = GTK_STATE_PRELIGHT; 149 } 150 151 // Themes determine how to draw the button (which button to draw) based on the allocation 152 // of the widget. Where the target rect is in relation to the total widget allocation 153 // determines the button. 154 ScrollbarOrientation orientation = scrollbar->orientation(); 155 int buttonSize = (orientation == VerticalScrollbar) ? rect.height() : rect.width(); 156 int totalAllocation = buttonSize * 5; // One space for each button and one extra. 157 int buttonOffset = 0; 158 if (ForwardButtonStartPart == part) 159 buttonOffset = buttonSize; 160 else if (BackButtonEndPart == part) 161 buttonOffset = 3 * buttonSize; 162 else if (ForwardButtonEndPart == part) 163 buttonOffset = 4 * buttonSize; 164 165 // Now we want the allocation to be relative to the origin of the painted rect. 166 GtkWidget* widget = getWidgetForScrollbar(scrollbar); 167 GtkAllocation allocation; 168 gtk_widget_get_allocation(widget, &allocation); 169 allocation.x = allocation.y = 0; 170 allocation.width = rect.width(); 171 allocation.height = rect.height(); 172 173 if (orientation == VerticalScrollbar) { 174 allocation.height = totalAllocation; 175 allocation.y -= buttonOffset; 176 } else { 177 allocation.width = totalAllocation; 178 allocation.x -= buttonOffset; 179 } 180 gtk_widget_set_allocation(widget, &allocation); 181 182 const char* detail = orientation == VerticalScrollbar ? "vscrollbar" : "hscrollbar"; 183 WidgetRenderingContext widgetContext(context, rect); 184 185 IntRect buttonRect(IntPoint(), rect.size()); 186 widgetContext.gtkPaintBox(buttonRect, widget, stateType, shadowType, detail); 187 188 float arrowScaling; 189 gtk_widget_style_get(widget, "arrow-scaling", &arrowScaling, NULL); 190 IntSize arrowSize = rect.size(); 191 arrowSize.scale(arrowScaling); 192 IntRect arrowRect(IntPoint(buttonRect.x() + (buttonRect.width() - arrowSize.width()) / 2, 193 buttonRect.y() + (buttonRect.height() - arrowSize.height()) / 2), 194 arrowSize); 195 if (pressed) { 196 int arrowDisplacementX, arrowDisplacementY; 197 gtk_widget_style_get(widget, 198 "arrow-displacement-x", &arrowDisplacementX, 199 "arrow-displacement-y", &arrowDisplacementY, 200 NULL); 201 arrowRect.move(arrowDisplacementX, arrowDisplacementY); 202 } 203 204 GtkArrowType arrowType = GTK_ARROW_DOWN; 205 if (orientation == VerticalScrollbar) { 206 if (part == BackButtonEndPart || part == BackButtonStartPart) 207 arrowType = GTK_ARROW_UP; 208 } else if (orientation == HorizontalScrollbar) { 209 arrowType = GTK_ARROW_RIGHT; 210 if (part == BackButtonEndPart || part == BackButtonStartPart) 211 arrowType = GTK_ARROW_LEFT; 212 } 213 widgetContext.gtkPaintArrow(arrowRect, widget, stateType, shadowType, arrowType, detail); 214} 215 216} // namespace WebCore 217 218#endif // GTK_API_VERSION_2 219