Bug 1352238 Part 4 - Implement PaintCheckBox/RadioControl and modify the original painting functions. r?mats draft
authorLouis Chang <lochang@mozilla.com>
Tue, 05 Sep 2017 22:44:57 +0800
changeset 659110 efbd7c948027c0ec85d133d538b6ac17a2abbaf4
parent 659109 eec976c6e58fc347b2c26d7a7aedef5d0011bf17
child 659111 76c3b8b8d93add5cf893d8050826f66be63e24a3
push id78020
push userlochang@mozilla.com
push dateTue, 05 Sep 2017 14:49:24 +0000
reviewersmats
bugs1352238
milestone57.0a1
Bug 1352238 Part 4 - Implement PaintCheckBox/RadioControl and modify the original painting functions. r?mats MozReview-Commit-ID: Jk6MS5wFA2g
widget/android/AndroidColors.h
widget/android/moz.build
widget/android/nsNativeThemeAndroid.cpp
--- a/widget/android/AndroidColors.h
+++ b/widget/android/AndroidColors.h
@@ -8,13 +8,15 @@
 
 #include "mozilla/gfx/2D.h"
 
 namespace mozilla {
 namespace widget {
 
 static const Color sAndroidBorderColor(Color(0.73f, 0.73f, 0.73f));
 static const Color sAndroidCheckColor(Color(0.19f, 0.21f, 0.23f));
+static const Color sAndroidDisabledColor(Color(0.88f, 0.88f, 0.88f));
+static const Color sAndroidHoverColor(Color(0.94f, 0.94f, 0.94f));
 
 } // namespace widget
 } // namespace mozilla
 
 #endif // mozilla_widget_AndroidColors_h
--- a/widget/android/moz.build
+++ b/widget/android/moz.build
@@ -65,16 +65,18 @@ UNIFIED_SOURCES += [
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '/docshell/base',
     '/dom/base',
     '/dom/system/android',
+    '/gfx/2d',
+    '/layout/painting',
     '/netwerk/base',
     '/netwerk/cache',
     '/widget',
     '/xpcom/threads',
 ]
 
 CXXFLAGS += ['-Wno-error=shadow']
 
--- a/widget/android/nsNativeThemeAndroid.cpp
+++ b/widget/android/nsNativeThemeAndroid.cpp
@@ -3,28 +3,65 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsNativeThemeAndroid.h"
 
 #include "nsIDOMHTMLInputElement.h"
 #include "nsIFrame.h"
 #include "nsThemeConstants.h"
 #include "AndroidColors.h"
+#include "nsCSSRendering.h"
+#include "PathHelpers.h"
 
 NS_IMPL_ISUPPORTS_INHERITED(nsNativeThemeAndroid, nsNativeTheme, nsITheme)
 
 using namespace mozilla::gfx;
 
 static void
+PaintCheckboxControl(nsIFrame* aFrame,
+                     DrawTarget* aDrawTarget,
+                     const nsRect& aRect,
+                     const EventStates& aState)
+{
+  nsRect rect(aRect);
+  rect.Deflate(aFrame->GetUsedBorderAndPadding());
+
+  // Checkbox controls aren't something that we can render on Android
+  // natively. We fake native drawing of appearance: checkbox items
+  // out here, and use hardcoded colours from AndroidColors.h to
+  // simulate native theming.
+  RectCornerRadii innerRadii(2, 2, 2, 2);
+  nsRect paddingRect =
+    nsCSSRendering::GetBoxShadowInnerPaddingRect(aFrame, rect);
+  const nscoord twipsPerPixel = aFrame->PresContext()->DevPixelsToAppUnits(1);
+  Rect shadowGfxRect = NSRectToRect(paddingRect, twipsPerPixel);
+  shadowGfxRect.Round();
+  RefPtr<Path> roundedRect =
+    MakePathForRoundedRect(*aDrawTarget, shadowGfxRect, innerRadii);
+  aDrawTarget->Stroke(roundedRect,
+    ColorPattern(ToDeviceColor(mozilla::widget::sAndroidBorderColor)));
+
+  if (aState.HasState(NS_EVENT_STATE_DISABLED)) {
+    aDrawTarget->Fill(roundedRect,
+      ColorPattern(ToDeviceColor(mozilla::widget::sAndroidDisabledColor)));
+    return;
+  }
+
+  if (aState.HasState(NS_EVENT_STATE_HOVER)) {
+    aDrawTarget->Fill(roundedRect,
+      ColorPattern(ToDeviceColor(mozilla::widget::sAndroidHoverColor)));
+  }
+}
+
+static void
 PaintCheckMark(nsIFrame* aFrame,
                DrawTarget* aDrawTarget,
-               const nsRect& aDirtyRect,
-               nsPoint aPt)
+               const nsRect& aRect)
 {
-  nsRect rect(aPt, aFrame->GetSize());
+  nsRect rect(nsPoint(aRect.X(), aRect.Y()), 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
@@ -42,84 +79,115 @@ PaintCheckMark(nsIFrame* aFrame,
   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)));
