| 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 "PlatformContextCairo.h" |
|---|
| 30 | #include "PlatformMouseEvent.h" |
|---|
| 31 | #include "ScrollView.h" |
|---|
| 32 | #include "Scrollbar.h" |
|---|
| 33 | #include <gtk/gtk.h> |
|---|
| 34 | #include <wtf/NeverDestroyed.h> |
|---|
| 35 | #include <wtf/glib/GRefPtr.h> |
|---|
| 36 | |
|---|
| 37 | namespace WebCore { |
|---|
| 38 | |
|---|
| 39 | ScrollbarTheme* ScrollbarTheme::nativeTheme() |
|---|
| 40 | { |
|---|
| 41 | static ScrollbarThemeGtk theme; |
|---|
| 42 | return &theme; |
|---|
| 43 | } |
|---|
| 44 | |
|---|
| 45 | ScrollbarThemeGtk::~ScrollbarThemeGtk() |
|---|
| 46 | { |
|---|
| 47 | } |
|---|
| 48 | |
|---|
| 49 | bool ScrollbarThemeGtk::hasThumb(Scrollbar& scrollbar) |
|---|
| 50 | { |
|---|
| 51 | #ifndef GTK_API_VERSION_2 |
|---|
| 52 | // This method is just called as a paint-time optimization to see if |
|---|
| 53 | // painting the thumb can be skipped. We don't have to be exact here. |
|---|
| 54 | return thumbLength(scrollbar) > 0; |
|---|
| 55 | #else |
|---|
| 56 | UNUSED_PARAM(scrollbar); |
|---|
| 57 | return false; |
|---|
| 58 | #endif |
|---|
| 59 | } |
|---|
| 60 | |
|---|
| 61 | IntRect ScrollbarThemeGtk::backButtonRect(Scrollbar& scrollbar, ScrollbarPart part, bool) |
|---|
| 62 | { |
|---|
| 63 | #ifndef GTK_API_VERSION_2 |
|---|
| 64 | if (part == BackButtonEndPart && !m_hasBackButtonEndPart) |
|---|
| 65 | return IntRect(); |
|---|
| 66 | if (part == BackButtonStartPart && !m_hasBackButtonStartPart) |
|---|
| 67 | return IntRect(); |
|---|
| 68 | |
|---|
| 69 | int x = scrollbar.x() + m_troughBorderWidth; |
|---|
| 70 | int y = scrollbar.y() + m_troughBorderWidth; |
|---|
| 71 | IntSize size = buttonSize(scrollbar); |
|---|
| 72 | if (part == BackButtonStartPart) |
|---|
| 73 | return IntRect(x, y, size.width(), size.height()); |
|---|
| 74 | |
|---|
| 75 | // BackButtonEndPart (alternate button) |
|---|
| 76 | if (scrollbar.orientation() == HorizontalScrollbar) |
|---|
| 77 | return IntRect(scrollbar.x() + scrollbar.width() - m_troughBorderWidth - (2 * size.width()), y, size.width(), size.height()); |
|---|
| 78 | |
|---|
| 79 | // VerticalScrollbar alternate button |
|---|
| 80 | return IntRect(x, scrollbar.y() + scrollbar.height() - m_troughBorderWidth - (2 * size.height()), size.width(), size.height()); |
|---|
| 81 | #else |
|---|
| 82 | UNUSED_PARAM(scrollbar); |
|---|
| 83 | UNUSED_PARAM(part); |
|---|
| 84 | return IntRect(); |
|---|
| 85 | #endif |
|---|
| 86 | } |
|---|
| 87 | |
|---|
| 88 | IntRect ScrollbarThemeGtk::forwardButtonRect(Scrollbar& scrollbar, ScrollbarPart part, bool) |
|---|
| 89 | { |
|---|
| 90 | #ifndef GTK_API_VERSION_2 |
|---|
| 91 | if (part == ForwardButtonStartPart && !m_hasForwardButtonStartPart) |
|---|
| 92 | return IntRect(); |
|---|
| 93 | if (part == ForwardButtonEndPart && !m_hasForwardButtonEndPart) |
|---|
| 94 | return IntRect(); |
|---|
| 95 | |
|---|
| 96 | IntSize size = buttonSize(scrollbar); |
|---|
| 97 | if (scrollbar.orientation() == HorizontalScrollbar) { |
|---|
| 98 | int y = scrollbar.y() + m_troughBorderWidth; |
|---|
| 99 | if (part == ForwardButtonEndPart) |
|---|
| 100 | return IntRect(scrollbar.x() + scrollbar.width() - size.width() - m_troughBorderWidth, y, size.width(), size.height()); |
|---|
| 101 | |
|---|
| 102 | // ForwardButtonStartPart (alternate button) |
|---|
| 103 | return IntRect(scrollbar.x() + m_troughBorderWidth + size.width(), y, size.width(), size.height()); |
|---|
| 104 | } |
|---|
| 105 | |
|---|
| 106 | // VerticalScrollbar |
|---|
| 107 | int x = scrollbar.x() + m_troughBorderWidth; |
|---|
| 108 | if (part == ForwardButtonEndPart) |
|---|
| 109 | return IntRect(x, scrollbar.y() + scrollbar.height() - size.height() - m_troughBorderWidth, size.width(), size.height()); |
|---|
| 110 | |
|---|
| 111 | // ForwardButtonStartPart (alternate button) |
|---|
| 112 | return IntRect(x, scrollbar.y() + m_troughBorderWidth + size.height(), size.width(), size.height()); |
|---|
| 113 | #else |
|---|
| 114 | UNUSED_PARAM(scrollbar); |
|---|
| 115 | UNUSED_PARAM(part); |
|---|
| 116 | return IntRect(); |
|---|
| 117 | #endif |
|---|
| 118 | } |
|---|
| 119 | |
|---|
| 120 | IntRect ScrollbarThemeGtk::trackRect(Scrollbar& scrollbar, bool) |
|---|
| 121 | { |
|---|
| 122 | #ifndef GTK_API_VERSION_2 |
|---|
| 123 | // The padding along the thumb movement axis includes the trough border |
|---|
| 124 | // plus the size of stepper spacing (the space between the stepper and |
|---|
| 125 | // the place where the thumb stops). There is often no stepper spacing. |
|---|
| 126 | int movementAxisPadding = m_troughBorderWidth + m_stepperSpacing; |
|---|
| 127 | |
|---|
| 128 | // The fatness of the scrollbar on the non-movement axis. |
|---|
| 129 | int thickness = scrollbarThickness(scrollbar.controlSize()); |
|---|
| 130 | |
|---|
| 131 | int startButtonsOffset = 0; |
|---|
| 132 | int buttonsWidth = 0; |
|---|
| 133 | if (m_hasForwardButtonStartPart) { |
|---|
| 134 | startButtonsOffset += m_stepperSize; |
|---|
| 135 | buttonsWidth += m_stepperSize; |
|---|
| 136 | } |
|---|
| 137 | if (m_hasBackButtonStartPart) { |
|---|
| 138 | startButtonsOffset += m_stepperSize; |
|---|
| 139 | buttonsWidth += m_stepperSize; |
|---|
| 140 | } |
|---|
| 141 | if (m_hasBackButtonEndPart) |
|---|
| 142 | buttonsWidth += m_stepperSize; |
|---|
| 143 | if (m_hasForwardButtonEndPart) |
|---|
| 144 | buttonsWidth += m_stepperSize; |
|---|
| 145 | |
|---|
| 146 | if (scrollbar.orientation() == HorizontalScrollbar) { |
|---|
| 147 | // Once the scrollbar becomes smaller than the natural size of the |
|---|
| 148 | // two buttons, the track disappears. |
|---|
| 149 | if (scrollbar.width() < 2 * thickness) |
|---|
| 150 | return IntRect(); |
|---|
| 151 | return IntRect(scrollbar.x() + movementAxisPadding + startButtonsOffset, scrollbar.y(), |
|---|
| 152 | scrollbar.width() - (2 * movementAxisPadding) - buttonsWidth, thickness); |
|---|
| 153 | } |
|---|
| 154 | |
|---|
| 155 | if (scrollbar.height() < 2 * thickness) |
|---|
| 156 | return IntRect(); |
|---|
| 157 | return IntRect(scrollbar.x(), scrollbar.y() + movementAxisPadding + startButtonsOffset, |
|---|
| 158 | thickness, scrollbar.height() - (2 * movementAxisPadding) - buttonsWidth); |
|---|
| 159 | #else |
|---|
| 160 | UNUSED_PARAM(scrollbar); |
|---|
| 161 | return IntRect(); |
|---|
| 162 | #endif |
|---|
| 163 | } |
|---|
| 164 | |
|---|
| 165 | #ifndef GTK_API_VERSION_2 |
|---|
| 166 | class ScrollbarStyleContext { |
|---|
| 167 | WTF_MAKE_NONCOPYABLE(ScrollbarStyleContext); WTF_MAKE_FAST_ALLOCATED; |
|---|
| 168 | public: |
|---|
| 169 | ScrollbarStyleContext() |
|---|
| 170 | : m_context(adoptGRef(gtk_style_context_new())) |
|---|
| 171 | { |
|---|
| 172 | GtkWidgetPath* path = gtk_widget_path_new(); |
|---|
| 173 | gtk_widget_path_append_type(path, GTK_TYPE_SCROLLBAR); |
|---|
| 174 | gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SCROLLBAR); |
|---|
| 175 | gtk_style_context_set_path(m_context.get(), path); |
|---|
| 176 | gtk_widget_path_free(path); |
|---|
| 177 | } |
|---|
| 178 | |
|---|
| 179 | ~ScrollbarStyleContext() |
|---|
| 180 | { |
|---|
| 181 | } |
|---|
| 182 | |
|---|
| 183 | GtkStyleContext* context() const { return m_context.get(); } |
|---|
| 184 | |
|---|
| 185 | private: |
|---|
| 186 | GRefPtr<GtkStyleContext> m_context; |
|---|
| 187 | }; |
|---|
| 188 | |
|---|
| 189 | static GtkStyleContext* gtkScrollbarStyleContext() |
|---|
| 190 | { |
|---|
| 191 | static NeverDestroyed<ScrollbarStyleContext> styleContext; |
|---|
| 192 | return styleContext.get().context(); |
|---|
| 193 | } |
|---|
| 194 | |
|---|
| 195 | ScrollbarThemeGtk::ScrollbarThemeGtk() |
|---|
| 196 | { |
|---|
| 197 | updateThemeProperties(); |
|---|
| 198 | } |
|---|
| 199 | |
|---|
| 200 | void ScrollbarThemeGtk::themeChanged() |
|---|
| 201 | { |
|---|
| 202 | gtk_style_context_invalidate(gtkScrollbarStyleContext()); |
|---|
| 203 | updateThemeProperties(); |
|---|
| 204 | } |
|---|
| 205 | |
|---|
| 206 | void ScrollbarThemeGtk::updateThemeProperties() |
|---|
| 207 | { |
|---|
| 208 | gtk_style_context_get_style( |
|---|
| 209 | gtkScrollbarStyleContext(), |
|---|
| 210 | "min-slider-length", &m_minThumbLength, |
|---|
| 211 | "slider-width", &m_thumbFatness, |
|---|
| 212 | "trough-border", &m_troughBorderWidth, |
|---|
| 213 | "stepper-size", &m_stepperSize, |
|---|
| 214 | "stepper-spacing", &m_stepperSpacing, |
|---|
| 215 | "trough-under-steppers", &m_troughUnderSteppers, |
|---|
| 216 | "has-backward-stepper", &m_hasBackButtonStartPart, |
|---|
| 217 | "has-forward-stepper", &m_hasForwardButtonEndPart, |
|---|
| 218 | "has-secondary-backward-stepper", &m_hasBackButtonEndPart, |
|---|
| 219 | "has-secondary-forward-stepper", &m_hasForwardButtonStartPart, |
|---|
| 220 | nullptr); |
|---|
| 221 | updateScrollbarsFrameThickness(); |
|---|
| 222 | } |
|---|
| 223 | |
|---|
| 224 | typedef HashSet<Scrollbar*> ScrollbarMap; |
|---|
| 225 | |
|---|
| 226 | static ScrollbarMap& scrollbarMap() |
|---|
| 227 | { |
|---|
| 228 | static NeverDestroyed<ScrollbarMap> map; |
|---|
| 229 | return map; |
|---|
| 230 | } |
|---|
| 231 | |
|---|
| 232 | void ScrollbarThemeGtk::registerScrollbar(Scrollbar& scrollbar) |
|---|
| 233 | { |
|---|
| 234 | scrollbarMap().add(&scrollbar); |
|---|
| 235 | } |
|---|
| 236 | |
|---|
| 237 | void ScrollbarThemeGtk::unregisterScrollbar(Scrollbar& scrollbar) |
|---|
| 238 | { |
|---|
| 239 | scrollbarMap().remove(&scrollbar); |
|---|
| 240 | } |
|---|
| 241 | |
|---|
| 242 | void ScrollbarThemeGtk::updateScrollbarsFrameThickness() |
|---|
| 243 | { |
|---|
| 244 | if (scrollbarMap().isEmpty()) |
|---|
| 245 | return; |
|---|
| 246 | |
|---|
| 247 | // Update the thickness of every interior frame scrollbar widget. The |
|---|
| 248 | // platform-independent scrollbar them code isn't yet smart enough to get |
|---|
| 249 | // this information when it paints. |
|---|
| 250 | for (const auto& scrollbar : scrollbarMap()) { |
|---|
| 251 | // Top-level scrollbar i.e. scrollbars who have a parent ScrollView |
|---|
| 252 | // with no parent are native, and thus do not need to be resized. |
|---|
| 253 | if (!scrollbar->parent() || !scrollbar->parent()->parent()) |
|---|
| 254 | return; |
|---|
| 255 | |
|---|
| 256 | int thickness = scrollbarThickness(scrollbar->controlSize()); |
|---|
| 257 | if (scrollbar->orientation() == HorizontalScrollbar) |
|---|
| 258 | scrollbar->setFrameRect(IntRect(0, scrollbar->parent()->height() - thickness, scrollbar->width(), thickness)); |
|---|
| 259 | else |
|---|
| 260 | scrollbar->setFrameRect(IntRect(scrollbar->parent()->width() - thickness, 0, thickness, scrollbar->height())); |
|---|
| 261 | } |
|---|
| 262 | } |
|---|
| 263 | |
|---|
| 264 | IntRect ScrollbarThemeGtk::thumbRect(Scrollbar& scrollbar, const IntRect& unconstrainedTrackRect) |
|---|
| 265 | { |
|---|
| 266 | IntRect trackRect = constrainTrackRectToTrackPieces(scrollbar, unconstrainedTrackRect); |
|---|
| 267 | int thumbPos = thumbPosition(scrollbar); |
|---|
| 268 | if (scrollbar.orientation() == HorizontalScrollbar) |
|---|
| 269 | return IntRect(trackRect.x() + thumbPos, trackRect.y() + (trackRect.height() - m_thumbFatness) / 2, thumbLength(scrollbar), m_thumbFatness); |
|---|
| 270 | |
|---|
| 271 | // VerticalScrollbar |
|---|
| 272 | return IntRect(trackRect.x() + (trackRect.width() - m_thumbFatness) / 2, trackRect.y() + thumbPos, m_thumbFatness, thumbLength(scrollbar)); |
|---|
| 273 | } |
|---|
| 274 | |
|---|
| 275 | static void applyScrollbarStyleContextClasses(GtkStyleContext* context, ScrollbarOrientation orientation) |
|---|
| 276 | { |
|---|
| 277 | gtk_style_context_add_class(context, GTK_STYLE_CLASS_SCROLLBAR); |
|---|
| 278 | gtk_style_context_add_class(context, orientation == VerticalScrollbar ? GTK_STYLE_CLASS_VERTICAL : GTK_STYLE_CLASS_HORIZONTAL); |
|---|
| 279 | } |
|---|
| 280 | |
|---|
| 281 | static void adjustRectAccordingToMargin(GtkStyleContext* context, GtkStateFlags state, IntRect& rect) |
|---|
| 282 | { |
|---|
| 283 | GtkBorder margin; |
|---|
| 284 | gtk_style_context_get_margin(context, state, &margin); |
|---|
| 285 | rect.move(margin.left, margin.right); |
|---|
| 286 | rect.contract(margin.left + margin.right, margin.top + margin.bottom); |
|---|
| 287 | } |
|---|
| 288 | |
|---|
| 289 | void ScrollbarThemeGtk::paintTrackBackground(GraphicsContext& context, Scrollbar& scrollbar, const IntRect& rect) |
|---|
| 290 | { |
|---|
| 291 | // Paint the track background. If the trough-under-steppers property is true, this |
|---|
| 292 | // should be the full size of the scrollbar, but if is false, it should only be the |
|---|
| 293 | // track rect. |
|---|
| 294 | IntRect fullScrollbarRect(rect); |
|---|
| 295 | if (m_troughUnderSteppers) |
|---|
| 296 | fullScrollbarRect = IntRect(scrollbar.x(), scrollbar.y(), scrollbar.width(), scrollbar.height()); |
|---|
| 297 | |
|---|
| 298 | GtkStyleContext* styleContext = gtkScrollbarStyleContext(); |
|---|
| 299 | gtk_style_context_save(styleContext); |
|---|
| 300 | |
|---|
| 301 | applyScrollbarStyleContextClasses(styleContext, scrollbar.orientation()); |
|---|
| 302 | gtk_style_context_add_class(styleContext, GTK_STYLE_CLASS_TROUGH); |
|---|
| 303 | |
|---|
| 304 | adjustRectAccordingToMargin(styleContext, static_cast<GtkStateFlags>(0), fullScrollbarRect); |
|---|
| 305 | gtk_render_background(styleContext, context.platformContext()->cr(), fullScrollbarRect.x(), fullScrollbarRect.y(), fullScrollbarRect.width(), fullScrollbarRect.height()); |
|---|
| 306 | gtk_render_frame(styleContext, context.platformContext()->cr(), fullScrollbarRect.x(), fullScrollbarRect.y(), fullScrollbarRect.width(), fullScrollbarRect.height()); |
|---|
| 307 | |
|---|
| 308 | gtk_style_context_restore(styleContext); |
|---|
| 309 | } |
|---|
| 310 | |
|---|
| 311 | void ScrollbarThemeGtk::paintScrollbarBackground(GraphicsContext& context, Scrollbar& scrollbar) |
|---|
| 312 | { |
|---|
| 313 | GtkStyleContext* styleContext = gtkScrollbarStyleContext(); |
|---|
| 314 | gtk_style_context_save(styleContext); |
|---|
| 315 | |
|---|
| 316 | applyScrollbarStyleContextClasses(styleContext, scrollbar.orientation()); |
|---|
| 317 | gtk_style_context_add_class(styleContext, "scrolled-window"); |
|---|
| 318 | gtk_render_frame(styleContext, context.platformContext()->cr(), scrollbar.x(), scrollbar.y(), scrollbar.width(), scrollbar.height()); |
|---|
| 319 | |
|---|
| 320 | gtk_style_context_restore(styleContext); |
|---|
| 321 | } |
|---|
| 322 | |
|---|
| 323 | void ScrollbarThemeGtk::paintThumb(GraphicsContext& context, Scrollbar& scrollbar, const IntRect& rect) |
|---|
| 324 | { |
|---|
| 325 | GtkStyleContext* styleContext = gtkScrollbarStyleContext(); |
|---|
| 326 | gtk_style_context_save(styleContext); |
|---|
| 327 | |
|---|
| 328 | ScrollbarOrientation orientation = scrollbar.orientation(); |
|---|
| 329 | applyScrollbarStyleContextClasses(styleContext, orientation); |
|---|
| 330 | gtk_style_context_add_class(styleContext, GTK_STYLE_CLASS_SLIDER); |
|---|
| 331 | |
|---|
| 332 | guint flags = 0; |
|---|
| 333 | if (scrollbar.pressedPart() == ThumbPart) |
|---|
| 334 | flags |= GTK_STATE_FLAG_ACTIVE; |
|---|
| 335 | if (scrollbar.hoveredPart() == ThumbPart) |
|---|
| 336 | flags |= GTK_STATE_FLAG_PRELIGHT; |
|---|
| 337 | gtk_style_context_set_state(styleContext, static_cast<GtkStateFlags>(flags)); |
|---|
| 338 | |
|---|
| 339 | IntRect thumbRect(rect); |
|---|
| 340 | adjustRectAccordingToMargin(styleContext, static_cast<GtkStateFlags>(flags), thumbRect); |
|---|
| 341 | gtk_render_slider(styleContext, context.platformContext()->cr(), thumbRect.x(), thumbRect.y(), thumbRect.width(), thumbRect.height(), |
|---|
| 342 | orientation == VerticalScrollbar ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL); |
|---|
| 343 | |
|---|
| 344 | gtk_style_context_restore(styleContext); |
|---|
| 345 | } |
|---|
| 346 | |
|---|
| 347 | void ScrollbarThemeGtk::paintButton(GraphicsContext& context, Scrollbar& scrollbar, const IntRect& rect, ScrollbarPart part) |
|---|
| 348 | { |
|---|
| 349 | GtkStyleContext* styleContext = gtkScrollbarStyleContext(); |
|---|
| 350 | gtk_style_context_save(styleContext); |
|---|
| 351 | |
|---|
| 352 | ScrollbarOrientation orientation = scrollbar.orientation(); |
|---|
| 353 | applyScrollbarStyleContextClasses(styleContext, orientation); |
|---|
| 354 | |
|---|
| 355 | guint flags = 0; |
|---|
| 356 | if ((BackButtonStartPart == part && scrollbar.currentPos()) |
|---|
| 357 | || (BackButtonEndPart == part && scrollbar.currentPos()) |
|---|
| 358 | || (ForwardButtonEndPart == part && scrollbar.currentPos() != scrollbar.maximum()) |
|---|
| 359 | || (ForwardButtonStartPart == part && scrollbar.currentPos() != scrollbar.maximum())) { |
|---|
| 360 | if (part == scrollbar.pressedPart()) |
|---|
| 361 | flags |= GTK_STATE_FLAG_ACTIVE; |
|---|
| 362 | if (part == scrollbar.hoveredPart()) |
|---|
| 363 | flags |= GTK_STATE_FLAG_PRELIGHT; |
|---|
| 364 | } else |
|---|
| 365 | flags |= GTK_STATE_FLAG_INSENSITIVE; |
|---|
| 366 | gtk_style_context_set_state(styleContext, static_cast<GtkStateFlags>(flags)); |
|---|
| 367 | |
|---|
| 368 | gtk_style_context_add_class(styleContext, GTK_STYLE_CLASS_BUTTON); |
|---|
| 369 | gtk_render_background(styleContext, context.platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height()); |
|---|
| 370 | gtk_render_frame(styleContext, context.platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height()); |
|---|
| 371 | |
|---|
| 372 | gfloat arrowScaling; |
|---|
| 373 | gtk_style_context_get_style(styleContext, "arrow-scaling", &arrowScaling, nullptr); |
|---|
| 374 | |
|---|
| 375 | double arrowSize = std::min(rect.width(), rect.height()) * arrowScaling; |
|---|
| 376 | FloatPoint arrowPoint( |
|---|
| 377 | rect.x() + (rect.width() - arrowSize) / 2, |
|---|
| 378 | rect.y() + (rect.height() - arrowSize) / 2); |
|---|
| 379 | |
|---|
| 380 | if (flags & GTK_STATE_FLAG_ACTIVE) { |
|---|
| 381 | gint arrowDisplacementX, arrowDisplacementY; |
|---|
| 382 | gtk_style_context_get_style(styleContext, "arrow-displacement-x", &arrowDisplacementX, "arrow-displacement-y", &arrowDisplacementY, nullptr); |
|---|
| 383 | arrowPoint.move(arrowDisplacementX, arrowDisplacementY); |
|---|
| 384 | } |
|---|
| 385 | |
|---|
| 386 | gdouble angle; |
|---|
| 387 | if (orientation == VerticalScrollbar) |
|---|
| 388 | angle = (part == ForwardButtonEndPart || part == ForwardButtonStartPart) ? G_PI : 0; |
|---|
| 389 | else |
|---|
| 390 | angle = (part == ForwardButtonEndPart || part == ForwardButtonStartPart) ? G_PI / 2 : 3 * (G_PI / 2); |
|---|
| 391 | |
|---|
| 392 | gtk_render_arrow(styleContext, context.platformContext()->cr(), angle, arrowPoint.x(), arrowPoint.y(), arrowSize); |
|---|
| 393 | |
|---|
| 394 | gtk_style_context_restore(styleContext); |
|---|
| 395 | } |
|---|
| 396 | |
|---|
| 397 | bool ScrollbarThemeGtk::paint(Scrollbar& scrollbar, GraphicsContext& graphicsContext, const IntRect& damageRect) |
|---|
| 398 | { |
|---|
| 399 | if (graphicsContext.paintingDisabled()) |
|---|
| 400 | return false; |
|---|
| 401 | |
|---|
| 402 | // Create the ScrollbarControlPartMask based on the damageRect |
|---|
| 403 | ScrollbarControlPartMask scrollMask = NoPart; |
|---|
| 404 | |
|---|
| 405 | IntRect backButtonStartPaintRect; |
|---|
| 406 | IntRect backButtonEndPaintRect; |
|---|
| 407 | IntRect forwardButtonStartPaintRect; |
|---|
| 408 | IntRect forwardButtonEndPaintRect; |
|---|
| 409 | if (hasButtons(scrollbar)) { |
|---|
| 410 | backButtonStartPaintRect = backButtonRect(scrollbar, BackButtonStartPart, true); |
|---|
| 411 | if (damageRect.intersects(backButtonStartPaintRect)) |
|---|
| 412 | scrollMask |= BackButtonStartPart; |
|---|
| 413 | backButtonEndPaintRect = backButtonRect(scrollbar, BackButtonEndPart, true); |
|---|
| 414 | if (damageRect.intersects(backButtonEndPaintRect)) |
|---|
| 415 | scrollMask |= BackButtonEndPart; |
|---|
| 416 | forwardButtonStartPaintRect = forwardButtonRect(scrollbar, ForwardButtonStartPart, true); |
|---|
| 417 | if (damageRect.intersects(forwardButtonStartPaintRect)) |
|---|
| 418 | scrollMask |= ForwardButtonStartPart; |
|---|
| 419 | forwardButtonEndPaintRect = forwardButtonRect(scrollbar, ForwardButtonEndPart, true); |
|---|
| 420 | if (damageRect.intersects(forwardButtonEndPaintRect)) |
|---|
| 421 | scrollMask |= ForwardButtonEndPart; |
|---|
| 422 | } |
|---|
| 423 | |
|---|
| 424 | IntRect trackPaintRect = trackRect(scrollbar, true); |
|---|
| 425 | if (damageRect.intersects(trackPaintRect)) |
|---|
| 426 | scrollMask |= TrackBGPart; |
|---|
| 427 | |
|---|
| 428 | if (m_troughUnderSteppers && (scrollMask & BackButtonStartPart |
|---|
| 429 | || scrollMask & BackButtonEndPart |
|---|
| 430 | || scrollMask & ForwardButtonStartPart |
|---|
| 431 | || scrollMask & ForwardButtonEndPart)) |
|---|
| 432 | scrollMask |= TrackBGPart; |
|---|
| 433 | |
|---|
| 434 | bool thumbPresent = hasThumb(scrollbar); |
|---|
| 435 | IntRect currentThumbRect; |
|---|
| 436 | if (thumbPresent) { |
|---|
| 437 | IntRect track = trackRect(scrollbar, false); |
|---|
| 438 | currentThumbRect = thumbRect(scrollbar, track); |
|---|
| 439 | if (damageRect.intersects(currentThumbRect)) |
|---|
| 440 | scrollMask |= ThumbPart; |
|---|
| 441 | } |
|---|
| 442 | |
|---|
| 443 | ScrollbarControlPartMask allButtons = BackButtonStartPart | BackButtonEndPart | ForwardButtonStartPart | ForwardButtonEndPart; |
|---|
| 444 | if (scrollMask & TrackBGPart || scrollMask & ThumbPart || scrollMask & allButtons) |
|---|
| 445 | paintScrollbarBackground(graphicsContext, scrollbar); |
|---|
| 446 | paintTrackBackground(graphicsContext, scrollbar, trackPaintRect); |
|---|
| 447 | |
|---|
| 448 | // Paint the back and forward buttons. |
|---|
| 449 | if (scrollMask & BackButtonStartPart) |
|---|
| 450 | paintButton(graphicsContext, scrollbar, backButtonStartPaintRect, BackButtonStartPart); |
|---|
| 451 | if (scrollMask & BackButtonEndPart) |
|---|
| 452 | paintButton(graphicsContext, scrollbar, backButtonEndPaintRect, BackButtonEndPart); |
|---|
| 453 | if (scrollMask & ForwardButtonStartPart) |
|---|
| 454 | paintButton(graphicsContext, scrollbar, forwardButtonStartPaintRect, ForwardButtonStartPart); |
|---|
| 455 | if (scrollMask & ForwardButtonEndPart) |
|---|
| 456 | paintButton(graphicsContext, scrollbar, forwardButtonEndPaintRect, ForwardButtonEndPart); |
|---|
| 457 | |
|---|
| 458 | // Paint the thumb. |
|---|
| 459 | if (scrollMask & ThumbPart) |
|---|
| 460 | paintThumb(graphicsContext, scrollbar, currentThumbRect); |
|---|
| 461 | |
|---|
| 462 | return true; |
|---|
| 463 | } |
|---|
| 464 | |
|---|
| 465 | bool ScrollbarThemeGtk::shouldCenterOnThumb(Scrollbar&, const PlatformMouseEvent& event) |
|---|
| 466 | { |
|---|
| 467 | return (event.shiftKey() && event.button() == LeftButton) || (event.button() == MiddleButton); |
|---|
| 468 | } |
|---|
| 469 | |
|---|
| 470 | int ScrollbarThemeGtk::scrollbarThickness(ScrollbarControlSize) |
|---|
| 471 | { |
|---|
| 472 | return m_thumbFatness + (m_troughBorderWidth * 2); |
|---|
| 473 | } |
|---|
| 474 | |
|---|
| 475 | IntSize ScrollbarThemeGtk::buttonSize(Scrollbar& scrollbar) |
|---|
| 476 | { |
|---|
| 477 | if (scrollbar.orientation() == VerticalScrollbar) |
|---|
| 478 | return IntSize(m_thumbFatness, m_stepperSize); |
|---|
| 479 | |
|---|
| 480 | // HorizontalScrollbar |
|---|
| 481 | return IntSize(m_stepperSize, m_thumbFatness); |
|---|
| 482 | } |
|---|
| 483 | |
|---|
| 484 | int ScrollbarThemeGtk::minimumThumbLength(Scrollbar&) |
|---|
| 485 | { |
|---|
| 486 | return m_minThumbLength; |
|---|
| 487 | } |
|---|
| 488 | #endif // GTK_API_VERSION_2 |
|---|
| 489 | |
|---|
| 490 | } |
|---|