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
This commit is contained in:
rods%netscape.com
2002-04-20 12:29:40 +00:00
parent 21202829f3
commit 2ec240eb5c
4 changed files with 217 additions and 2 deletions

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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<nsIPresShell> 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<nsIContent> focusedContent;
nsCOMPtr<nsIDOMNSHTMLSelectElement> selectNSElement(do_QueryInterface(mContent));
NS_ASSERTION(selectNSElement, "Can't be null");
nsCOMPtr<nsISelectElement> 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<nsIDOMHTMLSelectElement> 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<nsIDOMNode> node;
PRUint32 length;
selectHTMLElement->GetLength(&length);
if (length) {
// find the first non-disabled item
PRBool isDisabled = PR_TRUE;
for (PRInt32 i=0;i<PRInt32(length) && isDisabled;i++) {
if (NS_FAILED(selectNSElement->Item(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<nsIContent> parentContent;
focusedContent->GetParent(*getter_AddRefs(parentContent));
nsCOMPtr<nsIDOMHTMLOptGroupElement> 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<nsIDOMNode> node;
if (NS_SUCCEEDED(selectNSElement->Item(focusedIndex, getter_AddRefs(node)))) {
nsCOMPtr<nsIDOMHTMLOptionElement> 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<nsILookAndFeel> 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");
}

View File

@@ -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