+    ColorPattern(ToDeviceColor(mozilla::widget::sAndroidCheckColor)));
 }
 
 static void
 PaintIndeterminateMark(nsIFrame* aFrame,
                        DrawTarget* aDrawTarget,
-                       const nsRect& aDirtyRect,
-                       nsPoint aPt)
+                       const nsRect& aRect)
 {
   int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
 
-  nsRect rect(aPt, aFrame->GetSize());
+  nsRect rect(nsPoint(aRect.X(), aRect.Y()), 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)));
+  aDrawTarget->FillRect(devPxRect,
+    ColorPattern(ToDeviceColor(mozilla::widget::sAndroidCheckColor)));
+}
+
+static void
+PaintRadioControl(nsIFrame* aFrame,
+                  DrawTarget* aDrawTarget,
+                  const nsRect& aRect,
+                  const EventStates& aState)
+{
+  nsRect rect(aRect);
+  rect.Deflate(aFrame->GetUsedBorderAndPadding());
+
+  // Radio controls aren't something that we can render on Android
+  // natively. We fake native drawing of appearance: radio items
+  // out here, and use hardcoded colours to simulate native
+  // theming.
+  const nscoord twipsPerPixel = aFrame->PresContext()->DevPixelsToAppUnits(1);
+  Rect devPxRect = NSRectToRect(rect, twipsPerPixel);
+  RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
+  AppendEllipseToPath(builder, devPxRect.Center(), devPxRect.Size());
+  RefPtr<Path> ellipse = builder->Finish();
+  aDrawTarget->Stroke(ellipse,
+    ColorPattern(ToDeviceColor(mozilla::widget::sAndroidBorderColor)));
+
+  if (aState.HasState(NS_EVENT_STATE_DISABLED)) {
+    aDrawTarget->Fill(ellipse,
+      ColorPattern(ToDeviceColor(mozilla::widget::sAndroidDisabledColor)));
+    return;
+  }
+
+  if (aState.HasState(NS_EVENT_STATE_HOVER)) {
+    aDrawTarget->Fill(ellipse,
+      ColorPattern(ToDeviceColor(mozilla::widget::sAndroidHoverColor)));
+  }
 }
 
 static void
 PaintCheckedRadioButton(nsIFrame* aFrame,
                         DrawTarget* aDrawTarget,
-                        const nsRect& aDirtyRect,
-                        nsPoint aPt)
+                        const nsRect& aRect)
 {
   // The dot is an ellipse 2px on all sides smaller than the content-box,
   // drawn in the foreground color.
-  nsRect rect(aPt, aFrame->GetSize());
+  nsRect rect(nsPoint(aRect.X(), aRect.Y()), 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);
+  aDrawTarget->Fill(ellipse,
+    ColorPattern(ToDeviceColor(mozilla::widget::sAndroidCheckColor)));
 }
 
 NS_IMETHODIMP
 nsNativeThemeAndroid::DrawWidgetBackground(gfxContext* aContext,
                                            nsIFrame* aFrame,
                                            uint8_t aWidgetType,
                                            const nsRect& aRect,
                                            const nsRect& aDirtyRect)
 {
+  EventStates eventState = GetContentState(aFrame, aWidgetType);
   switch (aWidgetType) {
     case NS_THEME_RADIO:
-      PaintRadioControl(aFrame, aContext->GetDrawTarget(), aDirtyRect);
+      PaintRadioControl(aFrame, aContext->GetDrawTarget(), aRect, eventState);
       if (IsSelected(aFrame)) {
-        PaintCheckedRadioButton(aFrame, aContext->GetDrawTarget(), aDirtyRect);
+        PaintCheckedRadioButton(aFrame, aContext->GetDrawTarget(), aRect);
       }
       break;
     case NS_THEME_CHECKBOX:
-      PaintCheckboxControl(aFrame, aContext->GetDrawTarget(), aDirtyRect);
+      PaintCheckboxControl(aFrame, aContext->GetDrawTarget(), aRect, eventState);
       if (IsChecked(aFrame)) {
-        PaintCheckMark(aFrame, aContext->GetDrawTarget(), aDirtyRect);
+        PaintCheckMark(aFrame, aContext->GetDrawTarget(), aRect);
       }
       if (GetIndeterminate(aFrame)) {
-        PaintIndeterminateMark(aFrame, aContext->GetDrawTarget(), aDirtyRect);
+        PaintIndeterminateMark(aFrame, aContext->GetDrawTarget(), aRect);
       }
       break;
     default:
       MOZ_ASSERT_UNREACHABLE("Should not get here with a widget type we don't support.");
       return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   return NS_OK;
@@ -151,19 +219,19 @@ nsNativeThemeAndroid::GetWidgetOverflow(
 
 NS_IMETHODIMP
 nsNativeThemeAndroid::GetMinimumWidgetSize(nsPresContext* aPresContext,
                                        nsIFrame* aFrame, uint8_t aWidgetType,
                                        LayoutDeviceIntSize* aResult,
                                        bool* aIsOverridable)
 {
   if (aWidgetType == NS_THEME_RADIO || aWidgetType == NS_THEME_CHECKBOX) {
-    // The fixed size of checkmark used in PaintCheckMark
-    aResult->width = 9;
-    aResult->height = 9;
+    // 9px + (1px padding + 1px border) * 2
+    aResult->width = 13;
+    aResult->height = 13;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNativeThemeAndroid::WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType,
                                      nsIAtom* aAttribute, bool* aShouldRepaint,