mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 10:44:56 +00:00
Bug 1340661 - Manually draw checkbox and radio frames on Android. r=snorp,tnikkel
MozReview-Commit-ID: 8IiaRZNJs16 --HG-- extra : rebase_source : 8844292c6bbfec709752a51d293fc3b9bdfdced8
This commit is contained in:
parent
538d0a5aad
commit
9036c3c896
@ -47,3 +47,133 @@ nsGfxCheckboxControlFrame::AccessibleType()
|
|||||||
return a11y::eHTMLCheckboxType;
|
return a11y::eHTMLCheckboxType;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef ANDROID
|
||||||
|
|
||||||
|
#include "mozilla/widget/AndroidColors.h"
|
||||||
|
|
||||||
|
static void
|
||||||
|
PaintCheckboxBorder(nsIFrame* aFrame,
|
||||||
|
DrawTarget* aDrawTarget,
|
||||||
|
const nsRect& aDirtyRect,
|
||||||
|
nsPoint aPt)
|
||||||
|
{
|
||||||
|
nsRect rect(aPt, aFrame->GetSize());
|
||||||
|
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.
|
||||||
|
int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
|
||||||
|
Rect devPxRect = NSRectToSnappedRect(rect, appUnitsPerDevPixel, *aDrawTarget);
|
||||||
|
aDrawTarget->StrokeRect(devPxRect,
|
||||||
|
ColorPattern(ToDeviceColor(mozilla::widget::sAndroidBorderColor)));
|
||||||
|
}
|
||||||
|
|
||||||
|
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(mozilla::widget::sAndroidCheckColor)));
|
||||||
|
}
|
||||||
|
|
||||||
|
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(mozilla::widget::sAndroidCheckColor)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsGfxCheckboxControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||||
|
const nsRect& aDirtyRect,
|
||||||
|
const nsDisplayListSet& aLists)
|
||||||
|
{
|
||||||
|
nsFormControlFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
|
||||||
|
|
||||||
|
if (!IsVisibleForPainting(aBuilder)) {
|
||||||
|
return; // nothing to paint.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsThemed()) {
|
||||||
|
return; // No need to paint the checkmark. The theme will do it.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StyleDisplay()->mAppearance != NS_THEME_CHECKBOX) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
aLists.Content()->AppendNewToTop(new (aBuilder)
|
||||||
|
nsDisplayGeneric(aBuilder, this, PaintCheckboxBorder,
|
||||||
|
"CheckboxBorder", nsDisplayItem::TYPE_CHECKBOX_BORDER));
|
||||||
|
|
||||||
|
if (IsChecked() || IsIndeterminate()) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
nsGfxCheckboxControlFrame::IsIndeterminate()
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsIDOMHTMLInputElement> elem(do_QueryInterface(mContent));
|
||||||
|
bool retval = false;
|
||||||
|
elem->GetIndeterminate(&retval);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
@ -22,9 +22,24 @@ public:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef ANDROID
|
||||||
|
// On Android, there's no native theme or native widget support for
|
||||||
|
// checkbox or radio buttons. We draw them ourselves here using
|
||||||
|
// hardcoded colour values in order to simulate native drawing.
|
||||||
|
virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||||
|
const nsRect& aDirtyRect,
|
||||||
|
const nsDisplayListSet& aLists) override;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef ACCESSIBILITY
|
#ifdef ACCESSIBILITY
|
||||||
virtual mozilla::a11y::AccType AccessibleType() override;
|
virtual mozilla::a11y::AccType AccessibleType() override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef ANDROID
|
||||||
|
protected:
|
||||||
|
bool IsChecked();
|
||||||
|
bool IsIndeterminate();
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -40,3 +40,90 @@ nsGfxRadioControlFrame::AccessibleType()
|
|||||||
return a11y::eHTMLRadioButtonType;
|
return a11y::eHTMLRadioButtonType;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef ANDROID
|
||||||
|
|
||||||
|
#include "mozilla/widget/AndroidColors.h"
|
||||||
|
|
||||||
|
static void
|
||||||
|
PaintRadioBorder(nsIFrame* aFrame,
|
||||||
|
DrawTarget* aDrawTarget,
|
||||||
|
const nsRect& aDirtyRect,
|
||||||
|
nsPoint aPt)
|
||||||
|
{
|
||||||
|
nsRect rect(aPt, aFrame->GetSize());
|
||||||
|
rect.Deflate(aFrame->GetUsedBorderAndPadding());
|
||||||
|
|
||||||
|
Rect devPxRect =
|
||||||
|
ToRect(nsLayoutUtils::RectToGfxRect(rect,
|
||||||
|
aFrame->PresContext()->AppUnitsPerDevPixel()));
|
||||||
|
// 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.
|
||||||
|
RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
|
||||||
|
AppendEllipseToPath(builder, devPxRect.Center(), devPxRect.Size());
|
||||||
|
RefPtr<Path> ellipse = builder->Finish();
|
||||||
|
aDrawTarget->Stroke(ellipse,
|
||||||
|
ColorPattern(ToDeviceColor(mozilla::widget::sAndroidBorderColor)));
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
// 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()));
|
||||||
|
|
||||||
|
RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
|
||||||
|
AppendEllipseToPath(builder, devPxRect.Center(), devPxRect.Size());
|
||||||
|
RefPtr<Path> ellipse = builder->Finish();
|
||||||
|
aDrawTarget->Fill(ellipse,
|
||||||
|
ColorPattern(ToDeviceColor(mozilla::widget::sAndroidCheckColor)));
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StyleDisplay()->mAppearance != NS_THEME_RADIO) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
aLists.Content()->AppendNewToTop(new (aBuilder)
|
||||||
|
nsDisplayGeneric(aBuilder, this, PaintRadioBorder,
|
||||||
|
"RadioBorder", nsDisplayItem::TYPE_RADIOBUTTON_BORDER));
|
||||||
|
|
||||||
|
bool checked = true;
|
||||||
|
GetCurrentCheckState(&checked); // Get check state from the content model
|
||||||
|
if (checked) {
|
||||||
|
aLists.Content()->AppendNewToTop(new (aBuilder)
|
||||||
|
nsDisplayGeneric(aBuilder, this, PaintCheckedRadioButton,
|
||||||
|
"CheckedRadioButton",
|
||||||
|
nsDisplayItem::TYPE_CHECKED_RADIOBUTTON));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
@ -23,6 +23,15 @@ public:
|
|||||||
#ifdef ACCESSIBILITY
|
#ifdef ACCESSIBILITY
|
||||||
virtual mozilla::a11y::AccType AccessibleType() override;
|
virtual mozilla::a11y::AccType AccessibleType() override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef ANDROID
|
||||||
|
// On Android, there's no native theme or native widget support for
|
||||||
|
// checkbox or radio buttons. We draw them ourselves here using
|
||||||
|
// hardcoded colour values in order to simulate native drawing.
|
||||||
|
virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||||
|
const nsRect& aDirtyRect,
|
||||||
|
const nsDisplayListSet& aLists) override;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -18,6 +18,7 @@ DECLARE_DISPLAY_ITEM_TYPE(CANVAS_THEMED_BACKGROUND)
|
|||||||
DECLARE_DISPLAY_ITEM_TYPE(CANVAS_BACKGROUND_IMAGE)
|
DECLARE_DISPLAY_ITEM_TYPE(CANVAS_BACKGROUND_IMAGE)
|
||||||
DECLARE_DISPLAY_ITEM_TYPE(CANVAS_FOCUS)
|
DECLARE_DISPLAY_ITEM_TYPE(CANVAS_FOCUS)
|
||||||
DECLARE_DISPLAY_ITEM_TYPE(CARET)
|
DECLARE_DISPLAY_ITEM_TYPE(CARET)
|
||||||
|
DECLARE_DISPLAY_ITEM_TYPE(CHECKBOX_BORDER)
|
||||||
DECLARE_DISPLAY_ITEM_TYPE(CHECKED_CHECKBOX)
|
DECLARE_DISPLAY_ITEM_TYPE(CHECKED_CHECKBOX)
|
||||||
DECLARE_DISPLAY_ITEM_TYPE(CHECKED_RADIOBUTTON)
|
DECLARE_DISPLAY_ITEM_TYPE(CHECKED_RADIOBUTTON)
|
||||||
DECLARE_DISPLAY_ITEM_TYPE(CLEAR_BACKGROUND)
|
DECLARE_DISPLAY_ITEM_TYPE(CLEAR_BACKGROUND)
|
||||||
@ -41,6 +42,7 @@ DECLARE_DISPLAY_ITEM_TYPE(PLUGIN)
|
|||||||
DECLARE_DISPLAY_ITEM_TYPE(PLUGIN_READBACK)
|
DECLARE_DISPLAY_ITEM_TYPE(PLUGIN_READBACK)
|
||||||
DECLARE_DISPLAY_ITEM_TYPE(PLUGIN_VIDEO)
|
DECLARE_DISPLAY_ITEM_TYPE(PLUGIN_VIDEO)
|
||||||
DECLARE_DISPLAY_ITEM_TYPE(PRINT_PLUGIN)
|
DECLARE_DISPLAY_ITEM_TYPE(PRINT_PLUGIN)
|
||||||
|
DECLARE_DISPLAY_ITEM_TYPE(RADIOBUTTON_BORDER)
|
||||||
DECLARE_DISPLAY_ITEM_TYPE(RANGE_FOCUS_RING)
|
DECLARE_DISPLAY_ITEM_TYPE(RANGE_FOCUS_RING)
|
||||||
DECLARE_DISPLAY_ITEM_TYPE(REMOTE)
|
DECLARE_DISPLAY_ITEM_TYPE(REMOTE)
|
||||||
DECLARE_DISPLAY_ITEM_TYPE(RESOLUTION)
|
DECLARE_DISPLAY_ITEM_TYPE(RESOLUTION)
|
||||||
|
@ -98,7 +98,7 @@ select[size="1"] xul|scrollbarbutton {
|
|||||||
textarea,
|
textarea,
|
||||||
button,
|
button,
|
||||||
xul|button,
|
xul|button,
|
||||||
* > input:not([type="image"]) {
|
* > input:not(:-moz-any([type="image"], [type="checkbox"], [type="radio"])) {
|
||||||
-moz-appearance: none !important; /* See bug 598421 for fixing the platform */
|
-moz-appearance: none !important; /* See bug 598421 for fixing the platform */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
15
widget/android/AndroidColors.h
Normal file
15
widget/android/AndroidColors.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#ifndef mozilla_widget_AndroidColors_h
|
||||||
|
#define mozilla_widget_AndroidColors_h
|
||||||
|
|
||||||
|
#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));
|
||||||
|
|
||||||
|
} // namespace widget
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif
|
@ -25,6 +25,7 @@ EXPORTS += [
|
|||||||
]
|
]
|
||||||
|
|
||||||
EXPORTS.mozilla.widget += [
|
EXPORTS.mozilla.widget += [
|
||||||
|
'AndroidColors.h',
|
||||||
'AndroidCompositorWidget.h',
|
'AndroidCompositorWidget.h',
|
||||||
'AndroidUiThread.h',
|
'AndroidUiThread.h',
|
||||||
]
|
]
|
||||||
|
Loading…
Reference in New Issue
Block a user