Bug 1352238 - [WIP] Implement a native theme for checkbox/radio form controls on Android. draft
authorMike Conley <mconley@mozilla.com>
Thu, 09 Mar 2017 10:51:01 -0500
changeset 557276 6cd790cde6815fda0b28c23005ef1586282e17a6
parent 556960 950612071c4e5cbd61af6c0f66ed0bc40c35e39c
child 623003 9fb914a9ce5217ab86cefdce2e158a5241b7a72a
push id52666
push usermconley@mozilla.com
push dateThu, 06 Apr 2017 15:23:53 +0000
bugs1352238
milestone55.0a1
Bug 1352238 - [WIP] Implement a native theme for checkbox/radio form controls on Android. MozReview-Commit-ID: LqgwaPPCcmP
widget/android/moz.build
widget/android/nsNativeThemeAndroid.cpp
widget/android/nsNativeThemeAndroid.h
widget/android/nsWidgetFactory.cpp
widget/moz.build
--- a/widget/android/moz.build
+++ b/widget/android/moz.build
@@ -44,16 +44,17 @@ UNIFIED_SOURCES += [
     'GeneratedJNIWrappers.cpp',
     'GfxInfo.cpp',
     'nsAndroidProtocolHandler.cpp',
     'nsAppShell.cpp',
     'nsClipboard.cpp',
     'nsDeviceContextAndroid.cpp',
     'nsIdleServiceAndroid.cpp',
     'nsLookAndFeel.cpp',
+    'nsNativeThemeAndroid.cpp',
     'nsPrintOptionsAndroid.cpp',
     'nsScreenManagerAndroid.cpp',
     'nsWidgetFactory.cpp',
     'nsWindow.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
new file mode 100644
--- /dev/null
+++ b/widget/android/nsNativeThemeAndroid.cpp
@@ -0,0 +1,170 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxUtils.h"
+#include "nsIDOMHTMLInputElement.h"
+#include "nsIFrame.h"
+#include "nsNativeThemeAndroid.h"
+#include "nsRenderingContext.h"
+#include "nsThemeConstants.h"
+
+#include "mozilla/widget/AndroidColors.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/PathHelpers.h"
+
+NS_IMPL_ISUPPORTS_INHERITED(nsNativeThemeAndroid, nsNativeTheme, nsITheme)
+
+using namespace mozilla::gfx;
+
+static void
+PaintRadioControl(nsIFrame* aFrame,
+                  DrawTarget* aDrawTarget,
+                  const nsRect& aDirtyRect)
+{
+  // TODO
+}
+
+//--------------------------------------------------------------
+// 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)
+{
+  // TODO
+}
+
+nsNativeThemeAndroid::nsNativeThemeAndroid()
+{
+}
+
+nsNativeThemeAndroid::~nsNativeThemeAndroid() {
+}
+
+NS_IMETHODIMP
+nsNativeThemeAndroid::DrawWidgetBackground(nsRenderingContext* aContext,
+                                           nsIFrame* aFrame,
+                                           uint8_t aWidgetType,
+                                           const nsRect& aRect,
+                                           const nsRect& aDirtyRect)
+{
+  // Note: not sure if the things below are really needed - was mostly
+  // using the GTK theme backend for inspiration / cargo culting.
+  gfxContext* ctx = aContext->ThebesContext();
+  nsPresContext *presContext = aFrame->PresContext();
+  gfxRect rect = presContext->AppUnitsToGfxUnits(aRect);
+  gfxRect dirtyRect = presContext->AppUnitsToGfxUnits(aDirtyRect);
+
+  bool snapped = ctx->UserToDevicePixelSnapped(rect);
+  if (snapped) {
+    // Leave rect in device coords but make dirtyRect consistent.
+    dirtyRect = ctx->UserToDevice(dirtyRect);
+  }
+
+  dirtyRect.MoveBy(-rect.TopLeft());
+  dirtyRect.RoundOut();
+
+  gfxPoint origin = rect.TopLeft() + dirtyRect.TopLeft();
+
+  switch(aWidgetType) {
+    case NS_THEME_RADIO: {
+      PaintRadioControl(aFrame, aContext->GetDrawTarget(), aDirtyRect);
+      break;
+    }
+    case NS_THEME_CHECKBOX: {
+      // TODO
+      break;
+    }
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNativeThemeAndroid::GetWidgetBorder(nsDeviceContext* aContext, nsIFrame* aFrame,
+                                      uint8_t aWidgetType, nsIntMargin* aResult)
+{
+  return NS_OK;
+}
+
+bool
+nsNativeThemeAndroid::GetWidgetPadding(nsDeviceContext* aContext,
+                                       nsIFrame* aFrame, uint8_t aWidgetType,
+                                       nsIntMargin* aResult)
+{
+  return false;
+}
+
+bool
+nsNativeThemeAndroid::GetWidgetOverflow(nsDeviceContext* aContext,
+                                        nsIFrame* aFrame, uint8_t aWidgetType,
+                                        nsRect* aOverflowRect)
+{
+  return false;
+}
+
+NS_IMETHODIMP
+nsNativeThemeAndroid::GetMinimumWidgetSize(nsPresContext* aPresContext,
+                                       nsIFrame* aFrame, uint8_t aWidgetType,
+                                       LayoutDeviceIntSize* aResult,
+                                       bool* aIsOverridable)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNativeThemeAndroid::WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType, 
+                                     nsIAtom* aAttribute, bool* aShouldRepaint,
+                                     const nsAttrValue* aOldValue)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNativeThemeAndroid::ThemeChanged()
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP_(bool)
+nsNativeThemeAndroid::ThemeSupportsWidget(nsPresContext* aPresContext,
+                                          nsIFrame* aFrame,
+                                          uint8_t aWidgetType)
+{
+  switch (aWidgetType) {
+    case NS_THEME_RADIO:
+      return true;
+      // eventual fallthrough
+    case NS_THEME_CHECKBOX: {
+      // haven't started this yet
+      return false;
+    }
+  }
+
+  return false;
+}
+
+NS_IMETHODIMP_(bool)
+nsNativeThemeAndroid::WidgetIsContainer(uint8_t aWidgetType)
+{
+  return false;
+}
+
+bool
+nsNativeThemeAndroid::ThemeDrawsFocusForWidget(uint8_t aWidgetType)
+{
+  return false;
+}
+
+bool
+nsNativeThemeAndroid::ThemeNeedsComboboxDropmarker()
+{
+  return false;
+}
+
+nsITheme::Transparency
+nsNativeThemeAndroid::GetWidgetTransparency(nsIFrame* aFrame, uint8_t aWidgetType)
+{
+  return eUnknownTransparency;
+}
new file mode 100644
--- /dev/null
+++ b/widget/android/nsNativeThemeAndroid.h
@@ -0,0 +1,68 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsNativeThemeAndroid_h_
+#define nsNativeThemeAndroid_h_
+
+#include "nsITheme.h"
+#include "nsNativeTheme.h"
+
+class nsNativeThemeAndroid: private nsNativeTheme,
+                            public nsITheme
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  // The nsITheme interface.
+  NS_IMETHOD DrawWidgetBackground(nsRenderingContext* aContext,
+                                  nsIFrame* aFrame, uint8_t aWidgetType,
+                                  const nsRect& aRect,
+                                  const nsRect& aDirtyRect) override;
+
+  NS_IMETHOD GetWidgetBorder(nsDeviceContext* aContext, nsIFrame* aFrame,
+                             uint8_t aWidgetType,
+                             nsIntMargin* aResult) override;
+
+  virtual bool GetWidgetPadding(nsDeviceContext* aContext,
+                                nsIFrame* aFrame,
+                                uint8_t aWidgetType,
+                                nsIntMargin* aResult) override;
+
+  virtual bool GetWidgetOverflow(nsDeviceContext* aContext,
+                                 nsIFrame* aFrame,
+                                 uint8_t aWidgetType,
+                                 nsRect* aOverflowRect) override;
+
+  NS_IMETHOD GetMinimumWidgetSize(nsPresContext* aPresContext,
+                                  nsIFrame* aFrame, uint8_t aWidgetType,
+                                  mozilla::LayoutDeviceIntSize* aResult,
+                                  bool* aIsOverridable) override;
+
+  NS_IMETHOD WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType, 
+                                nsIAtom* aAttribute,
+                                bool* aShouldRepaint,
+                                const nsAttrValue* aOldValue) override;
+
+  NS_IMETHOD ThemeChanged() override;
+
+  NS_IMETHOD_(bool) ThemeSupportsWidget(nsPresContext* aPresContext,
+                                        nsIFrame* aFrame,
+                                        uint8_t aWidgetType) override;
+
+  NS_IMETHOD_(bool) WidgetIsContainer(uint8_t aWidgetType) override;
+
+  NS_IMETHOD_(bool) ThemeDrawsFocusForWidget(uint8_t aWidgetType) override;
+
+  virtual bool ThemeNeedsComboboxDropmarker() override;
+
+  virtual Transparency GetWidgetTransparency(nsIFrame* aFrame,
+                                             uint8_t aWidgetType) override;
+
+  nsNativeThemeAndroid();
+
+protected:
+  virtual ~nsNativeThemeAndroid();
+};
+
+#endif // nsNativeThemeAndroid_h_
--- a/widget/android/nsWidgetFactory.cpp
+++ b/widget/android/nsWidgetFactory.cpp
@@ -24,16 +24,17 @@
 #include "nsPrintSession.h"
 #include "nsDeviceContextAndroid.h"
 #include "nsHTMLFormatConverter.h"
 #include "nsXULAppAPI.h"
 #include "nsAndroidProtocolHandler.h"
 
 #include "nsToolkitCompsCID.h"
 #include "AndroidAlerts.h"
