overhaul menus to use spiffy new titledbutton features

This commit is contained in:
shaver%netscape.com 1999-09-17 20:12:02 +00:00
parent 9e4d4dd4cc
commit 6a26e5fb42
6 changed files with 246 additions and 183 deletions

View File

@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
@ -103,6 +103,7 @@ NS_IMETHODIMP nsMenuFrame::QueryInterface(REFNSIID aIID, void** aInstancePtr)
nsMenuFrame::nsMenuFrame()
: mIsMenu(PR_FALSE),
mMenuOpen(PR_FALSE),
mHasAnonymousContent(PR_FALSE),
mMenuParent(nsnull),
mPresContext(nsnull)
{
@ -365,13 +366,27 @@ nsMenuFrame::AttributeChanged(nsIPresContext* aPresContext,
nsIAtom* aAttribute,
PRInt32 aHint)
{
nsAutoString value;
if (aAttribute == nsXULAtoms::open) {
nsAutoString openVal;
nsCOMPtr<nsIDOMElement> domElement = do_QueryInterface(aChild);
domElement->GetAttribute("open", openVal);
if (openVal == "true")
aChild->GetAttribute(kNameSpaceID_None, aAttribute, value);
if (value == "true")
OpenMenuInternal(PR_TRUE);
else OpenMenuInternal(PR_FALSE);
else
OpenMenuInternal(PR_FALSE);
}
if (mHasAnonymousContent) {
if (aAttribute == nsXULAtoms::accesskey ||
aAttribute == nsHTMLAtoms::value) {
/* update accesskey or value on menu-left */
aChild->GetAttribute(kNameSpaceID_None, aAttribute, value);
mMenuText->SetAttribute(kNameSpaceID_None, aAttribute, value, PR_TRUE);
} else if (aAttribute == nsXULAtoms::acceltext) {
/* update content in accel-text */
aChild->GetAttribute(kNameSpaceID_None, aAttribute, value);
mAccelText->SetData(value);
}
}
return NS_OK;
@ -686,7 +701,7 @@ nsMenuFrame::CreateAnonymousContent(nsISupportsArray& aAnonymousChildren)
PRInt32 childCount;
mContent->ChildCount(childCount);
PRBool createContent = PR_TRUE;
mHasAnonymousContent = PR_TRUE;
for (PRInt32 i = 0; i < childCount; i++) {
// XXX Should optimize this to look for a display type of none.
// Not sure how to do this. For now screen out some known tags.
@ -697,12 +712,12 @@ nsMenuFrame::CreateAnonymousContent(nsISupportsArray& aAnonymousChildren)
if (tag.get() != nsXULAtoms::menupopup &&
tag.get() != nsXULAtoms::templateAtom &&
tag.get() != nsXULAtoms::observes) {
createContent = PR_FALSE;
mHasAnonymousContent = PR_FALSE;
break;
}
}
if (!createContent)
if (!mHasAnonymousContent)
return NS_OK;
nsCOMPtr<nsIDocument> idocument;
@ -717,60 +732,43 @@ nsMenuFrame::CreateAnonymousContent(nsISupportsArray& aAnonymousChildren)
nsCOMPtr<nsIDOMElement> node;
nsCOMPtr<nsIContent> content;
// Create the "menu-left" object. It's a titledbutton. Don't make this for menu bar items.
PRBool onMenuBar = PR_FALSE;
if (mMenuParent)
mMenuParent->IsMenuBar(onMenuBar);
if (!onMenuBar) { // XXX Maybe we should make one for a .menubar-left class so that the option exists
nsDocument->CreateElementWithNameSpace("titledbutton", xulNamespace, getter_AddRefs(node));
content = do_QueryInterface(node);
content->SetAttribute(kNameSpaceID_None, classAtom, "menu-left", PR_FALSE);
aAnonymousChildren.AppendElement(content);
}
// Create the div object. Split the text based on our accesskey value and
// make a div that contains the before string, an underline node with the
// access key as a child, and the after string.
nsDocument->CreateElementWithNameSpace("div", htmlNamespace, getter_AddRefs(node));
/* Create .menu-left (.menubar-left) titledbutton for icon. */
nsDocument->CreateElementWithNameSpace("titledbutton", xulNamespace,
getter_AddRefs(node));
content = do_QueryInterface(node);
content->SetAttribute(kNameSpaceID_None, classAtom,
onMenuBar ? "menubar-left" : "menu-left" , PR_FALSE);
aAnonymousChildren.AppendElement(content);
nsAutoString beforeString;
nsAutoString accessString;
nsAutoString afterString;
SplitOnShortcut(beforeString, accessString, afterString);
/*
* Create the .menu-text titledbutton, and propagate crop, accesskey and
* value attributes. If we're a menubar, make the class menubar-text
* instead.
*/
nsDocument->CreateElementWithNameSpace("titledbutton", xulNamespace,
getter_AddRefs(node));
content = do_QueryInterface(node);
content->SetAttribute(kNameSpaceID_None, classAtom,
onMenuBar ? "menubar-text" : "menu-text", PR_FALSE);
nsAutoString accessKey, value, crop;
mMenuText = content;
mContent->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::value, value);
content->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::value, value, PR_FALSE);
mContent->GetAttribute(kNameSpaceID_None, nsXULAtoms::accesskey, accessKey);
content->SetAttribute(kNameSpaceID_None, nsXULAtoms::accesskey, accessKey,
PR_FALSE);
// Create the before text node.
nsCOMPtr<nsIDOMText> beforeTextNode;
if (beforeString != "")
document->CreateTextNode(beforeString, getter_AddRefs(beforeTextNode));
// Create the <html:u> element.
nsCOMPtr<nsIDOMElement> underlineElement;
mContent->GetAttribute(kNameSpaceID_None, nsXULAtoms::crop, crop);
content->SetAttribute(kNameSpaceID_None, nsXULAtoms::crop,
crop == "" ? "right" : crop, PR_FALSE);
if (accessString != "") {
nsDocument->CreateElementWithNameSpace("u", htmlNamespace, getter_AddRefs(underlineElement));
// Create the child of the <html:u> element and append it to the U element.
nsCOMPtr<nsIDOMText> accessTextNode;
document->CreateTextNode(accessString, getter_AddRefs(accessTextNode));
underlineElement->AppendChild(accessTextNode, getter_AddRefs(dummyResult));
}
// Create the after text node.
nsCOMPtr<nsIDOMText> afterTextNode;
if (afterString != "") {
document->CreateTextNode(afterString, getter_AddRefs(afterTextNode));
}
// Append the before, the underline, and the after to the div.
if (beforeTextNode)
node->AppendChild(beforeTextNode, getter_AddRefs(dummyResult));
if (underlineElement)
node->AppendChild(underlineElement, getter_AddRefs(dummyResult));
if (afterTextNode)
node->AppendChild(afterTextNode, getter_AddRefs(dummyResult));
// append now, after we've set all the attributes
aAnonymousChildren.AppendElement(content);
// Create a spring that serves as padding between the text and the
// accelerator.
@ -791,6 +789,7 @@ nsMenuFrame::CreateAnonymousContent(nsISupportsArray& aAnonymousChildren)
nsCOMPtr<nsIDOMText> accelNode;
document->CreateTextNode(accelString, getter_AddRefs(accelNode));
mAccelText = accelNode;
node->AppendChild(accelNode, getter_AddRefs(dummyResult));
}
@ -831,13 +830,10 @@ nsMenuFrame::SplitOnShortcut(nsString& aBeforeString, nsString& aAccessString, n
{
nsString value;
nsString accessKey;
mContent->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::value, value);
aBeforeString = value;
aAccessString = "";
aAfterString = "";
mContent->GetAttribute(kNameSpaceID_None, nsXULAtoms::accesskey, accessKey);
if (accessKey == "") // Nothing to do.
return;

View File

@ -34,6 +34,8 @@
#include "nsITimer.h"
#include "nsITimerCallback.h"
#include "nsISupportsArray.h"
#include "nsIDOMText.h"
#include "nsIContent.h"
nsresult NS_NewMenuFrame(nsIFrame** aResult, PRUint32 aFlags) ;
@ -161,6 +163,9 @@ protected:
nsFrameList mPopupFrames;
PRBool mIsMenu; // Whether or not we can even have children or not.
PRBool mMenuOpen;
PRBool mHasAnonymousContent; // Do we have anonymous content frames?
nsCOMPtr<nsIContent> mMenuText;
nsCOMPtr<nsIDOMText> mAccelText;
nsIMenuParent* mMenuParent; // Our parent menu.
nsCOMPtr<nsITimer> mOpenTimer;
nsIPresContext* mPresContext; // Our pres context.

View File

@ -603,7 +603,9 @@ nsMenuPopupFrame::HandleEvent(nsIPresContext& aPresContext,
{
aEventStatus = nsEventStatus_eConsumeDoDefault;
if (aEvent->message == NS_MOUSE_MOVE) {
#ifdef DEBUG_hyatt
printf("Mouse enter!\n");
#endif
//HandleMouseEnterEvent(aPresContext, aEvent, aEventStatus);
}

View File

@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "License"); you may not use this file except in
@ -191,8 +191,12 @@ nsTitledButtonFrame::AttributeChanged(nsIPresContext* aPresContext,
shell->AppendReflowCommand(reflowCmd);
}
if (aAttribute == nsXULAtoms::accesskey)
nsFrame::Invalidate(nsRect(0, 0, mRect.width, mRect.height), PR_FALSE);
if (aAttribute == nsXULAtoms::accesskey ||
aAttribute == nsHTMLAtoms::value ||
aAttribute == nsXULAtoms::crop) {
UpdateAccessUnderline();
nsFrame::Invalidate(nsRect(0, 0, mRect.width, mRect.height), PR_FALSE);
}
// redraw
mRenderer->Redraw();
@ -219,6 +223,7 @@ nsTitledButtonFrame::nsTitledButtonFrame()
mAlign = NS_SIDE_TOP;
mCropType = CropRight;
mNeedsLayout = PR_TRUE;
mNeedsAccessUpdate = PR_TRUE;
mHasImage = PR_FALSE;
mHasOnceBeenInMixedState = PR_FALSE;
mRenderer = new nsTitledButtonRenderer();
@ -404,7 +409,10 @@ nsTitledButtonFrame::UpdateImage(nsIPresContext& aPresContext)
}
}
#ifdef DEBUG_shaver
#include <execinfo.h>
static PRBool dump_stack = PR_FALSE;
#endif
NS_IMETHODIMP
nsTitledButtonFrame::Paint(nsIPresContext& aPresContext,
@ -412,7 +420,20 @@ nsTitledButtonFrame::Paint(nsIPresContext& aPresContext,
const nsRect& aDirtyRect,
nsFramePaintLayer aWhichLayer)
{
#ifdef DEBUG_shaver
if (dump_stack) {
fprintf(stderr, "Paint(%p)\n", this);
void *bt[15];
int n = backtrace(bt, sizeof bt / sizeof (bt[0]));
if (n) {
char **names = backtrace_symbols(bt, n);
for (int i = 5; i < n; ++i)
printf("\t\t%s\n", names[i]);
free(names);
}
}
#endif
const nsStyleDisplay* disp = (const nsStyleDisplay*)
mStyleContext->GetStyleData(eStyleStruct_Display);
if (!disp->mVisible)
@ -451,105 +472,106 @@ nsTitledButtonFrame::LayoutTitleAndImage(nsIPresContext& aPresContext,
const nsRect& aDirtyRect,
nsFramePaintLayer aWhichLayer)
{
// and do caculations if our size changed
if (mNeedsLayout == PR_FALSE)
return;
// and do caculations if our size changed
if (mNeedsLayout == PR_FALSE)
return;
// given our rect try to place the image and text
// if they don't fit then crop the text, the image can't be squeezed.
// given our rect try to place the image and text
// if they don't fit then crop the text, the image can't be squeezed.
nsRect rect;
mRenderer->GetButtonContentRect(nsRect(0,0,mRect.width,mRect.height), rect);
nsRect rect;
mRenderer->GetButtonContentRect(nsRect(0,0,mRect.width,mRect.height), rect);
// set up some variables we will use a lot.
nscoord bottom_y = rect.y + rect.height;
nscoord top_y = rect.y;
nscoord right_x = rect.x + rect.width;
nscoord left_x = rect.x;
nscoord center_x = rect.x + rect.width/2;
nscoord center_y = rect.y + rect.height/2;
nscoord bottom_y = rect.y + rect.height;
nscoord top_y = rect.y;
nscoord right_x = rect.x + rect.width;
nscoord left_x = rect.x;
nscoord center_x = rect.x + rect.width/2;
nscoord center_y = rect.y + rect.height/2;
mCroppedTitle = "";
mCroppedTitle = "";
nscoord spacing = 0;
nscoord spacing = 0;
// if we have a title or an image we need to do spacing
if (mTitle.Length() > 0 && mHasImage)
{
spacing = mSpacing;
}
if (mTitle.Length() > 0 && mHasImage)
{
spacing = mSpacing;
}
// for each type of alignment layout of the image and the text.
switch (mAlign) {
case NS_SIDE_BOTTOM: {
// get title and center it
CalculateTitleForWidth(aPresContext, aRenderingContext, rect.width);
// for each type of alignment layout of the image and the text.
switch (mAlign) {
case NS_SIDE_BOTTOM: {
// get title and center it
CalculateTitleForWidth(aPresContext, aRenderingContext, rect.width);
if (mHasImage) {
if (mHasImage) {
// title top
mTitleRect.x = center_x - mTitleRect.width/2;
mTitleRect.y = top_y;
// title top
mTitleRect.x = center_x - mTitleRect.width/2;
mTitleRect.y = top_y;
// image bottom center
mImageRect.x = center_x - mImageRect.width/2;
mImageRect.y = rect.y + (rect.height - mTitleRect.height - spacing)/2 - mImageRect.height/2 + mTitleRect.height + spacing;
} else {
// title bottom
mTitleRect.x = center_x - mTitleRect.width/2;
mTitleRect.y = bottom_y - mTitleRect.height;
}
}
break;
case NS_SIDE_TOP:{
// get title and center it
CalculateTitleForWidth(aPresContext, aRenderingContext, rect.width);
// image bottom center
mImageRect.x = center_x - mImageRect.width/2;
mImageRect.y = rect.y + (rect.height - mTitleRect.height - spacing)/2 - mImageRect.height/2 + mTitleRect.height + spacing;
} else {
// title bottom
mTitleRect.x = center_x - mTitleRect.width/2;
mTitleRect.y = bottom_y - mTitleRect.height;
}
}
break;
case NS_SIDE_TOP:{
// get title and center it
CalculateTitleForWidth(aPresContext, aRenderingContext, rect.width);
if (mHasImage) {
// image top center
mImageRect.x = center_x - mImageRect.width/2;
mImageRect.y = rect.y + (rect.height - mTitleRect.height - spacing)/2 - mImageRect.height/2 + spacing;
if (mHasImage) {
// image top center
mImageRect.x = center_x - mImageRect.width/2;
mImageRect.y = rect.y + (rect.height - mTitleRect.height - spacing)/2 - mImageRect.height/2 + spacing;
// title bottom
mTitleRect.x = center_x - mTitleRect.width/2;
mTitleRect.y = bottom_y - mTitleRect.height;
} else {
mTitleRect.x = center_x - mTitleRect.width/2;
mTitleRect.y = top_y;
}
}
break;
case NS_SIDE_LEFT: {
// get title
CalculateTitleForWidth(aPresContext, aRenderingContext, rect.width - (mImageRect.width + spacing));
// title bottom
mTitleRect.x = center_x - mTitleRect.width/2;
mTitleRect.y = bottom_y - mTitleRect.height;
} else {
mTitleRect.x = center_x - mTitleRect.width/2;
mTitleRect.y = top_y;
}
}
break;
case NS_SIDE_LEFT: {
// get title
CalculateTitleForWidth(aPresContext, aRenderingContext, rect.width - (mImageRect.width + spacing));
// image left
mImageRect.x = left_x;
mImageRect.y = center_y - mImageRect.height/2;
// image left
mImageRect.x = left_x;
mImageRect.y = center_y - mImageRect.height/2;
// text after image
mTitleRect.x = mImageRect.x + mImageRect.width + spacing;
mTitleRect.y = center_y - mTitleRect.height/2;
}
break;
case NS_SIDE_RIGHT: {
CalculateTitleForWidth(aPresContext, aRenderingContext, rect.width - (mImageRect.width + spacing));
// text after image
mTitleRect.x = mImageRect.x + mImageRect.width + spacing;
mTitleRect.y = center_y - mTitleRect.height/2;
}
break;
case NS_SIDE_RIGHT: {
CalculateTitleForWidth(aPresContext, aRenderingContext, rect.width - (mImageRect.width + spacing));
// image right
mImageRect.x = right_x - mImageRect.width;
mImageRect.y = center_y - mImageRect.height/2;
// image right
mImageRect.x = right_x - mImageRect.width;
mImageRect.y = center_y - mImageRect.height/2;
// text left of image
mTitleRect.x = mImageRect.x - spacing - mTitleRect.width;
mTitleRect.y = center_y - mTitleRect.height/2;
// text left of image
mTitleRect.x = mImageRect.x - spacing - mTitleRect.width;
mTitleRect.y = center_y - mTitleRect.height/2;
}
break;
}
}
break;
}
// ok layout complete
mNeedsLayout = PR_FALSE;
// now that we've calculated the title, update the accesskey highlighting
UpdateAccessUnderline();
// ok layout complete
mNeedsLayout = PR_FALSE;
}
void
@ -722,6 +744,21 @@ nsTitledButtonFrame::CalculateTitleForWidth(nsIPresContext& aPresContext, nsIRen
aRenderingContext.GetWidth(mCroppedTitle, mTitleRect.width);
}
void
nsTitledButtonFrame::UpdateAccessUnderline()
{
mAccesskeyIndex = -1;
nsAutoString accesskey;
mContent->GetAttribute(kNameSpaceID_None, nsXULAtoms::accesskey,
accesskey);
if (accesskey == "")
return; // our work here is done
mAccesskeyIndex = mCroppedTitle.Find(accesskey, PR_TRUE);
mNeedsAccessUpdate = PR_TRUE;
}
NS_IMETHODIMP
nsTitledButtonFrame::PaintTitle(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
@ -750,57 +787,50 @@ nsTitledButtonFrame::PaintTitle(nsIPresContext& aPresContext,
aRenderingContext.SetFont(fontStyle->mFont);
PRBool hasAccessKey = PR_FALSE;
nsString accesskey, value;
mContent->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::value, value);
mContent->GetAttribute(kNameSpaceID_None, nsXULAtoms::accesskey,
accesskey);
if (mAccesskeyIndex != -1 && mNeedsAccessUpdate) {
// get all the underline-positioning stuff
/* XXX are attribute values always two byte? */
const PRUnichar *titleString, *accessString;
titleString = mCroppedTitle.GetUnicode();
if (accesskey != "") {
PRInt32 idx = mCroppedTitle.Find(accesskey, PR_TRUE);
if (idx != -1) {
hasAccessKey = PR_TRUE;
accessString = titleString + idx;
}
}
PRInt32 fontID;
nscoord beforeWidth, accessWidth, offset, size, baseline;
if (hasAccessKey) {
aRenderingContext.GetWidth(titleString, accessString - titleString,
beforeWidth, &fontID);
aRenderingContext.GetWidth(accessString, 1, accessWidth);
// XXX are attribute values always two byte?
const PRUnichar *titleString;
titleString = mCroppedTitle.GetUnicode();
if (mAccesskeyIndex)
aRenderingContext.GetWidth(titleString, mAccesskeyIndex,
mBeforeWidth);
else
mBeforeWidth = 0;
aRenderingContext.GetWidth(titleString[mAccesskeyIndex],
mAccessWidth);
/* XXX?: is there a cheaper way to do this, since we're in layout? */
nsIFontMetrics *metrics;
aRenderingContext.GetFontMetrics(metrics);
metrics->GetUnderline(offset, size);
metrics->GetMaxAscent(baseline);
NS_RELEASE(metrics);
nscoord offset, baseline;
nsIFontMetrics *metrics;
aRenderingContext.GetFontMetrics(metrics);
metrics->GetUnderline(offset, mAccessUnderlineSize);
metrics->GetMaxAscent(baseline);
NS_RELEASE(metrics);
mAccessOffset = baseline - offset;
mNeedsAccessUpdate = PR_FALSE;
}
// if disabled paint
if (PR_TRUE == mRenderer->isDisabled())
{
aRenderingContext.SetColor(NS_RGB(255,255,255));
aRenderingContext.DrawString(mCroppedTitle, disabledRect.x,
disabledRect.y, fontID);
if (hasAccessKey)
aRenderingContext.FillRect(disabledRect.x + beforeWidth,
disabledRect.y + baseline - offset,
accessWidth, size);
disabledRect.y);
if (mAccesskeyIndex != -1)
aRenderingContext.FillRect(disabledRect.x + mBeforeWidth,
disabledRect.y + mAccessOffset,
mAccessWidth, mAccessUnderlineSize);
}
aRenderingContext.SetColor(colorStyle->mColor);
aRenderingContext.DrawString(mCroppedTitle, mTitleRect.x, mTitleRect.y,
fontID);
if (hasAccessKey)
aRenderingContext.FillRect(mTitleRect.x + beforeWidth,
mTitleRect.y + baseline - offset,
accessWidth, size);
aRenderingContext.DrawString(mCroppedTitle, mTitleRect.x, mTitleRect.y);
if (mAccesskeyIndex != -1)
aRenderingContext.FillRect(mTitleRect.x + mBeforeWidth,
mTitleRect.y + mAccessOffset,
mAccessWidth, mAccessUnderlineSize);
}
@ -856,7 +886,7 @@ nsTitledButtonFrame::Reflow(nsIPresContext& aPresContext,
nsReflowStatus& aStatus)
{
// redraw us on a reflow
mNeedsAccessUpdate = PR_TRUE;
if (eReflowReason_Incremental == aReflowState.reason) {
nsIFrame* targetFrame;

View File

@ -91,7 +91,7 @@ protected:
CheckState GetCurrentCheckState();
void SetCurrentCheckState(CheckState aState);
void UpdateAccessUnderline();
virtual void MouseClicked(nsIPresContext & aPresContext);
@ -169,6 +169,14 @@ private:
nscoord mSpacing;
nsTitledButtonRenderer* mRenderer;
PRBool mHasImage;
// accesskey highlighting
PRBool mNeedsAccessUpdate;
PRInt32 mAccesskeyIndex;
nscoord mBeforeWidth, mAccessWidth, mAccessUnderlineSize, mAccessOffset;
// nsIPopUpMenu * mPopUpMenu;
// PRBool mMenuIsPoppedUp;
}; // class nsTitledButtonFrame

View File

@ -1364,12 +1364,34 @@ popup > menuitem[menuactive="true"] {
}
.menu-left {
color: inherit;
padding: 0px;
border: 0px;
width: 13px;
margin-right: 2px;
}
.menubar-left {
color: inherit;
padding: 0px;
border: 0px;
margin-right: 2px;
}
.menu-text {
color: inherit;
padding: 0px;
border: 0px;
margin-right: 2px;
}
.menubar-text {
color: inherit;
padding: 0px;
border: 0px;
margin-right: 2px;
}
menuitem > .menu-right {
list-style-image: none;
}