| 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 | |
| 34 | namespace WebCore { |
| 35 | |
| 36 | static HashMap<unsigned, std::unique_ptr<RenderThemeWidget>>& widgetMap() |
| 37 | { |
| 38 | static NeverDestroyed<HashMap<unsigned, std::unique_ptr<RenderThemeWidget>>> map; |
| 39 | return map; |
| 40 | } |
| 41 | |
| 42 | RenderThemeWidget& 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 | |
| 97 | void RenderThemeWidget::clearCache() |
| 98 | { |
| 99 | widgetMap().clear(); |
| 100 | } |
| 101 | |
| 102 | RenderThemeWidget::~RenderThemeWidget() = default; |
| 103 | |
| 104 | RenderThemeScrollbar::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 | |
| 149 | RenderThemeGadget* 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 | |
| 170 | RenderThemeToggleButton::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 | |
| 185 | RenderThemeButton::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 | |
| 193 | RenderThemeComboBox::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 | |
| 212 | RenderThemeEntry::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 | |
| 223 | RenderThemeSearchEntry::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 | |
| 234 | RenderThemeSpinButton::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 | |
| 256 | RenderThemeSlider::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 | |
| 275 | RenderThemeProgressBar::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 | |
| 288 | RenderThemeListView::RenderThemeListView() |
| 289 | : m_treeview(RenderThemeGadget::create({ RenderThemeGadget::Type::Generic, "treeview" , { "view" } })) |
| 290 | { |
| 291 | } |
| 292 | |
| 293 | RenderThemeIcon::RenderThemeIcon() |
| 294 | : m_icon(RenderThemeGadget::create({ RenderThemeGadget::Type::Icon, "image" , { } })) |
| 295 | { |
| 296 | } |
| 297 | |
| 298 | RenderThemeWindow::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 | |