+#include "nsNativeThemeAndroid.h"
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindow)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsScreenManagerAndroid)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIdleServiceAndroid, nsIdleServiceAndroid::GetInstance)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsTransferable)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboard)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboardHelper)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintOptionsAndroid, Init)
@@ -47,20 +48,47 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsAndroid
 namespace mozilla {
 namespace widget {
 // This constructor should really be shared with all platforms.
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(GfxInfo, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(AndroidAlerts)
 }
 }
 
+static nsresult
+nsNativeThemeAndroidConstructor(nsISupports *aOuter, REFNSIID aIID,
+                                void **aResult)
+{
+  nsresult rv;
+  nsNativeThemeAndroid* inst;
+
+  *aResult = nullptr;
+  if (nullptr != aOuter) {
+    rv = NS_ERROR_NO_AGGREGATION;
+    return rv;
+  }
+
+  inst = new nsNativeThemeAndroid();
+  if (nullptr == inst) {
+    rv = NS_ERROR_OUT_OF_MEMORY;
+    return rv;
+  }
+
+  NS_ADDREF(inst);
+  rv = inst->QueryInterface(aIID, aResult);
+  NS_RELEASE(inst);
+
+  return rv;
+}
+
 NS_DEFINE_NAMED_CID(NS_APPSHELL_CID);
 NS_DEFINE_NAMED_CID(NS_WINDOW_CID);
 NS_DEFINE_NAMED_CID(NS_CHILD_CID);
 NS_DEFINE_NAMED_CID(NS_SCREENMANAGER_CID);
+NS_DEFINE_NAMED_CID(NS_THEMERENDERER_CID);
 NS_DEFINE_NAMED_CID(NS_IDLE_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_TRANSFERABLE_CID);
 NS_DEFINE_NAMED_CID(NS_CLIPBOARD_CID);
 NS_DEFINE_NAMED_CID(NS_CLIPBOARDHELPER_CID);
 NS_DEFINE_NAMED_CID(NS_PRINTSETTINGSSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_PRINTSESSION_CID);
 NS_DEFINE_NAMED_CID(NS_DEVICE_CONTEXT_SPEC_CID);
 NS_DEFINE_NAMED_CID(NS_HTMLFORMATCONVERTER_CID);
@@ -69,16 +97,17 @@ NS_DEFINE_NAMED_CID(NS_ANDROIDBRIDGE_CID
 NS_DEFINE_NAMED_CID(NS_ANDROIDPROTOCOLHANDLER_CID);
 NS_DEFINE_NAMED_CID(NS_SYSTEMALERTSSERVICE_CID);
 
 static const mozilla::Module::CIDEntry kWidgetCIDs[] = {
   { &kNS_WINDOW_CID, false, nullptr, nsWindowConstructor },
   { &kNS_CHILD_CID, false, nullptr, nsWindowConstructor },
   { &kNS_APPSHELL_CID, false, nullptr, nsAppShellConstructor },
   { &kNS_SCREENMANAGER_CID, false, nullptr, nsScreenManagerAndroidConstructor },
+  { &kNS_THEMERENDERER_CID, false, nullptr, nsNativeThemeAndroidConstructor },
   { &kNS_IDLE_SERVICE_CID, false, nullptr, nsIdleServiceAndroidConstructor },
   { &kNS_TRANSFERABLE_CID, false, nullptr, nsTransferableConstructor },
   { &kNS_CLIPBOARD_CID, false, nullptr, nsClipboardConstructor },
   { &kNS_CLIPBOARDHELPER_CID, false, nullptr, nsClipboardHelperConstructor },
   { &kNS_PRINTSETTINGSSERVICE_CID, false, nullptr, nsPrintOptionsAndroidConstructor },
   { &kNS_PRINTSESSION_CID, false, nullptr, nsPrintSessionConstructor },
   { &kNS_DEVICE_CONTEXT_SPEC_CID, false, nullptr, nsDeviceContextSpecAndroidConstructor },
   { &kNS_HTMLFORMATCONVERTER_CID, false, nullptr, nsHTMLFormatConverterConstructor },
@@ -89,16 +118,17 @@ static const mozilla::Module::CIDEntry k
   { nullptr }
 };
 
 static const mozilla::Module::ContractIDEntry kWidgetContracts[] = {
   { "@mozilla.org/widgets/window/android;1", &kNS_WINDOW_CID },
   { "@mozilla.org/widgets/child_window/android;1", &kNS_CHILD_CID },
   { "@mozilla.org/widget/appshell/android;1", &kNS_APPSHELL_CID },
   { "@mozilla.org/gfx/screenmanager;1", &kNS_SCREENMANAGER_CID },
+  { "@mozilla.org/chrome/chrome-native-theme;1", &kNS_THEMERENDERER_CID },
   { "@mozilla.org/widget/idleservice;1", &kNS_IDLE_SERVICE_CID },
   { "@mozilla.org/widget/transferable;1", &kNS_TRANSFERABLE_CID },
   { "@mozilla.org/widget/clipboard;1", &kNS_CLIPBOARD_CID },
   { "@mozilla.org/widget/clipboardhelper;1", &kNS_CLIPBOARDHELPER_CID },
   { "@mozilla.org/gfx/printsettings-service;1", &kNS_PRINTSETTINGSSERVICE_CID },
   { "@mozilla.org/gfx/printsession;1", &kNS_PRINTSESSION_CID },
   { "@mozilla.org/gfx/devicecontextspec;1", &kNS_DEVICE_CONTEXT_SPEC_CID },
   { "@mozilla.org/widget/htmlformatconverter;1", &kNS_HTMLFORMATCONVERTER_CID },
--- a/widget/moz.build
+++ b/widget/moz.build
@@ -231,17 +231,17 @@ if toolkit in ('cocoa', 'windows'):
     ]
 
 if toolkit in {'gtk2', 'gtk3', 'cocoa', 'windows',
                'android', 'gonk', 'uikit'}:
     UNIFIED_SOURCES += [
         'nsBaseFilePicker.cpp',
     ]
 
-if toolkit in ('gtk2', 'gtk3', 'windows', 'cocoa'):
+if toolkit in ('gtk2', 'gtk3', 'windows', 'cocoa', 'android'):
     UNIFIED_SOURCES += [
         'nsNativeTheme.cpp',
     ]
 if toolkit == 'gtk3':
     XPIDL_SOURCES += [
         'nsIApplicationChooser.idl',
     ]