From 2ec240eb5cbd0818a207af06f80f4943bbbe5ec7 Mon Sep 17 00:00:00 2001 From: "rods%netscape.com" Date: Sat, 20 Apr 2002 12:29:40 +0000 Subject: [PATCH] This displays a focus ring when the listbox has focus for HTML listboxes (NOT XBL listboxes) The idea is that when the SelectAreaFrame (which is the parent) of the options is asked to paint then it asks the ListControlframe to paint the focus ring in the correct spot. It need to find the first non-disabled option (ignoring opt groups) and if nothing is selected it need to find the first frame which is the dummy option. Also, it now track thru a static data member which listControl frame currently has focus (this was taking from how comboboxes track it internally) Bug 64165 r=jkeiser sr=attinasi adt=jaime git-svn-id: svn://10.0.0.236/branches/MOZILLA_1_0_BRANCH@119486 18797224-902f-48f8-a5cc-f745e15eee43 --- .../html/base/src/nsSelectsAreaFrame.cpp | 16 ++ .../layout/html/base/src/nsSelectsAreaFrame.h | 5 + .../html/forms/src/nsListControlFrame.cpp | 194 +++++++++++++++++- .../html/forms/src/nsListControlFrame.h | 4 + 4 files changed, 217 insertions(+), 2 deletions(-) diff --git a/mozilla/layout/html/base/src/nsSelectsAreaFrame.cpp b/mozilla/layout/html/base/src/nsSelectsAreaFrame.cpp index 5ac39a7a0dd..e80156379a1 100644 --- a/mozilla/layout/html/base/src/nsSelectsAreaFrame.cpp +++ b/mozilla/layout/html/base/src/nsSelectsAreaFrame.cpp @@ -40,6 +40,7 @@ #include "nsIDOMHTMLOptionElement.h" #include "nsIContent.h" #include "nsIStyleContext.h" +#include "nsListControlFrame.h" nsresult NS_NewSelectsAreaFrame(nsIPresShell* aShell, nsIFrame** aNewFrame, PRUint32 aFlags) @@ -131,3 +132,18 @@ nsSelectsAreaFrame::GetFrameForPoint(nsIPresContext* aPresContext, return result; } + +NS_IMETHODIMP +nsSelectsAreaFrame::Paint(nsIPresContext* aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nsFramePaintLayer aWhichLayer, + PRUint32 aFlags) +{ + nsresult rv = nsAreaFrame::Paint(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer, aFlags); + // This assumes that that the ListControlFrame is the Parent + nsListControlFrame* listFrame = NS_STATIC_CAST(nsListControlFrame*, mParent); + listFrame->PaintFocus(aRenderingContext, aWhichLayer); + + return rv; +} \ No newline at end of file diff --git a/mozilla/layout/html/base/src/nsSelectsAreaFrame.h b/mozilla/layout/html/base/src/nsSelectsAreaFrame.h index 52228850f6a..9b1a5e2c005 100644 --- a/mozilla/layout/html/base/src/nsSelectsAreaFrame.h +++ b/mozilla/layout/html/base/src/nsSelectsAreaFrame.h @@ -56,6 +56,11 @@ public: NS_IMETHOD GetFrameForPoint(nsIPresContext* aPresContext, const nsPoint& aPoint, nsFramePaintLayer aWhichLayer, nsIFrame** aFrame); + NS_IMETHOD Paint(nsIPresContext* aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nsFramePaintLayer aWhichLayer, + PRUint32 aFlags = 0); protected: PRBool IsOptionElement(nsIContent* aContent); PRBool IsOptionElementFrame(nsIFrame *aFrame); diff --git a/mozilla/layout/html/forms/src/nsListControlFrame.cpp b/mozilla/layout/html/forms/src/nsListControlFrame.cpp index 58e61aa3549..66bfa1aeba6 100644 --- a/mozilla/layout/html/forms/src/nsListControlFrame.cpp +++ b/mozilla/layout/html/forms/src/nsListControlFrame.cpp @@ -51,6 +51,7 @@ #include "nsIDOMHTMLCollection.h" #include "nsIDOMNSHTMLOptionCollectn.h" #include "nsIDOMHTMLSelectElement.h" +#include "nsIDOMNSHTMLSelectElement.h" #include "nsIDOMHTMLOptionElement.h" #include "nsIComboboxControlFrame.h" #include "nsIViewManager.h" @@ -82,6 +83,8 @@ #endif #include "nsISelectElement.h" #include "nsIPrivateDOMEvent.h" +#include "nsCSSRendering.h" +#include "nsILookAndFeel.h" // Timer Includes #include "nsITimer.h" @@ -94,7 +97,9 @@ const PRInt32 kNothingSelected = -1; const PRInt32 kMaxZ = 0x7fffffff; //XXX: Shouldn't there be a define somewhere for MaxInt for PRInt32 const PRInt32 kNoSizeSpecified = -1; -// We now use the :checked pseudoclass for the option selected state + +nsListControlFrame * nsListControlFrame::mFocused = nsnull; + //--------------------------------------------------------- nsresult @@ -522,6 +527,176 @@ nsListControlFrame::Paint(nsIPresContext* aPresContext, } +void nsListControlFrame::PaintFocus(nsIRenderingContext& aRC, nsFramePaintLayer aWhichLayer) +{ + if (NS_FRAME_PAINT_LAYER_FOREGROUND != aWhichLayer) return; + + if (mFocused != this) return; + + // The mEndSelectionIndex is what is currently being selected + // use the selected index if this is kNothingSelected + PRInt32 focusedIndex; + if (mEndSelectionIndex == kNothingSelected) { + GetSelectedIndex(&focusedIndex); + } else { + focusedIndex = mEndSelectionIndex; + } + + nsIScrollableView * scrollableView; + GetScrollableView(scrollableView); + if (!scrollableView) return; + + nsCOMPtr presShell; + mPresContext->GetShell(getter_AddRefs(presShell)); + if (!presShell) return; + + nsIFrame* containerFrame; + GetOptionsContainer(mPresContext, &containerFrame); + if (!containerFrame) return; + + nsIFrame * childframe = nsnull; + nsresult result = NS_ERROR_FAILURE; + + nsCOMPtr focusedContent; + + nsCOMPtr selectNSElement(do_QueryInterface(mContent)); + NS_ASSERTION(selectNSElement, "Can't be null"); + + nsCOMPtr selectElement(do_QueryInterface(mContent)); + NS_ASSERTION(selectElement, "Can't be null"); + + // If we have a selected index then get that child frame + if (focusedIndex != kNothingSelected) { + focusedContent = getter_AddRefs(GetOptionContent(focusedIndex)); + // otherwise we find the focusedContent's frame and scroll to it + if (focusedContent) { + result = presShell->GetPrimaryFrameFor(focusedContent, &childframe); + } + } else { + nsCOMPtr selectHTMLElement(do_QueryInterface(mContent)); + NS_ASSERTION(selectElement, "Can't be null"); + + // Since there isn't a selected item we need to show a focus ring around the first + // non-disabled item and skip all the option group elements (nodes) + nsCOMPtr node; + + PRUint32 length; + selectHTMLElement->GetLength(&length); + if (length) { + // find the first non-disabled item + PRBool isDisabled = PR_TRUE; + for (PRInt32 i=0;iItem(i, getter_AddRefs(node))) || !node) { + break; + } + if (NS_FAILED(selectElement->IsOptionDisabled(i, &isDisabled))) { + break; + } + if (isDisabled) { + node = nsnull; + } else { + break; + } + } + if (!node) { + return; + } + } + + // if we found a node use, if not get the first child (this is for empty selects) + if (node) { + focusedContent = do_QueryInterface(node); + result = presShell->GetPrimaryFrameFor(focusedContent, &childframe); + } + if (!childframe) { + // The only way we can get right here is that there are no options + // and we need to get the dummy frame so it has the focus ring + result = containerFrame->FirstChild(mPresContext, nsnull, &childframe); + } + } + + if (NS_FAILED(result) || !childframe) return; + + const nsIView * clippedView; + scrollableView->GetClipView(&clippedView); + if (!clippedView) return; + + nscoord x; + nscoord y; + scrollableView->GetScrollPosition(x,y); + // get the clipped rect + nsRect rect; + clippedView->GetBounds(rect); + + // now move it by the offset of the scroll position + rect.x = 0; + rect.y = 0; + rect.MoveBy(x,y); + + // get the child rect + nsRect fRect; + childframe->GetRect(fRect); + + nsRect clipRect; + containerFrame->GetRect(clipRect); + clipRect.x = 0; + clipRect.y = 0; + + PRBool clipEmpty; + aRC.PushState(); + aRC.SetClipRect(clipRect, nsClipCombine_kReplace, clipEmpty); + + // adjust position is it is a child of a option group + if (focusedContent) { + nsRect optRect(0,0,0,0); + nsCOMPtr parentContent; + focusedContent->GetParent(*getter_AddRefs(parentContent)); + nsCOMPtr optGroup(do_QueryInterface(parentContent)); + if (optGroup) { + nsIFrame * optFrame; + result = presShell->GetPrimaryFrameFor(parentContent, &optFrame); + if (NS_SUCCEEDED(result) && optFrame) { + optFrame->GetRect(optRect); + } + } + fRect.y += optRect.y; + } + + PRBool lastItemIsSelected = PR_FALSE; + nsCOMPtr node; + if (NS_SUCCEEDED(selectNSElement->Item(focusedIndex, getter_AddRefs(node)))) { + nsCOMPtr domOpt(do_QueryInterface(node)); + if (domOpt) { + selectElement->IsOptionSelected(domOpt, &lastItemIsSelected); + } + } + + // set up back stop colors and then ask L&F service for the real colors + nscolor color; + nsCOMPtr lookAndFeel; + mPresContext->GetLookAndFeel(getter_AddRefs(lookAndFeel)); + if (lookAndFeel){ + lookAndFeel->GetColor(lastItemIsSelected?nsILookAndFeel::eColor_WidgetSelectForeground:nsILookAndFeel::eColor_WidgetSelectBackground, color); + } else { + // Use some backstop colors if for some reason we don't have a L&F object + color = lastItemIsSelected? NS_RGB(245,219,149) : NS_RGB(0,0,0); + } + + float p2t; + mPresContext->GetScaledPixelsToTwips(&p2t); + nscoord onePixelInTwips = NSToCoordRound(p2t); + + nsRect dirty; + nscolor colors[] = {color, color, color, color}; + PRUint8 borderStyle[] = {NS_STYLE_BORDER_STYLE_DOTTED, NS_STYLE_BORDER_STYLE_DOTTED, NS_STYLE_BORDER_STYLE_DOTTED, NS_STYLE_BORDER_STYLE_DOTTED}; + nsRect innerRect = fRect; + innerRect.Deflate(nsSize(onePixelInTwips, onePixelInTwips)); + nsCSSRendering::DrawDashedSides(0, aRC, dirty, borderStyle, colors, fRect, innerRect, 0, nsnull); + + aRC.PopState(clipEmpty); + +} + //--------------------------------------------------------- // Frames are not refcounted, no need to AddRef NS_IMETHODIMP @@ -1831,7 +2006,18 @@ nsListControlFrame::GetName(nsAString* aResult) void nsListControlFrame::SetFocus(PRBool aOn, PRBool aRepaint) { - // XXX:TODO Make set focus work + if (aOn) { + PRInt32 selectedIndex; + GetSelectedIndex(&selectedIndex); + ScrollToIndex(selectedIndex); + mFocused = this; + } else { + mFocused = nsnull; + } + + // Make sure the SelectArea frame gets painted + Invalidate(mPresContext, nsRect(0,0,mRect.width,mRect.height), PR_TRUE); + } //--------------------------------------------------------- @@ -3311,6 +3497,10 @@ nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent) presShell->FlushPendingNotifications(PR_FALSE); } REFLOW_DEBUG_MSG2(" After: %d\n", newIndex); + + // Make sure the SelectArea frame gets painted + Invalidate(mPresContext, nsRect(0,0,mRect.width,mRect.height), PR_TRUE); + } else { REFLOW_DEBUG_MSG(" After: SKIPPED it\n"); } diff --git a/mozilla/layout/html/forms/src/nsListControlFrame.h b/mozilla/layout/html/forms/src/nsListControlFrame.h index e6c93e4d526..49b3b4cd391 100644 --- a/mozilla/layout/html/forms/src/nsListControlFrame.h +++ b/mozilla/layout/html/forms/src/nsListControlFrame.h @@ -320,6 +320,8 @@ public: // Helper void SetPassId(PRInt16 aId) { mPassId = aId; } + void PaintFocus(nsIRenderingContext& aRC, nsFramePaintLayer aWhichLayer); + protected: nsresult IsOptionDisabled(PRInt32 anIndex, PRBool &aIsDisabled); @@ -422,6 +424,8 @@ protected: nsSize mCachedUnconstrainedSize; nsSize mCachedAvailableSize; + static nsListControlFrame * mFocused; + #ifdef DO_REFLOW_COUNTER PRInt32 mReflowId; #endif