mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Bug 123836 - Implement indeterminate property on checkboxes and radio buttons - r=roc,jst sr=roc
This commit is contained in:
parent
32da7c5a78
commit
b119ed9345
@ -129,6 +129,7 @@ static NS_DEFINE_CID(kXULControllersCID, NS_XULCONTROLLERS_CID);
|
||||
#define BF_PARSER_CREATING 7
|
||||
#define BF_IN_INTERNAL_ACTIVATE 8
|
||||
#define BF_CHECKED_IS_TOGGLED 9
|
||||
#define BF_INDETERMINATE 10
|
||||
|
||||
#define GET_BOOLBIT(bitfield, field) (((bitfield) & (0x01 << (field))) \
|
||||
? PR_TRUE : PR_FALSE)
|
||||
@ -140,8 +141,10 @@ static NS_DEFINE_CID(kXULControllersCID, NS_XULCONTROLLERS_CID);
|
||||
#define NS_OUTER_ACTIVATE_EVENT (1 << 9)
|
||||
#define NS_ORIGINAL_CHECKED_VALUE (1 << 10)
|
||||
#define NS_NO_CONTENT_DISPATCH (1 << 11)
|
||||
#define NS_ORIGINAL_INDETERMINATE_VALUE (1 << 12)
|
||||
#define NS_CONTROL_TYPE(bits) ((bits) & ~( \
|
||||
NS_OUTER_ACTIVATE_EVENT | NS_ORIGINAL_CHECKED_VALUE | NS_NO_CONTENT_DISPATCH))
|
||||
NS_OUTER_ACTIVATE_EVENT | NS_ORIGINAL_CHECKED_VALUE | NS_NO_CONTENT_DISPATCH | \
|
||||
NS_ORIGINAL_INDETERMINATE_VALUE))
|
||||
|
||||
static const char kWhitespace[] = "\n\r\t\b";
|
||||
|
||||
@ -753,6 +756,26 @@ nsHTMLInputElement::SetDefaultValue(const nsAString& aValue)
|
||||
return SetAttrHelper(nsGkAtoms::value, aValue);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLInputElement::GetIndeterminate(PRBool* aValue)
|
||||
{
|
||||
*aValue = GET_BOOLBIT(mBitField, BF_INDETERMINATE);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLInputElement::SetIndeterminate(PRBool aValue)
|
||||
{
|
||||
SET_BOOLBIT(mBitField, BF_INDETERMINATE, aValue);
|
||||
|
||||
// Repaint the frame
|
||||
nsIFrame* frame = GetPrimaryFrame();
|
||||
if (frame)
|
||||
frame->InvalidateOverflowRect();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLInputElement::GetSize(PRUint32* aValue)
|
||||
{
|
||||
@ -1529,6 +1552,12 @@ nsHTMLInputElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
|
||||
switch(mType) {
|
||||
case NS_FORM_INPUT_CHECKBOX:
|
||||
{
|
||||
if (GET_BOOLBIT(mBitField, BF_INDETERMINATE)) {
|
||||
// indeterminate is always set to FALSE when the checkbox is toggled
|
||||
SET_BOOLBIT(mBitField, BF_INDETERMINATE, PR_FALSE);
|
||||
aVisitor.mItemFlags |= NS_ORIGINAL_INDETERMINATE_VALUE;
|
||||
}
|
||||
|
||||
GetChecked(&originalCheckedValue);
|
||||
DoSetChecked(!originalCheckedValue);
|
||||
SET_BOOLBIT(mBitField, BF_CHECKED_IS_TOGGLED, PR_TRUE);
|
||||
@ -1679,6 +1708,9 @@ nsHTMLInputElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
|
||||
DoSetChecked(PR_FALSE);
|
||||
}
|
||||
} else if (oldType == NS_FORM_INPUT_CHECKBOX) {
|
||||
PRBool originalIndeterminateValue =
|
||||
!!(aVisitor.mItemFlags & NS_ORIGINAL_INDETERMINATE_VALUE);
|
||||
SET_BOOLBIT(mBitField, BF_INDETERMINATE, originalIndeterminateValue);
|
||||
DoSetChecked(originalCheckedValue);
|
||||
}
|
||||
} else {
|
||||
|
@ -42,7 +42,7 @@
|
||||
interface nsIControllers;
|
||||
interface nsIDOMFileList;
|
||||
|
||||
[scriptable, uuid(df3dc133-d77a-482f-8364-8e40df978a33)]
|
||||
[scriptable, uuid(71e9ecc0-f2d0-422c-8601-430e7f17fa47)]
|
||||
interface nsIDOMNSHTMLInputElement : nsISupports
|
||||
{
|
||||
readonly attribute nsIControllers controllers;
|
||||
@ -54,6 +54,8 @@ interface nsIDOMNSHTMLInputElement : nsISupports
|
||||
|
||||
readonly attribute nsIDOMFileList files;
|
||||
|
||||
attribute boolean indeterminate;
|
||||
|
||||
/* convenience */
|
||||
void setSelectionRange(in long selectionStart,
|
||||
in long selectionEnd);
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include "nsIDOMHTMLInputElement.h"
|
||||
#include "nsDisplayList.h"
|
||||
#include "nsCSSAnonBoxes.h"
|
||||
#include "nsIDOMNSHTMLInputElement.h"
|
||||
|
||||
static void
|
||||
PaintCheckMark(nsIRenderingContext& aRenderingContext,
|
||||
@ -74,6 +75,18 @@ PaintCheckMark(nsIRenderingContext& aRenderingContext,
|
||||
aRenderingContext.FillPolygon(paintPolygon, checkNumPoints);
|
||||
}
|
||||
|
||||
static void
|
||||
PaintIndeterminateMark(nsIRenderingContext& aRenderingContext,
|
||||
const nsRect& aRect)
|
||||
{
|
||||
// Drawing a thin horizontal line in the middle of the rect.
|
||||
nsRect fillRect = aRect;
|
||||
fillRect.height /= 4;
|
||||
fillRect.y += (aRect.height - fillRect.height) / 2;
|
||||
|
||||
aRenderingContext.FillRect(fillRect);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
nsIFrame*
|
||||
NS_NewGfxCheckboxControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
|
||||
@ -209,7 +222,10 @@ nsGfxCheckboxControlFrame::PaintCheckBox(nsIRenderingContext& aRenderingContext,
|
||||
const nsStyleColor* color = GetStyleColor();
|
||||
aRenderingContext.SetColor(color->mColor);
|
||||
|
||||
PaintCheckMark(aRenderingContext, checkRect);
|
||||
if (IsIndeterminate())
|
||||
PaintIndeterminateMark(aRenderingContext, checkRect);
|
||||
else
|
||||
PaintCheckMark(aRenderingContext, checkRect);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
@ -222,7 +238,7 @@ nsGfxCheckboxControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Get current checked state through content model.
|
||||
if (!GetCheckboxState() || !IsVisibleForPainting(aBuilder))
|
||||
if ((!IsChecked() && !IsIndeterminate()) || !IsVisibleForPainting(aBuilder))
|
||||
return NS_OK; // we're not checked or not visible, nothing to paint.
|
||||
|
||||
if (IsThemed())
|
||||
@ -271,10 +287,19 @@ nsGfxCheckboxControlFrame::PaintCheckBoxFromStyle(
|
||||
|
||||
//------------------------------------------------------------
|
||||
PRBool
|
||||
nsGfxCheckboxControlFrame::GetCheckboxState ( )
|
||||
nsGfxCheckboxControlFrame::IsChecked()
|
||||
{
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> elem(do_QueryInterface(mContent));
|
||||
PRBool retval = PR_FALSE;
|
||||
elem->GetChecked(&retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsGfxCheckboxControlFrame::IsIndeterminate()
|
||||
{
|
||||
nsCOMPtr<nsIDOMNSHTMLInputElement> elem(do_QueryInterface(mContent));
|
||||
PRBool retval = PR_FALSE;
|
||||
elem->GetIndeterminate(&retval);
|
||||
return retval;
|
||||
}
|
||||
|
@ -91,7 +91,8 @@ public:
|
||||
|
||||
protected:
|
||||
|
||||
PRBool GetCheckboxState();
|
||||
PRBool IsChecked();
|
||||
PRBool IsIndeterminate();
|
||||
|
||||
nsRefPtr<nsStyleContext> mCheckButtonFaceStyle;
|
||||
};
|
||||
|
1
layout/reftests/forms/indeterminate-checked-notref.html
Normal file
1
layout/reftests/forms/indeterminate-checked-notref.html
Normal file
@ -0,0 +1 @@
|
||||
<input type="checkbox" checked style="-moz-appearance: none;">
|
1
layout/reftests/forms/indeterminate-checked.html
Normal file
1
layout/reftests/forms/indeterminate-checked.html
Normal file
@ -0,0 +1 @@
|
||||
<input type="checkbox" id="s" checked style="-moz-appearance: none;"><script>document.getElementById("s").indeterminate = true;</script>
|
@ -0,0 +1 @@
|
||||
<input type="checkbox" style="-moz-appearance: none;">
|
1
layout/reftests/forms/indeterminate-unchecked.html
Normal file
1
layout/reftests/forms/indeterminate-unchecked.html
Normal file
@ -0,0 +1 @@
|
||||
<input type="checkbox" id="s" style="-moz-appearance: none;"><script>document.getElementById("s").indeterminate = true;</script>
|
@ -5,3 +5,5 @@
|
||||
== input-text-size-2.html input-text-size-2-ref.html
|
||||
== radio-label-dynamic.html radio-label-dynamic-ref.html
|
||||
== out-of-bounds-selectedindex.html out-of-bounds-selectedindex-ref.html # bug 471741
|
||||
!= indeterminate-checked.html indeterminate-checked-notref.html
|
||||
!= indeterminate-unchecked.html indeterminate-unchecked-notref.html
|
||||
|
@ -964,8 +964,8 @@ moz_gtk_button_get_inner_border(GtkWidget* widget, GtkBorder* inner_border)
|
||||
static gint
|
||||
moz_gtk_toggle_paint(GdkDrawable* drawable, GdkRectangle* rect,
|
||||
GdkRectangle* cliprect, GtkWidgetState* state,
|
||||
gboolean selected, gboolean isradio,
|
||||
GtkTextDirection direction)
|
||||
gboolean selected, gboolean inconsistent,
|
||||
gboolean isradio, GtkTextDirection direction)
|
||||
{
|
||||
GtkStateType state_type = ConvertGtkState(state);
|
||||
GtkShadowType shadow_type = (selected)?GTK_SHADOW_IN:GTK_SHADOW_OUT;
|
||||
@ -1017,6 +1017,17 @@ moz_gtk_toggle_paint(GdkDrawable* drawable, GdkRectangle* rect,
|
||||
}
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* 'indeterminate' type on checkboxes. In GTK, the shadow type
|
||||
* must also be changed for the state to be drawn.
|
||||
*/
|
||||
if (inconsistent) {
|
||||
gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(gCheckboxWidget), TRUE);
|
||||
shadow_type = GTK_SHADOW_ETCHED_IN;
|
||||
} else {
|
||||
gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(gCheckboxWidget), FALSE);
|
||||
}
|
||||
|
||||
gtk_paint_check(style, drawable, state_type, shadow_type, cliprect,
|
||||
gCheckboxWidget, "checkbutton", x, y, width, height);
|
||||
if (state->focused) {
|
||||
@ -3042,7 +3053,8 @@ moz_gtk_widget_paint(GtkThemeWidgetType widget, GdkDrawable* drawable,
|
||||
case MOZ_GTK_CHECKBUTTON:
|
||||
case MOZ_GTK_RADIOBUTTON:
|
||||
return moz_gtk_toggle_paint(drawable, rect, cliprect, state,
|
||||
(gboolean) flags,
|
||||
!!(flags & MOZ_GTK_WIDGET_CHECKED),
|
||||
!!(flags & MOZ_GTK_WIDGET_INCONSISTENT),
|
||||
(widget == MOZ_GTK_RADIOBUTTON),
|
||||
direction);
|
||||
break;
|
||||
|
@ -110,6 +110,10 @@ typedef gint (*style_prop_t)(GtkStyle*, const gchar*, gint);
|
||||
#define MOZ_GTK_UNKNOWN_WIDGET -1
|
||||
#define MOZ_GTK_UNSAFE_THEME -2
|
||||
|
||||
/*** checkbox/radio flags ***/
|
||||
#define MOZ_GTK_WIDGET_CHECKED 1
|
||||
#define MOZ_GTK_WIDGET_INCONSISTENT (1 << 1)
|
||||
|
||||
/*** widget type constants ***/
|
||||
typedef enum {
|
||||
/* Paints a GtkButton. flags is a GtkReliefStyle. */
|
||||
|
@ -59,6 +59,7 @@
|
||||
#include "nsIMenuFrame.h"
|
||||
#include "prlink.h"
|
||||
#include "nsIDOMHTMLInputElement.h"
|
||||
#include "nsIDOMNSHTMLInputElement.h"
|
||||
#include "nsWidgetAtoms.h"
|
||||
|
||||
#include <gdk/gdkprivate.h>
|
||||
@ -212,10 +213,20 @@ nsNativeThemeGTK::GetGtkWidgetAndState(PRUint8 aWidgetType, nsIFrame* aFrame,
|
||||
} else {
|
||||
if (aWidgetFlags) {
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> inputElt(do_QueryInterface(content));
|
||||
*aWidgetFlags = 0;
|
||||
if (inputElt) {
|
||||
PRBool isHTMLChecked;
|
||||
inputElt->GetChecked(&isHTMLChecked);
|
||||
*aWidgetFlags = isHTMLChecked;
|
||||
if (isHTMLChecked)
|
||||
*aWidgetFlags |= MOZ_GTK_WIDGET_CHECKED;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMNSHTMLInputElement> inputEltNS(do_QueryInterface(content));
|
||||
if (inputEltNS) {
|
||||
PRBool isIndeterminate;
|
||||
inputEltNS->GetIndeterminate(&isIndeterminate);
|
||||
if (isIndeterminate)
|
||||
*aWidgetFlags |= MOZ_GTK_WIDGET_INCONSISTENT;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -234,7 +245,7 @@ nsNativeThemeGTK::GetGtkWidgetAndState(PRUint8 aWidgetType, nsIFrame* aFrame,
|
||||
aState->canDefault = FALSE; // XXX fix me
|
||||
aState->depressed = FALSE;
|
||||
|
||||
if (aFrame && aFrame->GetContent()->IsNodeOfType(nsINode::eXUL)) {
|
||||
if (aFrame->GetContent()->IsNodeOfType(nsINode::eXUL)) {
|
||||
// For these widget types, some element (either a child or parent)
|
||||
// actually has element focus, so we check the focused attribute
|
||||
// to see whether to draw in the focused state.
|
||||
|
Loading…
Reference in New Issue
Block a user