CheckBoxPaintable.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  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/AntiAliasingPainter.h>
  9. #include <LibGfx/Bitmap.h>
  10. #include <LibGfx/GrayscaleBitmap.h>
  11. #include <LibWeb/HTML/BrowsingContext.h>
  12. #include <LibWeb/HTML/HTMLImageElement.h>
  13. #include <LibWeb/Layout/CheckBox.h>
  14. #include <LibWeb/Layout/Label.h>
  15. #include <LibWeb/Painting/CheckBoxPaintable.h>
  16. #include <LibWeb/Painting/InputColors.h>
  17. namespace Web::Painting {
  18. // A 16x16 signed distance field for the checkbox's tick (slightly rounded):
  19. static constexpr Array<u8, 16 * 16> s_check_mark_sdf {
  20. 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 251, 254, 254, 254,
  21. 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 231, 194, 189, 218, 254, 254,
  22. 254, 254, 254, 254, 254, 254, 254, 254, 254, 245, 193, 142, 131, 165, 205, 254,
  23. 254, 254, 254, 254, 254, 254, 254, 254, 254, 209, 156, 105, 78, 116, 174, 237,
  24. 254, 254, 254, 254, 254, 254, 254, 254, 226, 173, 120, 69, 79, 132, 185, 243,
  25. 254, 254, 254, 254, 254, 254, 254, 243, 190, 138, 85, 62, 115, 167, 219, 254,
  26. 254, 254, 227, 203, 212, 249, 254, 207, 154, 102, 50, 98, 149, 202, 254, 254,
  27. 254, 225, 180, 141, 159, 204, 224, 171, 119, 67, 81, 134, 186, 238, 254, 254,
  28. 243, 184, 135, 90, 113, 157, 188, 136, 84, 64, 116, 169, 221, 254, 254, 254,
  29. 237, 174, 118, 71, 68, 113, 153, 100, 48, 100, 152, 204, 254, 254, 254, 254,
  30. 254, 208, 162, 116, 71, 67, 107, 65, 83, 135, 187, 240, 254, 254, 254, 254,
  31. 254, 251, 206, 162, 116, 71, 43, 66, 119, 171, 223, 254, 254, 254, 254, 254,
  32. 254, 254, 251, 206, 162, 116, 73, 102, 154, 207, 254, 254, 254, 254, 254, 254,
  33. 254, 254, 254, 251, 206, 162, 124, 139, 190, 242, 254, 254, 254, 254, 254, 254,
  34. 254, 254, 254, 254, 251, 210, 187, 194, 229, 254, 254, 254, 254, 254, 254, 254,
  35. 254, 254, 254, 254, 254, 254, 251, 254, 254, 254, 254, 254, 254, 254, 254, 254
  36. };
  37. // A 16x16 signed distance field for an indeterminate checkbox (rounded line)
  38. // Note: We could use the AA fill_rect_with_rounded_corners() for this in future,
  39. // though right now it can't draw at subpixel accuracy (so is misaligned and jitters when scaling).
  40. static constexpr Array<u8, 16 * 16> s_check_indeterminate {
  41. 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
  42. 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
  43. 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
  44. 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
  45. 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
  46. 254, 254, 254, 239, 211, 209, 209, 209, 209, 209, 209, 211, 237, 254, 254, 254,
  47. 254, 254, 252, 195, 151, 145, 145, 145, 145, 145, 145, 150, 193, 250, 254, 254,
  48. 254, 254, 243, 179, 115, 81, 81, 81, 81, 81, 81, 113, 177, 241, 254, 254,
  49. 254, 254, 243, 179, 115, 79, 79, 79, 79, 79, 79, 113, 177, 241, 254, 254,
  50. 254, 254, 251, 194, 149, 143, 143, 143, 143, 143, 143, 148, 192, 250, 254, 254,
  51. 254, 254, 254, 237, 210, 207, 207, 207, 207, 207, 207, 209, 236, 254, 254, 254,
  52. 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
  53. 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
  54. 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
  55. 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
  56. 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254
  57. };
  58. static constexpr Gfx::GrayscaleBitmap check_mark_sdf()
  59. {
  60. return Gfx::GrayscaleBitmap(s_check_mark_sdf, 16, 16);
  61. }
  62. static constexpr Gfx::GrayscaleBitmap check_indeterminate_sdf()
  63. {
  64. return Gfx::GrayscaleBitmap(s_check_indeterminate, 16, 16);
  65. }
  66. JS::NonnullGCPtr<CheckBoxPaintable>
  67. CheckBoxPaintable::create(Layout::CheckBox const& layout_box)
  68. {
  69. return layout_box.heap().allocate_without_realm<CheckBoxPaintable>(layout_box);
  70. }
  71. CheckBoxPaintable::CheckBoxPaintable(Layout::CheckBox const& layout_box)
  72. : LabelablePaintable(layout_box)
  73. {
  74. }
  75. Layout::CheckBox const& CheckBoxPaintable::layout_box() const
  76. {
  77. return static_cast<Layout::CheckBox const&>(layout_node());
  78. }
  79. Layout::CheckBox& CheckBoxPaintable::layout_box()
  80. {
  81. return static_cast<Layout::CheckBox&>(layout_node());
  82. }
  83. void CheckBoxPaintable::paint(PaintContext& context, PaintPhase phase) const
  84. {
  85. if (!is_visible())
  86. return;
  87. PaintableBox::paint(context, phase);
  88. if (phase != PaintPhase::Foreground)
  89. return;
  90. auto const& checkbox = static_cast<HTML::HTMLInputElement const&>(layout_box().dom_node());
  91. bool enabled = layout_box().dom_node().enabled();
  92. auto checkbox_rect = context.enclosing_device_rect(absolute_rect()).to_type<int>();
  93. auto checkbox_radius = checkbox_rect.width() / 5;
  94. auto& palette = context.palette();
  95. auto shade = [&](Color color, float amount) {
  96. return InputColors::get_shade(color, amount, palette.is_dark());
  97. };
  98. auto modify_color = [&](Color color) {
  99. if (being_pressed() && enabled)
  100. return shade(color, 0.3f);
  101. return color;
  102. };
  103. auto input_colors = compute_input_colors(palette, computed_values().accent_color());
  104. auto increase_contrast = [&](Color color, Color background) {
  105. auto constexpr min_contrast = 2;
  106. if (color.contrast_ratio(background) < min_contrast) {
  107. color = color.inverted();
  108. if (color.contrast_ratio(background) > min_contrast)
  109. return color;
  110. }
  111. return color;
  112. };
  113. // Little heuristic that smaller things look better with more smoothness.
  114. float smoothness = 1.0f / (max(checkbox_rect.width(), checkbox_rect.height()) / 2);
  115. if (checkbox.checked() && !checkbox.indeterminate()) {
  116. auto background_color = enabled ? input_colors.accent : input_colors.mid_gray;
  117. context.recording_painter().fill_rect_with_rounded_corners(checkbox_rect, modify_color(background_color), checkbox_radius);
  118. auto tick_color = increase_contrast(input_colors.base, background_color);
  119. if (!enabled)
  120. tick_color = shade(tick_color, 0.5f);
  121. context.recording_painter().draw_signed_distance_field(checkbox_rect, tick_color, check_mark_sdf(), smoothness);
  122. } else {
  123. auto background_color = input_colors.background_color(enabled);
  124. auto border_thickness = max(1, checkbox_rect.width() / 10);
  125. context.recording_painter().fill_rect_with_rounded_corners(checkbox_rect, modify_color(input_colors.border_color(enabled)), checkbox_radius);
  126. context.recording_painter().fill_rect_with_rounded_corners(checkbox_rect.shrunken(border_thickness, border_thickness, border_thickness, border_thickness),
  127. background_color, max(0, checkbox_radius - border_thickness));
  128. if (checkbox.indeterminate()) {
  129. auto dash_color = increase_contrast(input_colors.dark_gray, background_color);
  130. context.recording_painter().draw_signed_distance_field(checkbox_rect,
  131. modify_color(enabled ? dash_color : shade(dash_color, 0.3f)), check_indeterminate_sdf(), smoothness);
  132. }
  133. }
  134. }
  135. }