From 7aec829ec99abf00ee1b7d7554f82fff568543c9 Mon Sep 17 00:00:00 2001 From: "pinkerton%netscape.com" Date: Fri, 16 Apr 1999 22:13:50 +0000 Subject: [PATCH] tri-state'ness works, click tracking works. now it should just be bug-fixin. should work enough to use now. --- .../xul/base/src/nsTriStateCheckboxFrame.cpp | 223 ++++++++++++++---- layout/xul/base/src/nsTriStateCheckboxFrame.h | 42 ++-- 2 files changed, 207 insertions(+), 58 deletions(-) diff --git a/layout/xul/base/src/nsTriStateCheckboxFrame.cpp b/layout/xul/base/src/nsTriStateCheckboxFrame.cpp index 1a442917b82a..cdd1a3fcf1e6 100644 --- a/layout/xul/base/src/nsTriStateCheckboxFrame.cpp +++ b/layout/xul/base/src/nsTriStateCheckboxFrame.cpp @@ -16,6 +16,10 @@ * Reserved. */ +// pinkerton - this should be removed when the onload handler is called at +// the correct time so that changes to content in there notify the frames. +#define ONLOAD_CALLED_TOO_EARLY 1 + #include "nsTriStateCheckboxFrame.h" #include "nsFormControlHelper.h" @@ -32,6 +36,19 @@ #include "nsINameSpaceManager.h" +// +// GetDepressAtom [static] +// +// Use a lazily instantiated static initialization scheme to create an atom that +// represents the attribute set when the button is depressed. +// +void +nsTriStateCheckboxFrame :: GetDepressAtom ( nsCOMPtr* outAtom ) +{ + static nsCOMPtr depressAtom = dont_QueryInterface(NS_NewAtom("depress")); + *outAtom = depressAtom; +} + // // NS_NewTriStateCheckboxFrame @@ -52,11 +69,8 @@ NS_NewTriStateCheckboxFrame(nsIFrame*& aResult) // nsTriStateCheckboxFrame cntr // nsTriStateCheckboxFrame::nsTriStateCheckboxFrame() - : mMouseDownOnCheckbox(PR_FALSE), nsLeafFrame() + : mMouseDownOnCheckbox(PR_FALSE), mHasOnceBeenInMixedState(PR_FALSE) { - // create an atom for the "depress" attribute if it hasn't yet been created. -// if ( !sDepressAtom ) -// sDepressAtom = dont_QueryInterface(NS_NewAtom("depress")); } // cntr @@ -76,7 +90,13 @@ nsTriStateCheckboxFrame::GetCurrentCheckState() if ( res == NS_CONTENT_ATTR_HAS_VALUE ) outState = StringToCheckState(value); -printf("getting value, it is %s\n", value.ToNewCString()); +#if ONLOAD_CALLED_TOO_EARLY +// this code really belongs in AttributeChanged, but is needed here because +// setting the value in onload doesn't trip the AttributeChanged method on the frame + if ( outState == eMixed ) + mHasOnceBeenInMixedState = PR_TRUE; +#endif + return outState; } // GetCurrentCheckState @@ -91,13 +111,7 @@ nsTriStateCheckboxFrame::SetCurrentCheckState(CheckState aState) { nsString valueAsString; CheckStateToString ( aState, valueAsString ); -printf("setting value, it is %s\n", valueAsString.ToNewCString()); - if ( NS_SUCCEEDED(mContent->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::value, valueAsString, PR_TRUE)) ) - Invalidate(nsRect(0, 0, mRect.width, mRect.height), PR_TRUE); - #ifdef NS_DEBUG - else - printf("nsTriStateCheckboxFrame::SetCurrentCheckState -- SetAttribute failed\n"); - #endif + mContent->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::value, valueAsString, PR_TRUE); } // SetCurrentCheckState @@ -111,32 +125,25 @@ printf("setting value, it is %s\n", valueAsString.ToNewCString()); void nsTriStateCheckboxFrame::MouseClicked ( const nsIPresContext & aPresContext) { -printf("MouseClicked\n"); mMouseDownOnCheckbox = PR_FALSE; CheckState oldState = GetCurrentCheckState(); CheckState newState = eOn; switch ( oldState ) { case eOn: - case eMixed: newState = eOff; break; + + case eMixed: + newState = eOn; + break; + + case eOff: + newState = mHasOnceBeenInMixedState ? eMixed: eOn; } SetCurrentCheckState(newState); } -// -// GetMaxNumValues -// -// Because we have a mixed state, we go up to two. -// -PRInt32 -nsTriStateCheckboxFrame::GetMaxNumValues() -{ - return 2; -} - - // // PaintCheckBox // @@ -153,26 +160,78 @@ nsTriStateCheckboxFrame::PaintCheckBox(nsIPresContext& aPresContext, float p2t; aPresContext.GetScaledPixelsToTwips(&p2t); - // Get current checked state through content model. - // XXX: This is very inefficient, but it is necessary in the case of printing. - // During printing the Paint is called but the actual state of the checkbox - // is in a frame in presentation shell 0. + // Get current checked state through content model. CheckState checked = GetCurrentCheckState(); - if ( checked == eOn ) { - // Draw check mark - const nsStyleColor* color = (const nsStyleColor*) - mStyleContext->GetStyleData(eStyleStruct_Color); - aRenderingContext.SetColor(color->mColor); -printf("painting checkbox\n"); - nsFormControlHelper::PaintCheckMark(aRenderingContext, - p2t, mRect.width, mRect.height); - - } + switch ( checked ) { + case eOn: + { + const nsStyleColor* color = (const nsStyleColor*) + mStyleContext->GetStyleData(eStyleStruct_Color); + aRenderingContext.SetColor(color->mColor); + nsFormControlHelper::PaintCheckMark(aRenderingContext, p2t, mRect.width, mRect.height); + break; + } + + case eMixed: + { + const nsStyleColor* color = (const nsStyleColor*) + mStyleContext->GetStyleData(eStyleStruct_Color); + aRenderingContext.SetColor(color->mColor); + PaintMixedMark(aRenderingContext, p2t, mRect.width, mRect.height); + break; + } + + } // case of value of checkbox + PRBool clip; aRenderingContext.PopState(clip); } +// +// PaintMixedMark +// +// Like nsFormControlHelper::PaintCheckMark(), but paints the horizontal "mixed" +// bar inside the box. +// +void +nsTriStateCheckboxFrame::PaintMixedMark(nsIRenderingContext& aRenderingContext, + float aPixelsToTwips, PRUint32 aWidth, PRUint32 aHeight) +{ + const PRUint32 checkpoints = 4; + const PRUint32 checksize = 6; //This is value is determined by added 2 units to the end + //of the 7X& pixel rectangle below to provide some white space + //around the checkmark when it is rendered. + + // Points come from the coordinates on a 7X7 pixels + // box with 0,0 at the lower left. + nscoord checkedPolygonDef[] = { 1,2, 5,2, 5,4, 1,4 }; + // Location of the center point of the checkmark + const PRUint32 centerx = 3; + const PRUint32 centery = 3; + + nsPoint checkedPolygon[checkpoints]; + PRUint32 defIndex = 0; + PRUint32 polyIndex = 0; + + // Scale the checkmark based on the smallest dimension + PRUint32 size = aWidth / checksize; + if (aHeight < aWidth) + size = aHeight / checksize; + + // Center and offset each point in the polygon definition. + for (defIndex = 0; defIndex < (checkpoints * 2); defIndex++) { + checkedPolygon[polyIndex].x = nscoord((((checkedPolygonDef[defIndex]) - centerx) * (size)) + (aWidth / 2)); + defIndex++; + checkedPolygon[polyIndex].y = nscoord((((checkedPolygonDef[defIndex]) - centery) * (size)) + (aHeight / 2)); + polyIndex++; + } + + aRenderingContext.FillPolygon(checkedPolygon, checkpoints); + +} // PaintMixedMark + + // // Paint // @@ -219,23 +278,35 @@ nsTriStateCheckboxFrame::HandleEvent(nsIPresContext& aPresContext, break; case NS_MOUSE_LEFT_BUTTON_DOWN: + { // set "depressed" state so CSS redraws us -// if ( NS_SUCCEEDED(mContent->SetAttribute(kNameSpaceID_None, sDepressAtom, NS_STRING_TRUE, PR_TRUE)) ) -// Invalidate(nsRect(0, 0, mRect.width, mRect.height), PR_TRUE); + DisplayDepressed(); mMouseDownOnCheckbox = PR_TRUE; break; + } case NS_MOUSE_EXIT: + { // clear "depressed" state so css redraws us -// if ( NS_SUCCEEDED(mContent->UnsetAttribute(kNameSpaceID_None, sDepressAtom, PR_TRUE)) ) -// Invalidate(nsRect(0, 0, mRect.width, mRect.height), PR_TRUE); + if ( mMouseDownOnCheckbox ) + DisplayNormal(); mMouseDownOnCheckbox = PR_FALSE; break; + } + + case NS_MOUSE_ENTER: + { + // if the mouse is down, reset the depressed attribute so CSS redraws. + if ( mMouseDownOnCheckbox ) + DisplayDepressed(); + break; + } case NS_MOUSE_LEFT_CLICK: case NS_MOUSE_LEFT_BUTTON_UP: if ( mMouseDownOnCheckbox ) MouseClicked(aPresContext); + DisplayNormal(); break; default: @@ -248,6 +319,38 @@ nsTriStateCheckboxFrame::HandleEvent(nsIPresContext& aPresContext, } +// +// DisplayDepressed +// +// Tickle the right attributes so that CSS draws us in a depressed state. Used +// when doing mouse tracking +// +void +nsTriStateCheckboxFrame :: DisplayDepressed ( ) +{ + nsCOMPtr depressAtom; + GetDepressAtom(&depressAtom); + mContent->SetAttribute(kNameSpaceID_None, depressAtom, NS_STRING_TRUE, PR_TRUE); + +} // DisplayDepressed + + +// +// DisplayNormal +// +// Tickle the right attributes so that CSS draws us in a normal state. Used +// when doing mouse tracking to reset us when the mouse leaves or at the end. +// +void +nsTriStateCheckboxFrame :: DisplayNormal ( ) +{ + nsCOMPtr depressAtom; + GetDepressAtom(&depressAtom); + mContent->UnsetAttribute(kNameSpaceID_None, depressAtom, PR_TRUE); + +} // DisplayNormal + + // // StringToCheckState // @@ -322,3 +425,35 @@ nsTriStateCheckboxFrame :: GetDesiredSize(nsIPresContext* aPresContext, } // GetDesiredSize + +// +// AttributeChanged +// +// We only want to show the mixed state if the button has ever been in that +// state in the past. That means that we need to trap all changes to the "value" +// attribute and see if we ever get set to "mixed" +// +NS_IMETHODIMP +nsTriStateCheckboxFrame::AttributeChanged(nsIPresContext* aPresContext, + nsIContent* aChild, + nsIAtom* aAttribute, + PRInt32 aHint) +{ + nsresult result = NS_OK; +#if !ONLOAD_CALLED_TOO_EARLY +// onload handlers are called to early, so we have to do this code +// elsewhere. It really belongs HERE. + if ( aAttribute == nsHTMLAtoms::value ) { + CheckState newState = GetCurrentCheckState(); + if ( newState == eMixed ) { + mHasOnceBeenInMixedState = PR_TRUE; + } + } +#endif + + // process normally regardless. + result = nsLeafFrame::AttributeChanged(aPresContext, aChild, aAttribute, aHint); + + return result; + +} // AttributeChanged diff --git a/layout/xul/base/src/nsTriStateCheckboxFrame.h b/layout/xul/base/src/nsTriStateCheckboxFrame.h index 67e24a6583d2..3df05df29c37 100644 --- a/layout/xul/base/src/nsTriStateCheckboxFrame.h +++ b/layout/xul/base/src/nsTriStateCheckboxFrame.h @@ -32,10 +32,21 @@ // Clicking the control when it is mixed would uncheck the control, as if // it is totally off. In the above example, the entire selection would be // unbolded. Clicking it again would check the control and bold the entire -// selection. Note that there is no way to get back to the mixed state. This -// is by design. It just doesn't make any sense. What that action really means -// would be satisfied by "Cancel" or "Undo" which is beyond the scope of -// the control. +// selection. Clicking a third time would get back into the mixed state. +// +// Note that the user can only get into the mixed state when the control +// has been in that state at some previous time during its lifetime. That +// means that it must be explicitly set to "mixed" at some point in order +// for the user to get there by clicking. This is done by setting the "value" +// attribute to "2". If this is not done, this checkbox behaves just like +// the normal checkbox. +// +// The only DOM APIs that this checkbox supports are the generic XML DOM APIs. +// This is mainly a result of the fact that our content node is a XUL content +// node, and we (read: hyatt) would have to go off and implement these +// extra HTMLInputElement APIs to match the API set of the normal checkbox. +// We're not going to do that, so you're just going to have to live with +// getting and setting the "value" attribute ;) // #ifndef nsTriStateCheckboxFrame_h__ @@ -60,23 +71,21 @@ class nsTriStateCheckboxFrame : public nsLeafFrame public: nsTriStateCheckboxFrame(); + // nsIFrame overrides NS_IMETHOD GetFrameName(nsString& aResult) const { return MakeFrameName("TriStateCheckboxFrame", aResult); } - - - virtual PRInt32 GetMaxNumValues(); - + NS_IMETHOD AttributeChanged(nsIPresContext* aPresContext, + nsIContent* aChild, + nsIAtom* aAttribute, + PRInt32 aHint) ; NS_IMETHOD Paint(nsIPresContext& aPresContext, nsIRenderingContext& aRenderingContext, const nsRect& aDirtyRect, nsFramePaintLayer aWhichLayer); - NS_IMETHOD HandleEvent(nsIPresContext& aPresContext, nsGUIEvent* aEvent, nsEventStatus& aEventStatus); - - //End of GFX-rendering methods protected: @@ -95,17 +104,22 @@ protected: nsIRenderingContext& aRenderingContext, const nsRect& aDirtyRect, nsFramePaintLayer aWhichLayer); + virtual void PaintMixedMark(nsIRenderingContext& aRenderingContext, + float aPixelsToTwips, PRUint32 aWidth, PRUint32 aHeight) ; + + void DisplayDepressed ( ) ; + void DisplayNormal ( ) ; // utility routine for converting from DOM values to internal enum void CheckStateToString ( CheckState inState, nsString& outStateAsString ) ; CheckState StringToCheckState ( const nsString & aStateAsString ) ; - //GFX-rendered state variables - PRBool mMouseDownOnCheckbox; + PRBool mMouseDownOnCheckbox; // for tracking clicks + PRBool mHasOnceBeenInMixedState; // since we only want to show the // atom for the "depress" attribute. We will have a CSS rule that // when this is set, draws the button depressed. - //static nsCOMPtr sDepressAtom; + static void GetDepressAtom(nsCOMPtr* outAtom) ; }; // class nsTriStateCheckboxFrame