Remove the dummy frame stuff from selects. Replace it with smarter focus rect

painting and sizing for empty selects.  Bug 314879, r+sr=dbaron


git-svn-id: svn://10.0.0.236/trunk@184162 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
bzbarsky%mit.edu
2005-11-04 23:36:27 +00:00
parent a574295133
commit d490f00b7b
9 changed files with 61 additions and 233 deletions

View File

@@ -5308,16 +5308,6 @@ nsCSSFrameConstructor::InitializeSelectFrame(nsFrameConstructorState& aState,
ProcessChildren(aState, aContent, scrolledFrame, PR_FALSE,
childItems, PR_TRUE);
// if a select is being created with zero options we need to create
// a special pseudo frame so it can be sized as best it can
nsCOMPtr<nsIDOMHTMLSelectElement> selectElement(do_QueryInterface(aContent));
if (selectElement) {
AddDummyFrameToSelect(aState, scrollFrame, scrolledFrame, &childItems,
aContent, selectElement);
}
//////////////////////////////////////////////////
//////////////////////////////////////////////////
// Set the scrolled frame's initial child lists
scrolledFrame->SetInitialChildList(aState.mPresContext, nsnull,
childItems.childList);
@@ -8972,17 +8962,6 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
}
}
// Here we have been notified that content has been appended so if
// the select now has a single item we need to go in and removed
// the dummy frame.
nsCOMPtr<nsIDOMHTMLSelectElement> sel(do_QueryInterface(aContainer));
if (sel) {
nsIContent *childContent = aContainer->GetChildAt(aNewIndexInContainer);
if (childContent) {
RemoveDummyFrameFromSelect(aContainer, childContent, sel);
}
}
#ifdef DEBUG
if (gReallyNoisyContentUpdates) {
nsIFrameDebug* fdbg = nsnull;
@@ -8997,49 +8976,6 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
return NS_OK;
}
nsresult
nsCSSFrameConstructor::AddDummyFrameToSelect(nsFrameConstructorState& aState,
nsIFrame* aListFrame,
nsIFrame* aParentFrame,
nsFrameItems* aChildItems,
nsIContent* aContainer,
nsIDOMHTMLSelectElement* aSelectElement)
{
PRUint32 numOptions = 0;
nsresult rv = aSelectElement->GetLength(&numOptions);
if (NS_SUCCEEDED(rv) && 0 == numOptions) {
nsISelectControlFrame* listFrame = nsnull;
CallQueryInterface(aListFrame, &listFrame);
if (listFrame) {
nsIFrame* dummyFrame;
listFrame->GetDummyFrame(&dummyFrame);
if (!dummyFrame) {
nsStyleContext* styleContext = aParentFrame->GetStyleContext();
nsIFrame* generatedFrame = nsnull;
if (CreateGeneratedContentFrame(aState, aParentFrame, aContainer,
styleContext,
nsCSSAnonBoxes::dummyOption,
&generatedFrame)) {
// Add the generated frame to the child list
if (aChildItems) {
aChildItems->AddChild(generatedFrame);
} else {
aState.mFrameManager->AppendFrames(aParentFrame, nsnull,
generatedFrame);
}
listFrame->SetDummyFrame(generatedFrame);
return NS_OK;
}
}
}
}
return NS_ERROR_FAILURE;
}
// defined below
static nsresult
DeletingFrameSubtree(nsPresContext* aPresContext,
@@ -9047,44 +8983,6 @@ DeletingFrameSubtree(nsPresContext* aPresContext,
nsFrameManager* aFrameManager,
nsIFrame* aFrame);
nsresult
nsCSSFrameConstructor::RemoveDummyFrameFromSelect(nsIContent* aContainer,
nsIContent* aChild,
nsIDOMHTMLSelectElement * aSelectElement)
{
// Check to see if this is the first thing we have added to this frame.
PRUint32 numOptions = 0;
nsresult rv = aSelectElement->GetLength(&numOptions);
if (NS_SUCCEEDED(rv) && numOptions > 0) {
nsIFrame* frame = mPresShell->GetPrimaryFrameFor(aContainer);
if (frame) {
nsISelectControlFrame* listFrame = nsnull;
CallQueryInterface(frame, &listFrame);
if (listFrame) {
nsIFrame* dummyFrame;
listFrame->GetDummyFrame(&dummyFrame);
if (dummyFrame) {
listFrame->SetDummyFrame(nsnull);
// get the child's parent frame (which ought to be the list frame)
nsIFrame* parentFrame = dummyFrame->GetParent();
nsFrameManager *frameManager = mPresShell->FrameManager();
DeletingFrameSubtree(mPresShell->GetPresContext(), mPresShell,
frameManager, dummyFrame);
frameManager->RemoveFrame(parentFrame, nsnull, dummyFrame);
return NS_OK;
}
}
}
}
return NS_ERROR_FAILURE;
}
// Return TRUE if the insertion of aChild into aParent1,2 should force a reframe. aParent1 is
// the special inline container which contains a block. aParentFrame is approximately aParent1's
// primary frame and will be set to the correct parent of aChild if a reframe is not necessary.
@@ -9646,12 +9544,6 @@ nsCSSFrameConstructor::ContentInserted(nsIContent* aContainer,
}
}
}
// Here we have been notified that content has been insert
// so if the select now has a single item
// we need to go in and removed the dummy frame
nsCOMPtr<nsIDOMHTMLSelectElement> selectElement = do_QueryInterface(aContainer);
if (selectElement)
RemoveDummyFrameFromSelect(aContainer, aChild, selectElement);
#ifdef DEBUG
if (gReallyNoisyContentUpdates && parentFrame) {
@@ -9908,26 +9800,6 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer,
frameManager->ClearUndisplayedContentIn(aChild, aContainer);
}
// When the last item is removed from a select,
// we need to add a pseudo frame so select gets sized as the best it can
// so here we see if it is a select and then we get the number of options
if (aContainer && childFrame) {
nsCOMPtr<nsIDOMHTMLSelectElement> selectElement = do_QueryInterface(aContainer);
if (selectElement) {
// XXX temp needed only native controls
nsIFrame* selectFrame = mPresShell->GetPrimaryFrameFor(aContainer);
// For "select" add the pseudo frame after the last item is deleted
nsIFrame* parentFrame = childFrame->GetParent();
if (parentFrame && parentFrame != selectFrame) {
nsFrameConstructorState state(mPresShell,
nsnull, nsnull, nsnull);
AddDummyFrameToSelect(state, selectFrame, parentFrame, nsnull,
aContainer, selectElement);
}
}
}
#ifdef MOZ_XUL
if (NotifyListBoxBody(presContext, aContainer, aChild, aIndexInContainer,
mDocument, childFrame, gUseXBLForms, CONTENT_REMOVED))

