Pārlūkot izejas kodu

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).
MacDue 2 gadi atpakaļ
vecāks
revīzija
dd77b878b3

+ 54 - 3
Userland/Libraries/LibWeb/Painting/RadioButtonPaintable.cpp

@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2018-2022, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2023, MacDue <macdue@dueutil.tech>
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
@@ -11,6 +12,7 @@
 #include <LibWeb/HTML/HTMLInputElement.h>
 #include <LibWeb/Layout/Label.h>
 #include <LibWeb/Layout/RadioButton.h>
+#include <LibWeb/Painting/InputColors.h>
 #include <LibWeb/Painting/RadioButtonPaintable.h>
 
 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<HTML::HTMLInputElement const&>(layout_box().dom_node());
-    if (phase == PaintPhase::Foreground)
-        Gfx::StylePainter::paint_radio_button(context.painter(), context.enclosing_device_rect(absolute_rect()).to_type<int>(), 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<HTML::HTMLInputElement const&>(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<int>();
+    auto outer_border_width = max(1, static_cast<int>(ceilf(radio_button_rect.width() / 13.0f)));
+    auto inner_border_width = max(2, static_cast<int>(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);
 }
 
 }