RadioButtonPaintable.cpp 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. /*
  2. * Copyright (c) 2018-2022, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2023, MacDue <macdue@dueutil.tech>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <LibGUI/Event.h>
  8. #include <LibGfx/StylePainter.h>
  9. #include <LibWeb/DOM/Document.h>
  10. #include <LibWeb/HTML/BrowsingContext.h>
  11. #include <LibWeb/HTML/HTMLInputElement.h>
  12. #include <LibWeb/Layout/Label.h>
  13. #include <LibWeb/Layout/RadioButton.h>
  14. #include <LibWeb/Painting/InputColors.h>
  15. #include <LibWeb/Painting/RadioButtonPaintable.h>
  16. namespace Web::Painting {
  17. JS::NonnullGCPtr<RadioButtonPaintable> RadioButtonPaintable::create(Layout::RadioButton const& layout_box)
  18. {
  19. return layout_box.heap().allocate_without_realm<RadioButtonPaintable>(layout_box);
  20. }
  21. RadioButtonPaintable::RadioButtonPaintable(Layout::RadioButton const& layout_box)
  22. : LabelablePaintable(layout_box)
  23. {
  24. }
  25. void RadioButtonPaintable::paint(PaintContext& context, PaintPhase phase) const
  26. {
  27. if (!is_visible())
  28. return;
  29. PaintableBox::paint(context, phase);
  30. if (phase != PaintPhase::Foreground)
  31. return;
  32. Gfx::AntiAliasingPainter painter { context.painter() };
  33. auto draw_circle = [&](auto const& rect, Color color) {
  34. // Note: Doing this is a bit more forgiving than draw_circle() which will round to the nearset even radius.
  35. // This will fudge it (which works better here).
  36. painter.fill_rect_with_rounded_corners(rect, color, rect.width() / 2);
  37. };
  38. auto shrink_all = [&](auto const& rect, int amount) {
  39. return rect.shrunken(amount, amount, amount, amount);
  40. };
  41. auto const& radio_button = static_cast<HTML::HTMLInputElement const&>(layout_box().dom_node());
  42. auto& palette = context.palette();
  43. bool enabled = layout_box().dom_node().enabled();
  44. auto input_colors = compute_input_colors(palette, computed_values().accent_color());
  45. auto background_color = input_colors.background_color(enabled);
  46. auto accent = input_colors.accent;
  47. auto radio_color = [&] {
  48. if (radio_button.checked()) {
  49. // Handle the awkward case where a light color has been used for the accent color.
  50. if (accent.contrast_ratio(background_color) < 2 && accent.contrast_ratio(input_colors.dark_gray) > 2)
  51. background_color = input_colors.dark_gray;
  52. return accent;
  53. }
  54. return input_colors.gray;
  55. };
  56. auto fill_color = [&] {
  57. if (!enabled)
  58. return input_colors.mid_gray;
  59. auto color = radio_color();
  60. if (being_pressed())
  61. color = InputColors::get_shade(color, 0.3f, palette.is_dark());
  62. return color;
  63. }();
  64. // This is based on a 1px outer border and 2px inner border when drawn at 13x13.
  65. auto radio_button_rect = context.enclosing_device_rect(absolute_rect()).to_type<int>();
  66. auto outer_border_width = max(1, static_cast<int>(ceilf(radio_button_rect.width() / 13.0f)));
  67. auto inner_border_width = max(2, static_cast<int>(ceilf(radio_button_rect.width() / 4.0f)));
  68. draw_circle(radio_button_rect, fill_color);
  69. draw_circle(shrink_all(radio_button_rect, outer_border_width), background_color);
  70. if (radio_button.checked())
  71. draw_circle(shrink_all(radio_button_rect, inner_border_width), fill_color);
  72. }
  73. }