From dd77b878b307b51cdf24712dc94dae2c02e335aa Mon Sep 17 00:00:00 2001 From: MacDue Date: Thu, 23 Mar 2023 22:54:43 +0000 Subject: [PATCH] LibWeb: Add scalable radio buttons (with theme/accent-color support) These are similar to the checkboxes now (though no SDFs here all just plain AA painter). --- .../LibWeb/Painting/RadioButtonPaintable.cpp | 57 ++++++++++++++++++- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/Userland/Libraries/LibWeb/Painting/RadioButtonPaintable.cpp b/Userland/Libraries/LibWeb/Painting/RadioButtonPaintable.cpp index 1aef4d2155..08d8f97d9d 100644 --- a/Userland/Libraries/LibWeb/Painting/RadioButtonPaintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/RadioButtonPaintable.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018-2022, Andreas Kling + * Copyright (c) 2023, MacDue * * SPDX-License-Identifier: BSD-2-Clause */ @@ -11,6 +12,7 @@ #include #include #include +#include #include namespace Web::Painting { @@ -32,9 +34,58 @@ void RadioButtonPaintable::paint(PaintContext& context, PaintPhase phase) const PaintableBox::paint(context, phase); - auto const& radio_box = static_cast(layout_box().dom_node()); - if (phase == PaintPhase::Foreground) - Gfx::StylePainter::paint_radio_button(context.painter(), context.enclosing_device_rect(absolute_rect()).to_type(), context.palette(), radio_box.checked(), being_pressed()); + if (phase != PaintPhase::Foreground) + return; + + Gfx::AntiAliasingPainter painter { context.painter() }; + + auto draw_circle = [&](auto const& rect, Color color) { + // Note: Doing this is a bit more forgiving than draw_circle() which will round to the nearset even radius. + // This will fudge it (which works better here). + painter.fill_rect_with_rounded_corners(rect, color, rect.width() / 2); + }; + + auto shrink_all = [&](auto const& rect, int amount) { + return rect.shrunken(amount, amount, amount, amount); + }; + + auto const& radio_button = static_cast(layout_box().dom_node()); + + auto& palette = context.palette(); + bool enabled = layout_box().dom_node().enabled(); + auto input_colors = compute_input_colors(palette, computed_values().accent_color()); + + auto background_color = input_colors.background_color(enabled); + auto accent = input_colors.accent; + + auto radio_color = [&] { + if (radio_button.checked()) { + // Handle the awkward case where a light color has been used for the accent color. + if (accent.contrast_ratio(background_color) < 2 && accent.contrast_ratio(input_colors.dark_gray) > 2) + background_color = input_colors.dark_gray; + return accent; + } + return input_colors.gray; + }; + + auto fill_color = [&] { + if (!enabled) + return input_colors.mid_gray; + auto color = radio_color(); + if (being_pressed()) + color = InputColors::get_shade(color, 0.3f, palette.is_dark()); + return color; + }(); + + // This is based on a 1px outer border and 2px inner border when drawn at 13x13. + auto radio_button_rect = context.enclosing_device_rect(absolute_rect()).to_type(); + auto outer_border_width = max(1, static_cast(ceilf(radio_button_rect.width() / 13.0f))); + auto inner_border_width = max(2, static_cast(ceilf(radio_button_rect.width() / 4.0f))); + + draw_circle(radio_button_rect, fill_color); + draw_circle(shrink_all(radio_button_rect, outer_border_width), background_color); + if (radio_button.checked()) + draw_circle(shrink_all(radio_button_rect, inner_border_width), fill_color); } }