[WPE][GTK] Bump minimum versions of GLib, GTK, libsoup, ATK, GStreamer, and Cairo
[WebKit-https.git] / Source / WebCore / platform / gtk / RenderThemeGadget.cpp
1 /*
2  * Copyright (C) 2016 Igalia S.L.
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 "RenderThemeGadget.h"
28
29 #include "FloatRect.h"
30 #include "GRefPtrGtk.h"
31
32 namespace WebCore {
33
34 std::unique_ptr<RenderThemeGadget> RenderThemeGadget::create(const RenderThemeGadget::Info& info, RenderThemeGadget* parent, const Vector<RenderThemeGadget::Info> siblings, unsigned position)
35 {
36     switch (info.type) {
37     case RenderThemeGadget::Type::Generic:
38         return std::make_unique<RenderThemeGadget>(info, parent, siblings, position);
39     case RenderThemeGadget::Type::TextField:
40         return std::make_unique<RenderThemeTextFieldGadget>(info, parent, siblings, position);
41     case RenderThemeGadget::Type::Radio:
42     case RenderThemeGadget::Type::Check:
43         return std::make_unique<RenderThemeToggleGadget>(info, parent, siblings, position);
44     case RenderThemeGadget::Type::Arrow:
45         return std::make_unique<RenderThemeArrowGadget>(info, parent, siblings, position);
46     case RenderThemeGadget::Type::Icon:
47         return std::make_unique<RenderThemeIconGadget>(info, parent, siblings, position);
48     case RenderThemeGadget::Type::Scrollbar:
49         return std::make_unique<RenderThemeScrollbarGadget>(info, parent, siblings, position);
50     case RenderThemeGadget::Type::Button:
51         return std::make_unique<RenderThemeButtonGadget>(info, parent, siblings, position);
52     }
53
54     ASSERT_NOT_REACHED();
55     return nullptr;
56 }
57
58 static GRefPtr<GtkStyleContext> createStyleContext(GtkWidgetPath* path, GtkStyleContext* parent)
59 {
60     GRefPtr<GtkStyleContext> context = adoptGRef(gtk_style_context_new());
61     gtk_style_context_set_path(context.get(), path);
62     gtk_style_context_set_parent(context.get(), parent);
63     return context;
64 }
65
66 static void appendElementToPath(GtkWidgetPath* path, const RenderThemeGadget::Info& info)
67 {
68     // Scrollbars need to use its GType to be able to get non-CSS style properties.
69     gtk_widget_path_append_type(path, info.type == RenderThemeGadget::Type::Scrollbar ? GTK_TYPE_SCROLLBAR : G_TYPE_NONE);
70     gtk_widget_path_iter_set_object_name(path, -1, info.name);
71     for (const auto* className : info.classList)
72         gtk_widget_path_iter_add_class(path, -1, className);
73 }
74
75 RenderThemeGadget::RenderThemeGadget(const RenderThemeGadget::Info& info, RenderThemeGadget* parent, const Vector<RenderThemeGadget::Info> siblings, unsigned position)
76 {
77     GRefPtr<GtkWidgetPath> path = parent ? adoptGRef(gtk_widget_path_copy(gtk_style_context_get_path(parent->context()))) : adoptGRef(gtk_widget_path_new());
78     if (!siblings.isEmpty()) {
79         GRefPtr<GtkWidgetPath> siblingsPath = adoptGRef(gtk_widget_path_new());
80         for (const auto& siblingInfo : siblings)
81             appendElementToPath(siblingsPath.get(), siblingInfo);
82         gtk_widget_path_append_with_siblings(path.get(), siblingsPath.get(), position);
83     } else
84         appendElementToPath(path.get(), info);
85     m_context = createStyleContext(path.get(), parent ? parent->context() : nullptr);
86 }
87
88 RenderThemeGadget::~RenderThemeGadget() = default;
89
90 GtkBorder RenderThemeGadget::marginBox() const
91 {
92     GtkBorder returnValue;
93     gtk_style_context_get_margin(m_context.get(), gtk_style_context_get_state(m_context.get()), &returnValue);
94     return returnValue;
95 }
96
97 GtkBorder RenderThemeGadget::borderBox() const
98 {
99     GtkBorder returnValue;
100     gtk_style_context_get_border(m_context.get(), gtk_style_context_get_state(m_context.get()), &returnValue);
101     return returnValue;
102 }
103
104 GtkBorder RenderThemeGadget::paddingBox() const
105 {
106     GtkBorder returnValue;
107     gtk_style_context_get_padding(m_context.get(), gtk_style_context_get_state(m_context.get()), &returnValue);
108     return returnValue;
109 }
110
111 GtkBorder RenderThemeGadget::contentsBox() const
112 {
113     auto margin = marginBox();
114     auto border = borderBox();
115     auto padding = paddingBox();
116     padding.left += margin.left + border.left;
117     padding.right += margin.right + border.right;
118     padding.top += margin.top + border.top;
119     padding.bottom += margin.bottom + border.bottom;
120     return padding;
121 }
122
123 Color RenderThemeGadget::color() const
124 {
125     GdkRGBA returnValue;
126     gtk_style_context_get_color(m_context.get(), gtk_style_context_get_state(m_context.get()), &returnValue);
127     return returnValue;
128 }
129
130 Color RenderThemeGadget::backgroundColor() const
131 {
132     GdkRGBA returnValue;
133     gtk_style_context_get_background_color(m_context.get(), gtk_style_context_get_state(m_context.get()), &returnValue);
134     return returnValue;
135 }
136
137 double RenderThemeGadget::opacity() const
138 {
139     double returnValue;
140     gtk_style_context_get(m_context.get(), gtk_style_context_get_state(m_context.get()), "opacity", &returnValue, nullptr);
141     return returnValue;
142 }
143
144 GtkStateFlags RenderThemeGadget::state() const
145 {
146     return gtk_style_context_get_state(m_context.get());
147 }
148
149 void RenderThemeGadget::setState(GtkStateFlags state)
150 {
151     gtk_style_context_set_state(m_context.get(), state);
152 }
153
154 IntSize RenderThemeGadget::minimumSize() const
155 {
156     int width, height;
157     gtk_style_context_get(m_context.get(), gtk_style_context_get_state(m_context.get()), "min-width", &width, "min-height", &height, nullptr);
158     return IntSize(width, height);
159 }
160
161 IntSize RenderThemeGadget::preferredSize() const
162 {
163     auto margin = marginBox();
164     auto border = borderBox();
165     auto padding = paddingBox();
166     auto minSize = minimumSize();
167     minSize.expand(margin.left + margin.right + border.left + border.right + padding.left + padding.right,
168         margin.top + margin.bottom + border.top + border.bottom + padding.top + padding.bottom);
169     return minSize;
170 }
171
172 bool RenderThemeGadget::render(cairo_t* cr, const FloatRect& paintRect, FloatRect* contentsRect)
173 {
174     FloatRect rect = paintRect;
175
176     auto margin = marginBox();
177     rect.move(margin.left, margin.top);
178     rect.contract(margin.left + margin.right, margin.top + margin.bottom);
179
180     auto minSize = minimumSize();
181     rect.setWidth(std::max<float>(rect.width(), minSize.width()));
182     rect.setHeight(std::max<float>(rect.height(), minSize.height()));
183
184     gtk_render_background(m_context.get(), cr, rect.x(), rect.y(), rect.width(), rect.height());
185     gtk_render_frame(m_context.get(), cr, rect.x(), rect.y(), rect.width(), rect.height());
186
187     if (contentsRect) {
188         auto border = borderBox();
189         auto padding = paddingBox();
190         *contentsRect = rect;
191         contentsRect->move(border.left + padding.left, border.top + padding.top);
192         contentsRect->contract(border.left + border.right + padding.left + padding.right, border.top + border.bottom + padding.top + padding.bottom);
193     }
194
195     return true;
196 }
197
198 void RenderThemeGadget::renderFocus(cairo_t* cr, const FloatRect& focusRect)
199 {
200     FloatRect rect = focusRect;
201     auto margin = marginBox();
202     rect.move(margin.left, margin.top);
203     rect.contract(margin.left + margin.right, margin.top + margin.bottom);
204     gtk_render_focus(m_context.get(), cr, rect.x(), rect.y(), rect.width(), rect.height());
205 }
206
207 RenderThemeBoxGadget::RenderThemeBoxGadget(const RenderThemeGadget::Info& info, GtkOrientation orientation, const Vector<RenderThemeGadget::Info> children, RenderThemeGadget* parent)
208     : RenderThemeGadget(info, parent, Vector<RenderThemeGadget::Info>(), 0)
209     , m_orientation(orientation)
210 {
211     m_children.reserveCapacity(children.size());
212     unsigned index = 0;
213     for (const auto& childInfo : children)
214         m_children.uncheckedAppend(RenderThemeGadget::create(childInfo, this, children, index++));
215 }
216
217 IntSize RenderThemeBoxGadget::preferredSize() const
218 {
219     IntSize childrenSize;
220     for (const auto& child : m_children) {
221         IntSize childSize = child->preferredSize();
222         switch (m_orientation) {
223         case GTK_ORIENTATION_HORIZONTAL:
224             childrenSize.setWidth(childrenSize.width() + childSize.width());
225             childrenSize.setHeight(std::max(childrenSize.height(), childSize.height()));
226             break;
227         case GTK_ORIENTATION_VERTICAL:
228             childrenSize.setWidth(std::max(childrenSize.width(), childSize.width()));
229             childrenSize.setHeight(childrenSize.height() + childSize.height());
230             break;
231         }
232     }
233     return RenderThemeGadget::preferredSize().expandedTo(childrenSize);
234 }
235
236 RenderThemeTextFieldGadget::RenderThemeTextFieldGadget(const RenderThemeGadget::Info& info, RenderThemeGadget* parent, const Vector<RenderThemeGadget::Info> siblings, unsigned position)
237     : RenderThemeGadget(info, parent, siblings, position)
238 {
239 }
240
241 IntSize RenderThemeTextFieldGadget::minimumSize() const
242 {
243     // We allow text fields smaller than the min size set on themes.
244     return IntSize();
245 }
246
247 RenderThemeToggleGadget::RenderThemeToggleGadget(const RenderThemeGadget::Info& info, RenderThemeGadget* parent, const Vector<RenderThemeGadget::Info> siblings, unsigned position)
248     : RenderThemeGadget(info, parent, siblings, position)
249     , m_type(info.type)
250 {
251     ASSERT(m_type == RenderThemeGadget::Type::Radio || m_type == RenderThemeGadget::Type::Check);
252 }
253
254 bool RenderThemeToggleGadget::render(cairo_t* cr, const FloatRect& paintRect, FloatRect*)
255 {
256     FloatRect contentsRect;
257     RenderThemeGadget::render(cr, paintRect, &contentsRect);
258     if (m_type == RenderThemeGadget::Type::Radio)
259         gtk_render_option(m_context.get(), cr, contentsRect.x(), contentsRect.y(), contentsRect.width(), contentsRect.height());
260     else
261         gtk_render_check(m_context.get(), cr, contentsRect.x(), contentsRect.y(), contentsRect.width(), contentsRect.height());
262     return true;
263 }
264
265 RenderThemeArrowGadget::RenderThemeArrowGadget(const RenderThemeGadget::Info& info, RenderThemeGadget* parent, const Vector<RenderThemeGadget::Info> siblings, unsigned position)
266     : RenderThemeGadget(info, parent, siblings, position)
267 {
268 }
269
270 bool RenderThemeArrowGadget::render(cairo_t* cr, const FloatRect& paintRect, FloatRect*)
271 {
272     FloatRect contentsRect;
273     RenderThemeGadget::render(cr, paintRect, &contentsRect);
274     IntSize minSize = minimumSize();
275     int arrowSize = std::min(minSize.width(), minSize.height());
276     FloatPoint arrowPosition(contentsRect.x(), contentsRect.y() + (contentsRect.height() - arrowSize) / 2);
277     if (gtk_style_context_get_state(m_context.get()) & GTK_STATE_FLAG_DIR_LTR)
278         arrowPosition.move(contentsRect.width() - arrowSize, 0);
279     gtk_render_arrow(m_context.get(), cr, G_PI / 2, arrowPosition.x(), arrowPosition.y(), arrowSize);
280     return true;
281 }
282
283 RenderThemeIconGadget::RenderThemeIconGadget(const RenderThemeGadget::Info& info, RenderThemeGadget* parent, const Vector<RenderThemeGadget::Info> siblings, unsigned position)
284     : RenderThemeGadget(info, parent, siblings, position)
285 {
286 }
287
288 GtkIconSize RenderThemeIconGadget::gtkIconSizeForPixelSize(unsigned pixelSize) const
289 {
290     if (pixelSize < IconSizeGtk::SmallToolbar)
291         return GTK_ICON_SIZE_MENU;
292     if (pixelSize >= IconSizeGtk::SmallToolbar && pixelSize < IconSizeGtk::Button)
293         return GTK_ICON_SIZE_SMALL_TOOLBAR;
294     if (pixelSize >= IconSizeGtk::Button && pixelSize < IconSizeGtk::LargeToolbar)
295         return GTK_ICON_SIZE_BUTTON;
296     if (pixelSize >= IconSizeGtk::LargeToolbar && pixelSize < IconSizeGtk::DragAndDrop)
297         return GTK_ICON_SIZE_LARGE_TOOLBAR;
298     if (pixelSize >= IconSizeGtk::DragAndDrop && pixelSize < IconSizeGtk::Dialog)
299         return GTK_ICON_SIZE_DND;
300
301     return GTK_ICON_SIZE_DIALOG;
302 }
303
304 bool RenderThemeIconGadget::render(cairo_t* cr, const FloatRect& paintRect, FloatRect*)
305 {
306     ASSERT(!m_iconName.isNull());
307     GRefPtr<GIcon> icon = adoptGRef(g_themed_icon_new(m_iconName.data()));
308     unsigned lookupFlags = GTK_ICON_LOOKUP_USE_BUILTIN | GTK_ICON_LOOKUP_FORCE_SIZE | GTK_ICON_LOOKUP_FORCE_SVG;
309     GtkTextDirection direction = gtk_style_context_get_direction(m_context.get());
310     if (direction & GTK_TEXT_DIR_LTR)
311         lookupFlags |= GTK_ICON_LOOKUP_DIR_LTR;
312     else if (direction & GTK_TEXT_DIR_RTL)
313         lookupFlags |= GTK_ICON_LOOKUP_DIR_RTL;
314     int iconWidth, iconHeight;
315     if (!gtk_icon_size_lookup(gtkIconSizeForPixelSize(m_iconSize), &iconWidth, &iconHeight))
316         iconWidth = iconHeight = m_iconSize;
317     GRefPtr<GtkIconInfo> iconInfo = adoptGRef(gtk_icon_theme_lookup_by_gicon(gtk_icon_theme_get_default(), icon.get(),
318         std::min(iconWidth, iconHeight), static_cast<GtkIconLookupFlags>(lookupFlags)));
319     if (!iconInfo)
320         return false;
321
322     GRefPtr<GdkPixbuf> iconPixbuf = adoptGRef(gtk_icon_info_load_symbolic_for_context(iconInfo.get(), m_context.get(), nullptr, nullptr));
323     if (!iconPixbuf)
324         return false;
325
326     FloatSize pixbufSize(gdk_pixbuf_get_width(iconPixbuf.get()), gdk_pixbuf_get_height(iconPixbuf.get()));
327     FloatRect contentsRect;
328     RenderThemeGadget::render(cr, paintRect, &contentsRect);
329     if (pixbufSize.width() > contentsRect.width() || pixbufSize.height() > contentsRect.height()) {
330         iconWidth = iconHeight = std::min(contentsRect.width(), contentsRect.height());
331         pixbufSize = FloatSize(iconWidth, iconHeight);
332         iconPixbuf = adoptGRef(gdk_pixbuf_scale_simple(iconPixbuf.get(), pixbufSize.width(), pixbufSize.height(), GDK_INTERP_BILINEAR));
333     }
334
335     gtk_render_icon(m_context.get(), cr, iconPixbuf.get(), contentsRect.x() + (contentsRect.width() - pixbufSize.width()) / 2,
336         contentsRect.y() + (contentsRect.height() - pixbufSize.height()) / 2);
337     return true;
338 }
339
340 IntSize RenderThemeIconGadget::minimumSize() const
341 {
342     if (m_iconSize < IconSizeGtk::Menu)
343         return IntSize(m_iconSize, m_iconSize);
344
345     int iconWidth, iconHeight;
346     if (gtk_icon_size_lookup(gtkIconSizeForPixelSize(m_iconSize), &iconWidth, &iconHeight))
347         return IntSize(iconWidth, iconHeight);
348
349     return IntSize(m_iconSize, m_iconSize);
350 }
351
352 RenderThemeScrollbarGadget::RenderThemeScrollbarGadget(const RenderThemeGadget::Info& info, RenderThemeGadget* parent, const Vector<RenderThemeGadget::Info> siblings, unsigned position)
353     : RenderThemeGadget(info, parent, siblings, position)
354 {
355     gboolean hasBackward, hasForward, hasSecondaryBackward, hasSecondaryForward;
356     gtk_style_context_get_style(m_context.get(), "has-backward-stepper", &hasBackward, "has-forward-stepper", &hasForward,
357         "has-secondary-backward-stepper", &hasSecondaryBackward, "has-secondary-forward-stepper", &hasSecondaryForward, nullptr);
358     if (hasBackward)
359         m_steppers.add(Steppers::Backward);
360     if (hasForward)
361         m_steppers.add(Steppers::Forward);
362     if (hasSecondaryBackward)
363         m_steppers.add(Steppers::SecondaryBackward);
364     if (hasSecondaryForward)
365         m_steppers.add(Steppers::SecondaryForward);
366 }
367
368 void RenderThemeScrollbarGadget::renderStepper(cairo_t* cr, const FloatRect& paintRect, RenderThemeGadget* stepperGadget, GtkOrientation orientation, Steppers stepper)
369 {
370     FloatRect contentsRect;
371     stepperGadget->render(cr, paintRect, &contentsRect);
372     double angle;
373     switch (stepper) {
374     case Steppers::Backward:
375     case Steppers::SecondaryBackward:
376         angle = orientation == GTK_ORIENTATION_VERTICAL ? 0 : 3 * (G_PI / 2);
377         break;
378     case Steppers::Forward:
379     case Steppers::SecondaryForward:
380         angle = orientation == GTK_ORIENTATION_VERTICAL ? G_PI / 2 : G_PI;
381         break;
382     }
383
384     int stepperSize = std::max(contentsRect.width(), contentsRect.height());
385     gtk_render_arrow(stepperGadget->context(), cr, angle, contentsRect.x() + (contentsRect.width() - stepperSize) / 2,
386         contentsRect.y() + (contentsRect.height() - stepperSize) / 2, stepperSize);
387 }
388
389 RenderThemeButtonGadget::RenderThemeButtonGadget(const Info& info, RenderThemeGadget* parent, const Vector<RenderThemeGadget::Info> siblings, unsigned position)
390     : RenderThemeGadget(info, parent, siblings, position)
391 {
392 }
393
394 IntSize RenderThemeButtonGadget::minimumSize() const
395 {
396     // Allow buttons to be smaller than the minimum size
397     return IntSize();
398 }
399
400 } // namespace WebCore