Bug 418833 - Move default checkbox/radio drawing to images. Allow overriding checkbox/radio styling. r?tnikkel,dao
MozReview-Commit-ID: HqyXXuYzjrE
--- a/layout/forms/nsGfxCheckboxControlFrame.cpp
+++ b/layout/forms/nsGfxCheckboxControlFrame.cpp
@@ -13,72 +13,16 @@
#include "nsRenderingContext.h"
#include "nsIDOMHTMLInputElement.h"
#include "nsDisplayList.h"
#include <algorithm>
using namespace mozilla;
using namespace mozilla::gfx;
-static void
-PaintCheckMark(nsIFrame* aFrame,
- DrawTarget* aDrawTarget,
- const nsRect& aDirtyRect,
- nsPoint aPt)
-{
- nsRect rect(aPt, aFrame->GetSize());
- rect.Deflate(aFrame->GetUsedBorderAndPadding());
-
- // Points come from the coordinates on a 7X7 unit box centered at 0,0
- const int32_t checkPolygonX[] = { -3, -1, 3, 3, -1, -3 };
- const int32_t checkPolygonY[] = { -1, 1, -3, -1, 3, 1 };
- const int32_t checkNumPoints = sizeof(checkPolygonX) / sizeof(int32_t);
- const int32_t checkSize = 9; // 2 units of padding on either side
- // of the 7x7 unit checkmark
-
- // Scale the checkmark based on the smallest dimension
- nscoord paintScale = std::min(rect.width, rect.height) / checkSize;
- nsPoint paintCenter(rect.x + rect.width / 2,
- rect.y + rect.height / 2);
-
- RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
- nsPoint p = paintCenter + nsPoint(checkPolygonX[0] * paintScale,
- checkPolygonY[0] * paintScale);
-
- int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
- builder->MoveTo(NSPointToPoint(p, appUnitsPerDevPixel));
- for (int32_t polyIndex = 1; polyIndex < checkNumPoints; polyIndex++) {
- p = paintCenter + nsPoint(checkPolygonX[polyIndex] * paintScale,
- checkPolygonY[polyIndex] * paintScale);
- builder->LineTo(NSPointToPoint(p, appUnitsPerDevPixel));
- }
- RefPtr<Path> path = builder->Finish();
- aDrawTarget->Fill(path,
- ColorPattern(ToDeviceColor(aFrame->StyleColor()->mColor)));
-}
-
-static void
-PaintIndeterminateMark(nsIFrame* aFrame,
- DrawTarget* aDrawTarget,
- const nsRect& aDirtyRect,
- nsPoint aPt)
-{
- int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
-
- nsRect rect(aPt, aFrame->GetSize());
- rect.Deflate(aFrame->GetUsedBorderAndPadding());
- rect.y += (rect.height - rect.height/4) / 2;
- rect.height /= 4;
-
- Rect devPxRect = NSRectToSnappedRect(rect, appUnitsPerDevPixel, *aDrawTarget);
-
- aDrawTarget->FillRect(
- devPxRect, ColorPattern(ToDeviceColor(aFrame->StyleColor()->mColor)));
-}
-
//------------------------------------------------------------
nsIFrame*
NS_NewGfxCheckboxControlFrame(nsIPresShell* aPresShell,
nsStyleContext* aContext)
{
return new (aPresShell) nsGfxCheckboxControlFrame(aContext);
}
@@ -100,39 +44,16 @@ nsGfxCheckboxControlFrame::~nsGfxCheckbo
a11y::AccType
nsGfxCheckboxControlFrame::AccessibleType()
{
return a11y::eHTMLCheckboxType;
}
#endif
//------------------------------------------------------------
-void
-nsGfxCheckboxControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
- const nsRect& aDirtyRect,
- const nsDisplayListSet& aLists)
-{
- nsFormControlFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
-
- // Get current checked state through content model.
- if ((!IsChecked() && !IsIndeterminate()) || !IsVisibleForPainting(aBuilder))
- return; // we're not checked or not visible, nothing to paint.
-
- if (IsThemed())
- return; // No need to paint the checkmark. The theme will do it.
-
- aLists.Content()->AppendNewToTop(new (aBuilder)
- nsDisplayGeneric(aBuilder, this,
- IsIndeterminate()
- ? PaintIndeterminateMark : PaintCheckMark,
- "CheckedCheckbox",
- nsDisplayItem::TYPE_CHECKED_CHECKBOX));
-}
-
-//------------------------------------------------------------
bool
nsGfxCheckboxControlFrame::IsChecked()
{
nsCOMPtr<nsIDOMHTMLInputElement> elem(do_QueryInterface(mContent));
bool retval = false;
elem->GetChecked(&retval);
return retval;
}
--- a/layout/forms/nsGfxCheckboxControlFrame.h
+++ b/layout/forms/nsGfxCheckboxControlFrame.h
@@ -17,20 +17,16 @@ public:
virtual ~nsGfxCheckboxControlFrame();
#ifdef DEBUG_FRAME_DUMP
virtual nsresult GetFrameName(nsAString& aResult) const override {
return MakeFrameName(NS_LITERAL_STRING("CheckboxControl"), aResult);
}
#endif
- virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
- const nsRect& aDirtyRect,
- const nsDisplayListSet& aLists) override;
-
#ifdef ACCESSIBILITY
virtual mozilla::a11y::AccType AccessibleType() override;
#endif
protected:
bool IsChecked();
bool IsIndeterminate();
--- a/layout/forms/nsGfxRadioControlFrame.cpp
+++ b/layout/forms/nsGfxRadioControlFrame.cpp
@@ -35,59 +35,8 @@ nsGfxRadioControlFrame::~nsGfxRadioContr
#ifdef ACCESSIBILITY
a11y::AccType
nsGfxRadioControlFrame::AccessibleType()
{
return a11y::eHTMLRadioButtonType;
}
#endif
-
-//--------------------------------------------------------------
-// Draw the dot for a non-native radio button in the checked state.
-static void
-PaintCheckedRadioButton(nsIFrame* aFrame,
- DrawTarget* aDrawTarget,
- const nsRect& aDirtyRect,
- nsPoint aPt)
-{
- // The dot is an ellipse 2px on all sides smaller than the content-box,
- // drawn in the foreground color.
- nsRect rect(aPt, aFrame->GetSize());
- rect.Deflate(aFrame->GetUsedBorderAndPadding());
- rect.Deflate(nsPresContext::CSSPixelsToAppUnits(2),
- nsPresContext::CSSPixelsToAppUnits(2));
-
- Rect devPxRect =
- ToRect(nsLayoutUtils::RectToGfxRect(rect,
- aFrame->PresContext()->AppUnitsPerDevPixel()));
-
- ColorPattern color(ToDeviceColor(aFrame->StyleColor()->mColor));
-
- RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
- AppendEllipseToPath(builder, devPxRect.Center(), devPxRect.Size());
- RefPtr<Path> ellipse = builder->Finish();
- aDrawTarget->Fill(ellipse, color);
-}
-
-void
-nsGfxRadioControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
- const nsRect& aDirtyRect,
- const nsDisplayListSet& aLists)
-{
- nsFormControlFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
-
- if (!IsVisibleForPainting(aBuilder))
- return;
-
- if (IsThemed())
- return; // The theme will paint the check, if any.
-
- bool checked = true;
- GetCurrentCheckState(&checked); // Get check state from the content model
- if (!checked)
- return;
-
- aLists.Content()->AppendNewToTop(new (aBuilder)
- nsDisplayGeneric(aBuilder, this, PaintCheckedRadioButton,
- "CheckedRadioButton",
- nsDisplayItem::TYPE_CHECKED_RADIOBUTTON));
-}
--- a/layout/forms/nsGfxRadioControlFrame.h
+++ b/layout/forms/nsGfxRadioControlFrame.h
@@ -18,15 +18,11 @@ public:
explicit nsGfxRadioControlFrame(nsStyleContext* aContext);
~nsGfxRadioControlFrame();
NS_DECL_FRAMEARENA_HELPERS
#ifdef ACCESSIBILITY
virtual mozilla::a11y::AccType AccessibleType() override;
#endif
-
- virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
- const nsRect& aDirtyRect,
- const nsDisplayListSet& aLists) override;
};
#endif
--- a/layout/style/jar.mn
+++ b/layout/style/jar.mn
@@ -26,10 +26,13 @@ toolkit.jar:
res/accessiblecaret-tilt-left@1x.png (res/accessiblecaret-tilt-left@1x.png)
res/accessiblecaret-tilt-left@1.5x.png (res/accessiblecaret-tilt-left@1.5x.png)
res/accessiblecaret-tilt-left@2x.png (res/accessiblecaret-tilt-left@2x.png)
res/accessiblecaret-tilt-left@2.25x.png (res/accessiblecaret-tilt-left@2.25x.png)
res/accessiblecaret-tilt-right@1x.png (res/accessiblecaret-tilt-right@1x.png)
res/accessiblecaret-tilt-right@1.5x.png (res/accessiblecaret-tilt-right@1.5x.png)
res/accessiblecaret-tilt-right@2x.png (res/accessiblecaret-tilt-right@2x.png)
res/accessiblecaret-tilt-right@2.25x.png (res/accessiblecaret-tilt-right@2.25x.png)
+ res/checkmark.svg (res/checkmark.svg)
+ res/indeterminate-checkmark.svg (res/indeterminate-checkmark.svg)
+ res/radio.svg (res/radio.svg)
% resource gre-resources %res/
new file mode 100644
--- /dev/null
+++ b/layout/style/res/checkmark.svg
@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 10 10" width="10" height="10">
+ <style type="text/css">
+ path { fill: -moz-FieldText; }
+ #disabled:target { fill: GrayText; }
+ </style>
+ <path id="disabled" d="M 2 4 L 4 6 L 8 2 L 8 4 L 4 8 L 2 6 Z"/>
+</svg>
--- a/layout/style/res/forms.css
+++ b/layout/style/res/forms.css
@@ -566,52 +566,80 @@ input[type="checkbox"] {
input[type="radio"],
input[type="checkbox"] {
box-sizing: border-box;
inline-size: 13px;
block-size: 13px;
cursor: default;
padding: 0 !important;
-moz-binding: none;
- /* same colors as |input| rule, but |!important| this time. */
- background-color: -moz-Field ! important;
- color: -moz-FieldText ! important;
- border: 2px inset ThreeDLightShadow ! important;
+ border: 2px inset ThreeDLightShadow;
+ background-repeat: no-repeat;
+ background-position: center;
}
input[type="radio"]:disabled,
input[type="radio"]:disabled:active,
input[type="radio"]:disabled:hover,
input[type="radio"]:disabled:hover:active,
input[type="checkbox"]:disabled,
input[type="checkbox"]:disabled:active,
input[type="checkbox"]:disabled:hover,
input[type="checkbox"]:disabled:hover:active {
padding: 1px;
- border: 1px inset ThreeDShadow ! important;
- /* same as above, but !important */
- color: GrayText ! important;
- background-color: ThreeDFace ! important;
+ border: 1px inset ThreeDShadow;
cursor: inherit;
}
% On Mac, the native theme takes care of this.
% See nsNativeThemeCocoa::ThemeDrawsFocusForWidget.
%ifndef XP_MACOSX
input[type="checkbox"]:-moz-focusring,
input[type="radio"]:-moz-focusring {
/* Don't specify the outline-color, we should always use initial value. */
outline: 1px dotted;
}
%endif
input[type="checkbox"]:hover:active,
input[type="radio"]:hover:active {
- background-color: ThreeDFace ! important;
- border-style: inset !important;
+ background-color: ThreeDFace;
+ border-style: inset;
+}
+
+input[type="radio"] {
+ background-size: calc(100% - 4px) calc(100% - 4px);
+}
+
+input[type="radio"]:checked {
+ background-image: url(radio.svg);
+}
+
+input[type="radio"]:disabled:checked {
+ background-image: url(radio.svg#disabled);
+}
+
+input[type="checkbox"] {
+ background-size: 100% 100%;
+}
+
+input[type="checkbox"]:checked {
+ background-image: url(checkmark.svg);
+}
+
+input[type="checkbox"]:disabled:checked {
+ background-image: url(checkmark.svg#disabled);
+}
+
+input[type="checkbox"]:indeterminate {
+ background-image: url(indeterminate-checkmark.svg);
+}
+
+input[type="checkbox"]:indeterminate:disabled {
+ background-image: url(indeterminate-checkmark.svg#disabled);
}
input[type="search"] {
box-sizing: border-box;
}
/* buttons */
new file mode 100644
--- /dev/null
+++ b/layout/style/res/indeterminate-checkmark.svg
@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 5 5" width="5" height="5">
+ <style type="text/css">
+ rect { fill: -moz-FieldText; }
+ #disabled:target { fill: GrayText; }
+ </style>
+ <rect id="disabled" x="0" y="2" width="5" height="1"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/style/res/radio.svg
@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 20 20" width="20" height="20">
+ <style type="text/css">
+ circle { fill: -moz-FieldText; }
+ #disabled:target { fill: GrayText; }
+ </style>
+ <circle id="disabled" cx="10" cy="10" r="10"/>
+</svg>