View File

@@ -666,17 +666,6 @@ private:
ImageFrameCreatorFunc aFunc,
nsIFrame** aFrame);
nsresult AddDummyFrameToSelect(nsFrameConstructorState& aState,
nsIFrame* aListFrame,
nsIFrame* aParentFrame,
nsFrameItems* aChildItems,
nsIContent* aContainer,
nsIDOMHTMLSelectElement* aSelectElement);
nsresult RemoveDummyFrameFromSelect(nsIContent* aContainer,
nsIContent* aChild,
nsIDOMHTMLSelectElement* aSelectElement);
nsIFrame* GetFrameFor(nsIContent* aContent);
nsIFrame* GetAbsoluteContainingBlock(nsIFrame* aFrame);

View File

@@ -1917,32 +1917,6 @@ nsComboboxControlFrame::GetOptionSelected(PRInt32 aIndex, PRBool* aValue)
return listFrame->GetOptionSelected(aIndex, aValue);
}
//---------------------------------------------------------
// Used by layout to determine if we have a fake option
NS_IMETHODIMP
nsComboboxControlFrame::GetDummyFrame(nsIFrame** aFrame)
{
nsISelectControlFrame* listFrame = nsnull;
NS_ASSERTION(mDropdownFrame, "No dropdown frame!");
CallQueryInterface(mDropdownFrame, &listFrame);
NS_ASSERTION(listFrame, "No list frame!");
return listFrame->GetDummyFrame(aFrame);
}
NS_IMETHODIMP
nsComboboxControlFrame::SetDummyFrame(nsIFrame* aFrame)
{
nsISelectControlFrame* listFrame = nsnull;
NS_ASSERTION(mDropdownFrame, "No dropdown frame!");
CallQueryInterface(mDropdownFrame, &listFrame);
NS_ASSERTION(listFrame, "No list frame!");
return listFrame->SetDummyFrame(aFrame);
}
NS_IMETHODIMP
nsComboboxControlFrame::OnSetSelectedIndex(PRInt32 aOldIndex, PRInt32 aNewIndex)
{

View File

@@ -184,8 +184,6 @@ public:
NS_IMETHOD OnOptionSelected(nsPresContext* aPresContext,
PRInt32 aIndex,
PRBool aSelected);
NS_IMETHOD GetDummyFrame(nsIFrame** aFrame);
NS_IMETHOD SetDummyFrame(nsIFrame* aFrame);
NS_IMETHOD OnSetSelectedIndex(PRInt32 aOldIndex, PRInt32 aNewIndex);
//nsIRollupListener

View File

@@ -42,10 +42,10 @@
#include "nsISupports.h"
// IID for the nsISelectControlFrame class
// 264dc2f5-1cca-47dd-9ebc-699c430be00a
// f8a1b329-d0d8-4bd5-a9ab-08c3c0f2f166
#define NS_ISELECTCONTROLFRAME_IID \
{ 0x264dc2f5, 0x1cca, 0x47dd, \
{ 0x9e, 0xbc, 0x69, 0x9c, 0x43, 0x0b, 0xe0, 0x0a } }
{ 0xf8a1b329, 0xd0d8, 0x4bd5, \
{ 0xa9, 0xab, 0x08, 0xc3, 0xc0, 0xf2, 0xf1, 0x66 } }
class nsIDOMHTMLOptionElement;
@@ -87,12 +87,6 @@ public:
PRInt32 aIndex,
PRBool aSelected) = 0;
/**
* For the content model to tell if there's a dummy frame or not
*/
NS_IMETHOD GetDummyFrame(nsIFrame** aFrame) = 0;
NS_IMETHOD SetDummyFrame(nsIFrame* aFrame) = 0;
/**
* Notify the frame when selectedIndex was changed
*/

