1/*
2 * Copyright (C) 2017 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 "RenderThemeWidget.h"
28
29#if GTK_CHECK_VERSION(3, 20, 0)
30
31#include <wtf/HashMap.h>
32#include <wtf/NeverDestroyed.h>
33
34namespace WebCore {
35
36static HashMap<unsigned, std::unique_ptr<RenderThemeWidget>>& widgetMap()
37{
38 static NeverDestroyed<HashMap<unsigned, std::unique_ptr<RenderThemeWidget>>> map;
39 return map;
40}
41
42RenderThemeWidget& RenderThemeWidget::getOrCreate(Type widgetType)
43{
44 auto addResult = widgetMap().ensure(static_cast<unsigned>(widgetType), [widgetType]() -> std::unique_ptr<RenderThemeWidget> {
45 switch (widgetType) {
46 case RenderThemeWidget::Type::VerticalScrollbarRight:
47 return std::make_unique<RenderThemeScrollbar>(GTK_ORIENTATION_VERTICAL, RenderThemeScrollbar::Mode::Full);
48 case RenderThemeWidget::Type::VerticalScrollbarLeft:
49 return std::make_unique<RenderThemeScrollbar>(GTK_ORIENTATION_VERTICAL, RenderThemeScrollbar::Mode::Full, RenderThemeScrollbar::VerticalPosition::Left);
50 case RenderThemeWidget::Type::HorizontalScrollbar:
51 return std::make_unique<RenderThemeScrollbar>(GTK_ORIENTATION_HORIZONTAL, RenderThemeScrollbar::Mode::Full);
52 case RenderThemeWidget::Type::VerticalScrollIndicatorRight:
53 return std::make_unique<RenderThemeScrollbar>(GTK_ORIENTATION_VERTICAL, RenderThemeScrollbar::Mode::Indicator);
54 case RenderThemeWidget::Type::VerticalScrollIndicatorLeft:
55 return std::make_unique<RenderThemeScrollbar>(GTK_ORIENTATION_VERTICAL, RenderThemeScrollbar::Mode::Indicator, RenderThemeScrollbar::VerticalPosition::Left);
56 case RenderThemeWidget::Type::HorizontalScrollIndicator:
57 return std::make_unique<RenderThemeScrollbar>(GTK_ORIENTATION_HORIZONTAL, RenderThemeScrollbar::Mode::Indicator);
58 case RenderThemeWidget::Type::CheckButton:
59 return std::make_unique<RenderThemeToggleButton>(RenderThemeToggleButton::Type::Check);
60 case RenderThemeWidget::Type::RadioButton:
61 return std::make_unique<RenderThemeToggleButton>(RenderThemeToggleButton::Type::Radio);
62 case RenderThemeWidget::Type::Button:
63 return std::make_unique<RenderThemeButton>(RenderThemeButton::Default::No);
64 case RenderThemeWidget::Type::ButtonDefault:
65 return std::make_unique<RenderThemeButton>(RenderThemeButton::Default::Yes);
66 case RenderThemeWidget::Type::ComboBox:
67 return std::make_unique<RenderThemeComboBox>();
68 case RenderThemeWidget::Type::Entry:
69 return std::make_unique<RenderThemeEntry>();
70 case RenderThemeWidget::Type::SelectedEntry:
71 return std::make_unique<RenderThemeEntry>(RenderThemeEntry::Selected::Yes);
72 case RenderThemeWidget::Type::SearchEntry:
73 return std::make_unique<RenderThemeSearchEntry>();
74 case RenderThemeWidget::Type::SpinButton:
75 return std::make_unique<RenderThemeSpinButton>();
76 case RenderThemeWidget::Type::VerticalSlider:
77 return std::make_unique<RenderThemeSlider>(GTK_ORIENTATION_VERTICAL);
78 case RenderThemeWidget::Type::HorizontalSlider:
79 return std::make_unique<RenderThemeSlider>(GTK_ORIENTATION_HORIZONTAL);
80 case RenderThemeWidget::Type::ProgressBar:
81 return std::make_unique<RenderThemeProgressBar>(RenderThemeProgressBar::Mode::Determinate);
82 case RenderThemeWidget::Type::IndeterminateProgressBar:
83 return std::make_unique<RenderThemeProgressBar>(RenderThemeProgressBar::Mode::Indeterminate);
84 case RenderThemeWidget::Type::ListView:
85 return std::make_unique<RenderThemeListView>();
86 case RenderThemeWidget::Type::Icon:
87 return std::make_unique<RenderThemeIcon>();
88 case RenderThemeWidget::Type::Window:
89 return std::make_unique<RenderThemeWindow>();
90 }
91 ASSERT_NOT_REACHED();
92 return nullptr;
93 });
94 return *addResult.iterator->value;
95}
96
97void RenderThemeWidget::clearCache()
98{
99 widgetMap().clear();
100}
101
102RenderThemeWidget::~RenderThemeWidget() = default;
103
104RenderThemeScrollbar::RenderThemeScrollbar(GtkOrientation orientation, Mode mode, VerticalPosition verticalPosition)
105{
106 RenderThemeGadget::Info info = { RenderThemeGadget::Type::Scrollbar, "scrollbar", { } };
107 if (orientation == GTK_ORIENTATION_VERTICAL) {
108 info.classList.append("vertical");
109 info.classList.append(verticalPosition == VerticalPosition::Right ? "right" : "left");
110 } else {
111 info.classList.append("horizontal");
112 info.classList.append("bottom");
113 }
114 static bool usesOverlayScrollbars = g_strcmp0(g_getenv("GTK_OVERLAY_SCROLLING"), "0");
115 if (usesOverlayScrollbars)
116 info.classList.append("overlay-indicator");
117 if (mode == Mode::Full)
118 info.classList.append("hovering");
119 m_scrollbar = RenderThemeGadget::create(info);
120
121 Vector<RenderThemeGadget::Info> children;
122 auto steppers = static_cast<RenderThemeScrollbarGadget*>(m_scrollbar.get())->steppers();
123 if (steppers.contains(RenderThemeScrollbarGadget::Steppers::Backward)) {
124 m_steppersPosition[0] = children.size();
125 children.append({ RenderThemeGadget::Type::Generic, "button", { "up" } });
126 }
127 if (steppers.contains(RenderThemeScrollbarGadget::Steppers::SecondaryForward)) {
128 m_steppersPosition[1] = children.size();
129 children.append({ RenderThemeGadget::Type::Generic, "button", { "down" } });
130 }
131 m_troughPosition = children.size();
132 children.append({ RenderThemeGadget::Type::Generic, "trough", { } });
133 if (steppers.contains(RenderThemeScrollbarGadget::Steppers::SecondaryBackward)) {
134 m_steppersPosition[2] = children.size();
135 children.append({ RenderThemeGadget::Type::Generic, "button", { "up" } });
136 }
137 if (steppers.contains(RenderThemeScrollbarGadget::Steppers::Forward)) {
138 m_steppersPosition[3] = children.size();
139 children.append({ RenderThemeGadget::Type::Generic, "button", { "down" } });
140 }
141 info.type = RenderThemeGadget::Type::Generic;
142 info.name = "contents";
143 info.classList.clear();
144 m_contents = std::make_unique<RenderThemeBoxGadget>(info, GTK_ORIENTATION_VERTICAL, children, m_scrollbar.get());
145 info.name = "slider";
146 m_slider = RenderThemeGadget::create(info, m_contents->child(m_troughPosition));
147}
148
149RenderThemeGadget* RenderThemeScrollbar::stepper(RenderThemeScrollbarGadget::Steppers scrollbarStepper)
150{
151 if (!static_cast<RenderThemeScrollbarGadget*>(m_scrollbar.get())->steppers().contains(scrollbarStepper))
152 return nullptr;
153
154 switch (scrollbarStepper) {
155 case RenderThemeScrollbarGadget::Steppers::Backward:
156 return m_contents->child(m_steppersPosition[0]);
157 case RenderThemeScrollbarGadget::Steppers::SecondaryForward:
158 return m_contents->child(m_steppersPosition[1]);
159 case RenderThemeScrollbarGadget::Steppers::SecondaryBackward:
160 return m_contents->child(m_steppersPosition[2]);
161 case RenderThemeScrollbarGadget::Steppers::Forward:
162 return m_contents->child(m_steppersPosition[3]);
163 default:
164 break;
165 }
166
167 return nullptr;
168}
169
170RenderThemeToggleButton::RenderThemeToggleButton(Type toggleType)
171{
172 RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, toggleType == Type::Check ? "checkbutton" : "radiobutton", { "text-button" } };
173 m_button = RenderThemeGadget::create(info);
174 if (toggleType == Type::Check) {
175 info.type = RenderThemeGadget::Type::Check;
176 info.name = "check";
177 } else {
178 info.type = RenderThemeGadget::Type::Radio;
179 info.name = "radio";
180 }
181 info.classList.clear();
182 m_toggle = RenderThemeGadget::create(info, m_button.get());
183}
184
185RenderThemeButton::RenderThemeButton(Default isDefault)
186{
187 RenderThemeGadget::Info info = { RenderThemeGadget::Type::Button, "button", { "text-button" } };
188 if (isDefault == Default::Yes)
189 info.classList.append("default");
190 m_button = RenderThemeGadget::create(info);
191}
192
193RenderThemeComboBox::RenderThemeComboBox()
194{
195 RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "combobox", { } };
196 m_comboBox = RenderThemeGadget::create(info);
197 Vector<RenderThemeGadget::Info> children = {
198 { RenderThemeGadget::Type::Generic, "button", { "combo" } }
199 };
200 info.name = "box";
201 info.classList = { "horizontal", "linked" };
202 m_box = std::make_unique<RenderThemeBoxGadget>(info, GTK_ORIENTATION_HORIZONTAL, children, m_comboBox.get());
203 RenderThemeGadget* button = m_box->child(0);
204 info.classList.removeLast();
205 m_buttonBox = RenderThemeGadget::create(info, button);
206 info.type = RenderThemeGadget::Type::Arrow;
207 info.name = "arrow";
208 info.classList = { };
209 m_arrow = RenderThemeGadget::create(info, m_buttonBox.get());
210}
211
212RenderThemeEntry::RenderThemeEntry(Selected isSelected)
213{
214 RenderThemeGadget::Info info = { RenderThemeGadget::Type::TextField, "entry", { } };
215 m_entry = RenderThemeGadget::create(info);
216 if (isSelected == Selected::Yes) {
217 info.type = RenderThemeGadget::Type::Generic;
218 info.name = "selection";
219 m_selection = RenderThemeGadget::create(info, m_entry.get());
220 }
221}
222
223RenderThemeSearchEntry::RenderThemeSearchEntry()
224{
225 RenderThemeGadget::Info info = { RenderThemeGadget::Type::Icon, "image", { "left" } };
226 m_leftIcon = RenderThemeGadget::create(info, m_entry.get());
227 static_cast<RenderThemeIconGadget*>(m_leftIcon.get())->setIconName("edit-find-symbolic");
228 info.classList.clear();
229 info.classList.append("right");
230 m_rightIcon = RenderThemeGadget::create(info, m_entry.get());
231 static_cast<RenderThemeIconGadget*>(m_rightIcon.get())->setIconName("edit-clear-symbolic");
232}
233
234RenderThemeSpinButton::RenderThemeSpinButton()
235{
236 RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "spinbutton", { "horizontal" } };
237 m_spinButton = RenderThemeGadget::create(info);
238 info.type = RenderThemeGadget::Type::TextField;
239 info.name = "entry";
240 info.classList.clear();
241 m_entry = RenderThemeGadget::create(info, m_spinButton.get());
242 info.type = RenderThemeGadget::Type::Icon;
243 info.name = "button";
244 info.classList.append("up");
245 m_up = RenderThemeGadget::create(info, m_spinButton.get());
246 auto* upIcon = static_cast<RenderThemeIconGadget*>(m_up.get());
247 upIcon->setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
248 upIcon->setIconName("list-add-symbolic");
249 info.classList[0] = "down";
250 m_down = RenderThemeGadget::create(info, m_spinButton.get());
251 auto* downIcon = static_cast<RenderThemeIconGadget*>(m_down.get());
252 downIcon->setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
253 downIcon->setIconName("list-remove-symbolic");
254}
255
256RenderThemeSlider::RenderThemeSlider(GtkOrientation orientation)
257{
258 RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "scale", { } };
259 if (orientation == GTK_ORIENTATION_VERTICAL)
260 info.classList.append("vertical");
261 else
262 info.classList.append("horizontal");
263 m_scale = RenderThemeGadget::create(info);
264 info.name = "contents";
265 info.classList.clear();
266 m_contents = RenderThemeGadget::create(info, m_scale.get());
267 info.name = "trough";
268 m_trough = RenderThemeGadget::create(info, m_contents.get());
269 info.name = "slider";
270 m_slider = RenderThemeGadget::create(info, m_trough.get());
271 info.name = "highlight";
272 m_highlight = RenderThemeGadget::create(info, m_trough.get());
273}
274
275RenderThemeProgressBar::RenderThemeProgressBar(Mode mode)
276{
277 RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "progressbar", { "horizontal" } };
278 m_progressBar = RenderThemeGadget::create(info);
279 info.name = "trough";
280 info.classList.clear();
281 m_trough = RenderThemeGadget::create(info, m_progressBar.get());
282 info.name = "progress";
283 if (mode == Mode::Determinate)
284 info.classList.append("pulse");
285 m_progress = RenderThemeGadget::create(info, m_trough.get());
286}
287
288RenderThemeListView::RenderThemeListView()
289 : m_treeview(RenderThemeGadget::create({ RenderThemeGadget::Type::Generic, "treeview", { "view" } }))
290{
291}
292
293RenderThemeIcon::RenderThemeIcon()
294 : m_icon(RenderThemeGadget::create({ RenderThemeGadget::Type::Icon, "image", { } }))
295{
296}
297
298RenderThemeWindow::RenderThemeWindow()
299 : m_window(RenderThemeGadget::create({ RenderThemeGadget::Type::Generic, "window", { "background" } }))
300{
301}
302
303} // namepsace WebCore
304
305#endif // GTK_CHECK_VERSION(3, 20, 0)
306