CheckBoxPaintable.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. /*
  2. * Copyright (c) 2018-2022, Andreas Kling <andreas@ladybird.org>
  3. * Copyright (c) 2023, MacDue <macdue@dueutil.tech>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <LibGfx/AntiAliasingPainter.h>
  8. #include <LibGfx/Bitmap.h>
  9. #include <LibWeb/HTML/BrowsingContext.h>
  10. #include <LibWeb/HTML/HTMLImageElement.h>
  11. #include <LibWeb/HTML/HTMLInputElement.h>
  12. #include <LibWeb/Layout/CheckBox.h>
  13. #include <LibWeb/Layout/Label.h>
  14. #include <LibWeb/Painting/CheckBoxPaintable.h>
  15. #include <LibWeb/Painting/InputColors.h>
  16. namespace Web::Painting {
  17. JS_DEFINE_ALLOCATOR(CheckBoxPaintable);
  18. static Gfx::Path check_mark_path(Gfx::IntRect checkbox_rect)
  19. {
  20. Gfx::Path path;
  21. path.move_to({ 72, 14 });
  22. path.line_to({ 37, 64 });
  23. path.line_to({ 19, 47 });
  24. path.line_to({ 8, 58 });
  25. path.line_to({ 40, 89 });
  26. path.line_to({ 85, 24 });
  27. path.close();
  28. float const checkmark_width = 100;
  29. float const checkmark_height = 100;
  30. Gfx::AffineTransform scale_checkmark_to_fit;
  31. scale_checkmark_to_fit.scale(checkbox_rect.width() / checkmark_width, checkbox_rect.height() / checkmark_height);
  32. return path.copy_transformed(scale_checkmark_to_fit);
  33. }
  34. JS::NonnullGCPtr<CheckBoxPaintable>
  35. CheckBoxPaintable::create(Layout::CheckBox const& layout_box)
  36. {
  37. return layout_box.heap().allocate<CheckBoxPaintable>(layout_box);
  38. }
  39. CheckBoxPaintable::CheckBoxPaintable(Layout::CheckBox const& layout_box)
  40. : LabelablePaintable(layout_box)
  41. {
  42. }
  43. Layout::CheckBox const& CheckBoxPaintable::layout_box() const
  44. {
  45. return static_cast<Layout::CheckBox const&>(layout_node());
  46. }
  47. Layout::CheckBox& CheckBoxPaintable::layout_box()
  48. {
  49. return static_cast<Layout::CheckBox&>(layout_node());
  50. }
  51. void CheckBoxPaintable::paint(PaintContext& context, PaintPhase phase) const
  52. {
  53. if (!is_visible())
  54. return;
  55. PaintableBox::paint(context, phase);
  56. if (phase != PaintPhase::Foreground)
  57. return;
  58. auto const& checkbox = static_cast<HTML::HTMLInputElement const&>(layout_box().dom_node());
  59. bool enabled = layout_box().dom_node().enabled();
  60. auto checkbox_rect = context.enclosing_device_rect(absolute_rect()).to_type<int>();
  61. auto checkbox_radius = checkbox_rect.width() / 5;
  62. auto& palette = context.palette();
  63. auto shade = [&](Color color, float amount) {
  64. return InputColors::get_shade(color, amount, palette.is_dark());
  65. };
  66. auto modify_color = [&](Color color) {
  67. if (being_pressed() && enabled)
  68. return shade(color, 0.3f);
  69. return color;
  70. };
  71. auto input_colors = compute_input_colors(palette, computed_values().accent_color());
  72. auto increase_contrast = [&](Color color, Color background) {
  73. auto constexpr min_contrast = 2;
  74. if (color.contrast_ratio(background) < min_contrast) {
  75. color = color.inverted();
  76. if (color.contrast_ratio(background) > min_contrast)
  77. return color;
  78. }
  79. return color;
  80. };
  81. // Little heuristic that smaller things look better with more smoothness.
  82. if (checkbox.checked() && !checkbox.indeterminate()) {
  83. auto background_color = enabled ? input_colors.accent : input_colors.mid_gray;
  84. context.display_list_recorder().fill_rect_with_rounded_corners(checkbox_rect, modify_color(background_color), checkbox_radius);
  85. auto tick_color = increase_contrast(input_colors.base, background_color);
  86. if (!enabled)
  87. tick_color = shade(tick_color, 0.5f);
  88. context.display_list_recorder().fill_path({
  89. .path = check_mark_path(checkbox_rect),
  90. .color = tick_color,
  91. .translation = checkbox_rect.location().to_type<float>(),
  92. });
  93. } else {
  94. auto background_color = input_colors.background_color(enabled);
  95. auto border_thickness = max(1, checkbox_rect.width() / 10);
  96. context.display_list_recorder().fill_rect_with_rounded_corners(checkbox_rect, modify_color(input_colors.border_color(enabled)), checkbox_radius);
  97. context.display_list_recorder().fill_rect_with_rounded_corners(checkbox_rect.shrunken(border_thickness, border_thickness, border_thickness, border_thickness),
  98. background_color, max(0, checkbox_radius - border_thickness));
  99. if (checkbox.indeterminate()) {
  100. int radius = 0.05 * checkbox_rect.width();
  101. auto dash_color = increase_contrast(input_colors.dark_gray, background_color);
  102. auto dash_rect = checkbox_rect.inflated(-0.4 * checkbox_rect.width(), -0.8 * checkbox_rect.height());
  103. context.display_list_recorder().fill_rect_with_rounded_corners(dash_rect, dash_color, radius, radius, radius, radius);
  104. }
  105. }
  106. }
  107. }