package edu.washington.cs.oneswarm.ui.gwt.client.newui;
/*
* Copyright 2007 Hilbrand Bouwkamp, hs@bouwkamp.com
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
/**
* The <code>RoundedPanel</code> class adds similar rounded corners, as seen in
* other Google applications, to a widget. This is done by adding several
* <code>div</code> Elements around a Widget. The HTML code roughly for a corner
* height 2 looks as follows:
*
* <pre>
* <div>
* <div style="margin:0 2px"></div>
* <div style="margin:0 1px"></div>
* <div>your widget</div>
* <div style="margin:0 1px"></div>
* <div style="margin:0 2px"></div>
* </div>
* </pre>
*
* To add the rounded corners to a widget start simply wrap your own widget in
* the <code>RoundedPanel</code> class, for example with all corners rounded:
*
* <pre>
* // all 4 corners are rounded.
* RoundedPanel rp = new RoundedPanel(yourWidget);
* // use rp where you would use 'yourWidget' otherwise
* </pre>
*
* Or with custom set corners, like only on the left:
*
* <pre>
* // custom set corners
* RoundedPanel rp = new RoundedPanel(yourWidget, RoundedPanel.LEFT);
* // use rp where you would use 'yourWidget' otherwise
* </pre>
*
* By default the height of the corners is 2px. It is possible to set a
* different height at construction time. The height can be a value between and
* including 1 and 9. This value doesn't correspond exactly with the height,
* e.g. 9 is 12px heigh.
*
* <pre>
* // all 4 corners are rounded and height index 5
* RoundedPanel rp = new RoundedPanel(yourWidget, ALL, 5);
* // use rp where you would use 'yourWidget' otherwise
* </pre>
*
* Programmatically set the color of the corners with
* {@link #setCornerColor(String)}:
*
* <pre>
* // all 4 corners are rounded.
* RoundedPanel rp = new RoundedPanel(yourWidget);
* rp.setCornerColor("red");
* </pre>
*
* Default the css style name of the rounded corner divs is <code>cbg-RP</code>.
* Use it to set the colors of the corner. For example:
*
* <pre>
* .cbg-RP { background-color:#c3d9ff; }
* </pre>
*
* A custom style might be needed when the corners are visible only when a panel
* is selected (see the KitchenSink example modification below). Use the
* <code>setCornerStyleName</code> method to set the custom style name. For
* example set a custom style <code>my-RP</code> and add something like the
* following to the stylesheet:
*
* <pre>
* .selected .my-RP { background-color:#c3d9ff; }
* </pre>
*
* <h3>Adding rounded corners to DialogBox widgets</h3> Adding rounded corners
* to a <code>DialogBox</code> is somewhat more complicated. The problem with
* <code>DialogBox</code> is that it uses a private panel and the
* <code>RoundedPanel</code> should be applied to this private
* <code>Panel</code>. To add the rounded corners to a <code>DialogBox</code>
* you have to rewrite the implementation of the <code>DialogBox</code>. For the
* <code>DialogBox</code> widget this can be done as follows. First create a new
* class like <code>RoundedDialogBox</code> and copy the code from the original
* <code>DialogBox</code> (Please take the copyright of this class into
* account). Next make the following changes to the code:
*
* <pre>
* // Private variable for RoundedPanel
* private RoundedPanel rp;
* </pre>
*
* In the constructor change:
*
* <pre>
* super.add(panel);
* </pre>
*
* into:
*
* <pre>
* rp = new RoundedPanel(panel);
* super.add(rp);
* </pre>
*
* Next change the style, otherwise the style of the dialog box is applied to
* the rounded lines too. Do this by changing the following line:
*
* <pre>
* setStyleName("gwt-DialogBox");
* </pre>
*
* into:
*
* <pre>
* panel.setStyleName("gwt-DialogBox");
* </pre>
*
* In your css add the color of the border, something like:
*
* <pre>
* .cbg-RP { background-color:#AAAAAA; }
* </pre>
*
* There seems to be a problem with the width under IE. The RoundedPanel divs
* don't follow the width of the dialog. To fix this change the
* <code>show</code> method as follows:
*
* <pre>
* public void show() {
* super.show();
* // (The width must be set after the super.show() otherwise it will be 0).
* // NOTE: As of GWT 1.1 the .getOffsetWidth() returns 0 at this point.
* // This is different with earlier versions of GWT. Disable the line will
* // make
* // the rounded corners work in at least Firefox 1.5, but not in IE.
* // I'm looking into this issue.
* rp.setWidth(panel.getOffsetWidth() + "px");
* }
* </pre>
*
* Now by extending your own dialog class on this <code>RoundedDialogBox</code>
* instead of the original <code>DialogBox</code> you will have rounded corners
* around your dialog.
*
*
* @author Hilbrand Bouwkamp(hs@bouwkamp.com)
* @version 1.3
*/
class RoundedPanel extends SimplePanel {
/**
* <code>TOPLEFT</code> top left rounded corner
*/
public final static int TOPLEFT = 1;
/**
* <code>TOPRIGHT</code> top right rounded corner
*/
public final static int TOPRIGHT = 2;
/**
* <code>BOTTOMLEFT</code> bottom left rounded corner
*/
public final static int BOTTOMLEFT = 4;
/**
* <code>BOTTOMRIGHT</code> bottom right rounded corner
*/
public final static int BOTTOMRIGHT = 8;
/**
* <code>BOTTOM</code> rounded corners at the top
*/
public final static int TOP = TOPLEFT | TOPRIGHT;
/**
* <code>TOP</code> rounded corners at the bottom
*/
public final static int BOTTOM = BOTTOMLEFT | BOTTOMRIGHT;
/**
* <code>LEFT</code> rounded corners on the left side
*/
public final static int LEFT = TOPLEFT | BOTTOMLEFT;
/**
* <code>RIGHT</code> rounded corners on the right side
*/
public final static int RIGHT = TOPRIGHT | BOTTOMRIGHT;
/**
* <code>ALL</code> rounded corners on all sides
*/
public final static int ALL = TOP | BOTTOM;
/**
* Default border style
*/
private final static String RPSTYLE = "cbg-RP";
/**
* Lookup table for corner border width
*/
private final static String[][] CORNERBORDER = { { "1px" }, { "1px", "1px" },
{ "1px", "1px", "1px" }, { "1px", "1px", "1px", "1px" },
{ "1px", "1px", "1px", "2px", "1px" }, { "1px", "1px", "1px", "1px", "2px", "1px" },
{ "1px", "1px", "1px", "1px", "1px", "2px", "1px" },
{ "1px", "1px", "1px", "1px", "1px", "2px", "2px", "1px" },
{ "1px", "1px", "1px", "1px", "1px", "1px", "2px", "3px", "1px" } };
/**
* Lookup table for corner height
*/
private final static String[][] CORNERHEIGHT = { { "1px" }, { "1px", "1px" },
{ "1px", "1px", "1px" }, { "1px", "1px", "1px", "1px" },
{ "2px", "1px", "1px", "1px", "1px" }, { "2px", "1px", "1px", "1px", "1px", "1px" },
{ "2px", "1px", "1px", "1px", "1px", "1px", "1px" },
{ "2px", "1px", "1px", "1px", "1px", "1px", "1px", "1px" },
{ "3px", "2px", "1px", "1px", "1px", "1px", "1px", "1px", "1px" } };
/**
* Lookup table for corner margin
*/
private final static String[][] CORNERMARGIN = { { "1px" }, { "1px", "2px" },
{ "1px", "2px", "3px" }, { "1px", "2px", "3px", "4px" },
{ "1px", "2px", "3px", "4px", "6px" }, { "1px", "2px", "3px", "4px", "5px", "7px" },
{ "1px", "2px", "3px", "4px", "5px", "6px", "8px" },
{ "1px", "2px", "3px", "4px", "5px", "6px", "8px", "10px" },
{ "1px", "2px", "3px", "4px", "5px", "6px", "7px", "9px", "12px" }, };
/**
* Element array containing all div elements of the top corner div's.
*/
protected final Element[] divt;
/**
* Element array containing all div elements of the bottom corner div's.
*/
protected final Element[] divb;
/**
* Index of the corner height.
*/
protected final int cornerHeight;
/**
* Index of which corners are set.
*/
protected final int corners;
private final Element body; // body of widget
private Element divElement; // div element containing widget
/**
* Creates a new <code>RoundedPanel</code> with all corners rounded and
* height of corners 2px. Use <code>setWidget</code> to add the widget.
*/
public RoundedPanel() {
this(ALL, 2);
}
/**
* Creates a new <code>RoundedPanel</code> with custom rounding and height
* of corners 2px but with no widget set. Use <code>setWidget</code> to add
* widget.
*
* Every combination of corners can be set via <code>corners</code>. Use the
* static constants to set the corners. For example, to create a panel with
* only rounded corners at the left, use:
*
* <code>new RoundedPanel(yourWidget, RoundedPanel.LEFT);</code>
*
* @param corners
* set custom rounded corners
*/
public RoundedPanel(int corners) {
this(corners, 2);
}
/**
* Creates a new <code>RoundedPanel</code> with custom rounding and custom
* height of the corners but with no widget set. Height can be value from 1
* to 9. The height for a value 5 or higher is actually larger, e.g. for 5
* the height is 6px. Use {@link #setWidget(Widget)} to add widget.
*
* @param corners
* set custom rounded corners
* @param cornerHeight
* height index between and including 1 and 9
* @throws IndexOutOfBoundsException
* when cornerHeight below 1 or above 9
*/
public RoundedPanel(int corners, int cornerHeight) {
super(DOM.createDiv());
body = getElement();
if (cornerHeight < 1 || cornerHeight > 9) {
throw new IndexOutOfBoundsException(
"RoundedPanel height range between and including 1 and 9");
}
this.cornerHeight = cornerHeight;
divt = new Element[cornerHeight];
divb = new Element[cornerHeight];
this.corners = corners;
if (inMask(corners, TOP)) {
final int ct = corners & TOP;
for (int i = 0; i < cornerHeight; ++i) {
divt[i] = addLine(ct, cornerHeight - (i + 1));
}
}
divElement = DOM.createDiv();
DOM.appendChild(body, divElement);
if (inMask(corners, BOTTOM)) {
final int cb = corners & BOTTOM;
for (int i = cornerHeight - 1; i >= 0; --i) {
divb[i] = addLine(cb, cornerHeight - (i + 1));
}
}
setCornerStyleName(RPSTYLE);
}
/**
* Creates a new <code>RoundedPanel</code> with all corners rounded and
* height 2px on the widget <code>w</code>.
*
* @param w
* widget to add corners to
*/
public RoundedPanel(Widget w) {
this(w, ALL);
}
/**
* Creates a new <code>RoundedPanel</code> with custom rounded corners and
* height 2px on the widget <code>w</code>.
*
* @param w
* widget to add corners to
* @param corners
* set custom rounded corners
*/
public RoundedPanel(Widget w, int corners) {
this(w, corners, 2);
}
/**
* Creates a new <code>RoundedPanel</code> with custom rounded corners and
* custom height on widget <code>w</code>.
*
* @param w
* widget to add corners to
* @param corners
* set custom rounded corners
* @param cornerHeight
* height index between and including 1 and 9
*/
public RoundedPanel(Widget w, int corners, int cornerHeight) {
this(corners, cornerHeight);
setWidget(w);
}
/**
* <p>
* Set the bolder color of the rounded corner by setting the background
* color of the div's.
* </p>
*
* @param borderColor
* border color
*/
public void setCornerColor(String borderColor) {
if (null != divt[0]) {
for (int i = 0; i < cornerHeight; ++i) {
DOM.setStyleAttribute(divt[i], "backgroundColor", borderColor);
}
}
if (null != divb[0]) {
for (int i = 0; i < cornerHeight; ++i) {
DOM.setStyleAttribute(divb[i], "backgroundColor", borderColor);
}
}
}
/**
* Set the css style name of the rounded corners div's. Default the css
* stylename is <code>cbg-RP</code>. Use it to set the colors of the corner.
* For example: <code>.cbg-RP { background-color:#c3d9ff; }</code>.
*
* A custom style might be needed when the corners are visible only when a
* panel is selected. Use this method to set the custom style name and add
* something like the following to the stylesheet:
*
* <code>.selected .cbg-RP { background-color:#c3d9ff; }</code> Setting the
* color is also possible via the method {@link #setCornerColor(String)}.
*
* @param style
* css style name
*/
public void setCornerStyleName(String style) {
if (null != divt[0]) {
for (int i = 0; i < cornerHeight; ++i) {
DOM.setAttribute(divt[i], "className", style);
}
}
if (null != divb[0]) {
for (int i = 0; i < cornerHeight; ++i) {
DOM.setAttribute(divb[i], "className", style);
}
}
}
/**
* Set the style of the RoundedPanel. In most cases this is not necessary
* and setting the style on the widget to which the
* <code>RoundedPanel</code> is applied should be set, as is done when not
* using the <code>RoundedPanel</code>
*
* @param style
* css style name
*/
public void setStyleName(String style) {
DOM.setAttribute(body, "className", style);
}
/**
* Overwrite of parent getContainerElement()
*/
protected Element getContainerElement() {
return divElement;
}
/**
* Convenience method to check if given <code>input</code> is with the
* <code>mask</code>.
*
* @param input
* input to check
* @param mask
* mask to check against
* @return true if input within mask
*/
protected boolean inMask(int input, int mask) {
return (input & mask) > 0;
}
/**
* Creates div element representing part of the rounded corner.
*
* @param corner
* corner mask to set rounded corner
* @param heightIndex
* margin width for line
*/
private Element addLine(int corner, int heightIndex) {
// margin 2 fields : top/bottom right/left => "0 <width>px"
// margin 4 fields : top right bottom left => "0 <width>px 0 <width>px"
String margin = (corner == TOP || corner == BOTTOM) ? "0 "
+ RoundedPanel.CORNERMARGIN[cornerHeight - 1][heightIndex]
: (inMask(corner, LEFT) ? "0 0 0 "
+ RoundedPanel.CORNERMARGIN[cornerHeight - 1][heightIndex] : "0 "
+ RoundedPanel.CORNERMARGIN[cornerHeight - 1][heightIndex] + " 0 0");
Element div = DOM.createDiv();
DOM.setStyleAttribute(div, "fontSize", "0px");
DOM.setStyleAttribute(div, "height",
RoundedPanel.CORNERHEIGHT[cornerHeight - 1][heightIndex]);
DOM.setStyleAttribute(div, "borderWidth", "0 "
+ RoundedPanel.CORNERBORDER[cornerHeight - 1][heightIndex]);
DOM.setStyleAttribute(div, "lineHeight",
RoundedPanel.CORNERHEIGHT[cornerHeight - 1][heightIndex]);
DOM.setStyleAttribute(div, "margin", margin);
DOM.setInnerHTML(div, " ");
DOM.appendChild(body, div);
return div;
}
}