View File

@@ -301,8 +301,6 @@ nsListControlFrame::nsListControlFrame(nsIPresShell* aShell,
mOverrideReflowOpt = PR_FALSE;
mPassId = 0;
mDummyFrame = nsnull;
mControlSelectMode = PR_FALSE;
REFLOW_COUNTER_INIT()
}
@@ -486,20 +484,25 @@ void nsListControlFrame::PaintFocus(nsIRenderingContext& aRC, nsFramePaintLayer
childframe = presShell->GetPrimaryFrameFor(focusedContent);
}
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
// Failing all else, try the first thing we have.
childframe = containerFrame->GetFirstChild(nsnull);
result = NS_OK;
}
}
if (!childframe) return;
nsRect fRect;
if (childframe) {
// get the child rect
fRect = childframe->GetRect();
// get the child rect
nsRect fRect = childframe->GetRect();
// get it into the coordinates of containerFrame
fRect.MoveBy(childframe->GetParent()->GetOffsetTo(containerFrame));
} else {
fRect.x = fRect.y = 0;
fRect.width = mRect.width;
fRect.height = CalcFallbackRowHeight(0);
}
// get it into the coordinates of containerFrame
fRect.MoveBy(childframe->GetParent()->GetOffsetTo(containerFrame));
PRBool lastItemIsSelected = PR_FALSE;
if (focusedIndex != kNothingSelected) {
nsCOMPtr<nsIDOMNode> node;
@@ -898,30 +901,8 @@ nsListControlFrame::Reflow(nsPresContext* aPresContext,
PRInt32 length = 0;
GetNumberOfOptions(&length);
// If there is only one option and that option's content is empty
// then heightOfARow is zero, so we need to go measure
// the height of the option as if it had some text.
if (heightOfARow == 0 && length > 0) {
nsCOMPtr<nsIContent> option = GetOptionContent(0);
if (option) {
nsIFrame * optFrame = GetPresContext()->PresShell()->
GetPrimaryFrameFor(option);
if (optFrame) {
nsStyleContext* optStyle = optFrame->GetStyleContext();
if (optStyle) {
const nsStyleFont* styleFont = optStyle->GetStyleFont();
nsCOMPtr<nsIFontMetrics> fontMet;
nsresult result = aPresContext->DeviceContext()->
GetMetricsFor(styleFont->mFont, *getter_AddRefs(fontMet));
if (NS_SUCCEEDED(result) && fontMet) {
if (fontMet) {
fontMet->GetHeight(heightOfARow);
mMaxHeight = heightOfARow;
}
}
}
}
}
if (heightOfARow == 0) {
heightOfARow = CalcFallbackRowHeight(length);
}
mMaxHeight = heightOfARow;
@@ -2167,22 +2148,6 @@ nsListControlFrame::GetOptionSelected(PRInt32 aIndex, PRBool* aValue)
return NS_OK;
}
//---------------------------------------------------------
// Used by layout to determine if we have a fake option
NS_IMETHODIMP
nsListControlFrame::GetDummyFrame(nsIFrame** aFrame)
{
(*aFrame) = mDummyFrame;
return NS_OK;
}
NS_IMETHODIMP
nsListControlFrame::SetDummyFrame(nsIFrame* aFrame)
{
mDummyFrame = aFrame;
return NS_OK;
}
NS_IMETHODIMP
nsListControlFrame::OnSetSelectedIndex(PRInt32 aOldIndex, PRInt32 aNewIndex)
{
@@ -2397,6 +2362,44 @@ nsListControlFrame::IsLeftButton(nsIDOMEvent* aMouseEvent)
return PR_FALSE;
}
nscoord
nsListControlFrame::CalcFallbackRowHeight(PRInt32 aNumOptions)
{
const nsStyleFont* styleFont = nsnull;
if (aNumOptions > 0) {
// Try the first option
nsCOMPtr<nsIContent> option = GetOptionContent(0);
if (option) {
nsIFrame * optFrame = GetPresContext()->PresShell()->
GetPrimaryFrameFor(option);
if (optFrame) {
styleFont = optFrame->GetStyleFont();
}
}
}
if (!styleFont) {
// Fall back to our own font
styleFont = GetStyleFont();
}
NS_ASSERTION(styleFont, "Must have font style by now!");
nscoord rowHeight = 0;
nsCOMPtr<nsIFontMetrics> fontMet;
nsresult result = GetPresContext()->DeviceContext()->
GetMetricsFor(styleFont->mFont, *getter_AddRefs(fontMet));
if (NS_SUCCEEDED(result) && fontMet) {
if (fontMet) {
fontMet->GetHeight(rowHeight);
}
}
return rowHeight;
}
//----------------------------------------------------------------------
// nsIDOMMouseListener
//----------------------------------------------------------------------

View File

@@ -182,8 +182,6 @@ public:
NS_IMETHOD OnOptionSelected(nsPresContext* aPresContext,
PRInt32 aIndex,
PRBool aSelected);
NS_IMETHOD GetDummyFrame(nsIFrame** aFrame);
NS_IMETHOD SetDummyFrame(nsIFrame* aFrame);
NS_IMETHOD OnSetSelectedIndex(PRInt32 aOldIndex, PRInt32 aNewIndex);
// mouse event listeners
@@ -241,6 +239,10 @@ protected:
PRInt32 GetIndexFromContent(nsIContent *aContent);
PRBool IsLeftButton(nsIDOMEvent* aMouseEvent);
// aNumOptions is the number of options we have; if we have none,
// we'll just guess at a row height based on our own style.
nscoord CalcFallbackRowHeight(PRInt32 aNumOptions);
// Dropped down stuff
void SetComboboxItem(PRInt32 aIndex);
PRBool IsInDropDownMode() const;
@@ -293,8 +295,6 @@ protected:
nsRefPtr<nsListEventListener> mEventListener;
nsIFrame* mDummyFrame;
//Resize Reflow OpitmizationSize;
nsSize mCacheSize;
nscoord mCachedAscent;

View File

@@ -185,9 +185,8 @@ select > input[type="button"]:active {
background-image: url("arrowd.gif") !important;
}
select::-moz-dummy-option {
visibility: hidden;
content: "XX"; /* demo 8, edge case test 1 */
select:empty {
width: 2.5em;
}
*|*::-moz-display-comboboxcontrol-frame {

View File

@@ -67,7 +67,6 @@ CSS_ANON_BOX(framesetBlank, ":-moz-frameset-blank")
CSS_ANON_BOX(radio, ":-moz-radio")
CSS_ANON_BOX(check, ":-moz-checkbox")
CSS_ANON_BOX(mozDisplayComboboxControlFrame, ":-moz-display-comboboxcontrol-frame")
CSS_ANON_BOX(dummyOption, ":-moz-dummy-option")
CSS_ANON_BOX(table, ":-moz-table")
CSS_ANON_BOX(tableCell, ":-moz-table-cell")