| 
									
										
										
										
											2022-03-10 14:02:25 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (c) 2018-2022, Andreas Kling <kling@serenityos.org> | 
					
						
							| 
									
										
										
										
											2023-03-19 23:55:03 +00:00
										 |  |  |  * Copyright (c) 2023, MacDue <macdue@dueutil.tech> | 
					
						
							| 
									
										
										
										
											2022-03-10 14:02:25 +01:00
										 |  |  |  * | 
					
						
							|  |  |  |  * SPDX-License-Identifier: BSD-2-Clause | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 23:55:03 +00:00
										 |  |  | #include <LibGfx/AntiAliasingPainter.h>
 | 
					
						
							|  |  |  | #include <LibGfx/Bitmap.h>
 | 
					
						
							| 
									
										
										
										
											2022-03-10 22:46:35 +01:00
										 |  |  | #include <LibWeb/HTML/BrowsingContext.h>
 | 
					
						
							| 
									
										
										
										
											2022-03-10 14:02:25 +01:00
										 |  |  | #include <LibWeb/HTML/HTMLImageElement.h>
 | 
					
						
							| 
									
										
										
										
											2024-09-23 11:13:14 -04:00
										 |  |  | #include <LibWeb/HTML/HTMLInputElement.h>
 | 
					
						
							| 
									
										
										
										
											2022-03-10 14:02:25 +01:00
										 |  |  | #include <LibWeb/Layout/CheckBox.h>
 | 
					
						
							| 
									
										
										
										
											2022-03-10 22:46:35 +01:00
										 |  |  | #include <LibWeb/Layout/Label.h>
 | 
					
						
							| 
									
										
										
										
											2022-03-10 14:02:25 +01:00
										 |  |  | #include <LibWeb/Painting/CheckBoxPaintable.h>
 | 
					
						
							| 
									
										
										
										
											2023-03-23 22:51:22 +00:00
										 |  |  | #include <LibWeb/Painting/InputColors.h>
 | 
					
						
							| 
									
										
										
										
											2022-03-10 14:02:25 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace Web::Painting { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-06 10:16:04 -07:00
										 |  |  | JS_DEFINE_ALLOCATOR(CheckBoxPaintable); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-09 14:00:10 +02:00
										 |  |  | static Gfx::Path check_mark_path(Gfx::IntRect checkbox_rect) | 
					
						
							| 
									
										
										
										
											2023-03-19 23:55:03 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-08-09 14:00:10 +02:00
										 |  |  |     Gfx::Path path; | 
					
						
							| 
									
										
										
										
											2024-06-13 17:31:58 +03:00
										 |  |  |     path.move_to({ 72, 14 }); | 
					
						
							|  |  |  |     path.line_to({ 37, 64 }); | 
					
						
							|  |  |  |     path.line_to({ 19, 47 }); | 
					
						
							|  |  |  |     path.line_to({ 8, 58 }); | 
					
						
							|  |  |  |     path.line_to({ 40, 89 }); | 
					
						
							|  |  |  |     path.line_to({ 85, 24 }); | 
					
						
							|  |  |  |     path.close(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     float const checkmark_width = 100; | 
					
						
							|  |  |  |     float const checkmark_height = 100; | 
					
						
							|  |  |  |     Gfx::AffineTransform scale_checkmark_to_fit; | 
					
						
							|  |  |  |     scale_checkmark_to_fit.scale(checkbox_rect.width() / checkmark_width, checkbox_rect.height() / checkmark_height); | 
					
						
							|  |  |  |     return path.copy_transformed(scale_checkmark_to_fit); | 
					
						
							| 
									
										
										
										
											2023-03-19 23:55:03 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | JS::NonnullGCPtr<CheckBoxPaintable> | 
					
						
							|  |  |  | CheckBoxPaintable::create(Layout::CheckBox const& layout_box) | 
					
						
							| 
									
										
										
										
											2022-03-10 14:02:25 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-01-11 12:51:49 +01:00
										 |  |  |     return layout_box.heap().allocate_without_realm<CheckBoxPaintable>(layout_box); | 
					
						
							| 
									
										
										
										
											2022-03-10 14:02:25 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | CheckBoxPaintable::CheckBoxPaintable(Layout::CheckBox const& layout_box) | 
					
						
							| 
									
										
										
										
											2022-03-10 22:46:35 +01:00
										 |  |  |     : LabelablePaintable(layout_box) | 
					
						
							| 
									
										
										
										
											2022-03-10 14:02:25 +01:00
										 |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Layout::CheckBox const& CheckBoxPaintable::layout_box() const | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-03-10 15:50:57 +01:00
										 |  |  |     return static_cast<Layout::CheckBox const&>(layout_node()); | 
					
						
							| 
									
										
										
										
											2022-03-10 14:02:25 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-10 22:46:35 +01:00
										 |  |  | Layout::CheckBox& CheckBoxPaintable::layout_box() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return static_cast<Layout::CheckBox&>(layout_node()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-19 23:55:03 +00:00
										 |  |  | void CheckBoxPaintable::paint(PaintContext& context, PaintPhase phase) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!is_visible()) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     PaintableBox::paint(context, phase); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (phase != PaintPhase::Foreground) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto const& checkbox = static_cast<HTML::HTMLInputElement const&>(layout_box().dom_node()); | 
					
						
							|  |  |  |     bool enabled = layout_box().dom_node().enabled(); | 
					
						
							|  |  |  |     auto checkbox_rect = context.enclosing_device_rect(absolute_rect()).to_type<int>(); | 
					
						
							|  |  |  |     auto checkbox_radius = checkbox_rect.width() / 5; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto& palette = context.palette(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto shade = [&](Color color, float amount) { | 
					
						
							| 
									
										
										
										
											2023-03-23 22:51:22 +00:00
										 |  |  |         return InputColors::get_shade(color, amount, palette.is_dark()); | 
					
						
							| 
									
										
										
										
											2023-03-19 23:55:03 +00:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto modify_color = [&](Color color) { | 
					
						
							|  |  |  |         if (being_pressed() && enabled) | 
					
						
							|  |  |  |             return shade(color, 0.3f); | 
					
						
							|  |  |  |         return color; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-23 22:51:22 +00:00
										 |  |  |     auto input_colors = compute_input_colors(palette, computed_values().accent_color()); | 
					
						
							| 
									
										
										
										
											2023-03-19 23:55:03 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     auto increase_contrast = [&](Color color, Color background) { | 
					
						
							|  |  |  |         auto constexpr min_contrast = 2; | 
					
						
							|  |  |  |         if (color.contrast_ratio(background) < min_contrast) { | 
					
						
							|  |  |  |             color = color.inverted(); | 
					
						
							|  |  |  |             if (color.contrast_ratio(background) > min_contrast) | 
					
						
							|  |  |  |                 return color; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return color; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Little heuristic that smaller things look better with more smoothness.
 | 
					
						
							|  |  |  |     if (checkbox.checked() && !checkbox.indeterminate()) { | 
					
						
							| 
									
										
										
										
											2023-03-23 22:51:22 +00:00
										 |  |  |         auto background_color = enabled ? input_colors.accent : input_colors.mid_gray; | 
					
						
							| 
									
										
										
										
											2024-06-23 18:40:10 +02:00
										 |  |  |         context.display_list_recorder().fill_rect_with_rounded_corners(checkbox_rect, modify_color(background_color), checkbox_radius); | 
					
						
							| 
									
										
										
										
											2023-03-23 22:51:22 +00:00
										 |  |  |         auto tick_color = increase_contrast(input_colors.base, background_color); | 
					
						
							| 
									
										
										
										
											2023-03-19 23:55:03 +00:00
										 |  |  |         if (!enabled) | 
					
						
							|  |  |  |             tick_color = shade(tick_color, 0.5f); | 
					
						
							| 
									
										
										
										
											2024-06-23 18:40:10 +02:00
										 |  |  |         context.display_list_recorder().fill_path({ | 
					
						
							| 
									
										
										
										
											2024-06-13 17:31:58 +03:00
										 |  |  |             .path = check_mark_path(checkbox_rect), | 
					
						
							|  |  |  |             .color = tick_color, | 
					
						
							|  |  |  |             .translation = checkbox_rect.location().to_type<float>(), | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2023-03-19 23:55:03 +00:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2023-03-23 22:51:22 +00:00
										 |  |  |         auto background_color = input_colors.background_color(enabled); | 
					
						
							| 
									
										
										
										
											2023-03-19 23:55:03 +00:00
										 |  |  |         auto border_thickness = max(1, checkbox_rect.width() / 10); | 
					
						
							| 
									
										
										
										
											2024-06-23 18:40:10 +02:00
										 |  |  |         context.display_list_recorder().fill_rect_with_rounded_corners(checkbox_rect, modify_color(input_colors.border_color(enabled)), checkbox_radius); | 
					
						
							|  |  |  |         context.display_list_recorder().fill_rect_with_rounded_corners(checkbox_rect.shrunken(border_thickness, border_thickness, border_thickness, border_thickness), | 
					
						
							| 
									
										
										
										
											2023-03-23 22:51:22 +00:00
										 |  |  |             background_color, max(0, checkbox_radius - border_thickness)); | 
					
						
							| 
									
										
										
										
											2023-03-19 23:55:03 +00:00
										 |  |  |         if (checkbox.indeterminate()) { | 
					
						
							| 
									
										
										
										
											2024-06-13 17:31:58 +03:00
										 |  |  |             int radius = 0.05 * checkbox_rect.width(); | 
					
						
							| 
									
										
										
										
											2023-03-23 22:51:22 +00:00
										 |  |  |             auto dash_color = increase_contrast(input_colors.dark_gray, background_color); | 
					
						
							| 
									
										
										
										
											2024-06-13 17:31:58 +03:00
										 |  |  |             auto dash_rect = checkbox_rect.inflated(-0.4 * checkbox_rect.width(), -0.8 * checkbox_rect.height()); | 
					
						
							| 
									
										
										
										
											2024-06-23 18:40:10 +02:00
										 |  |  |             context.display_list_recorder().fill_rect_with_rounded_corners(dash_rect, dash_color, radius, radius, radius, radius); | 
					
						
							| 
									
										
										
										
											2023-03-19 23:55:03 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-10 14:02:25 +01:00
										 |  |  | } |