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