blob: 1711cfa85b3a84da7eedbd2cebdbd879f9b7f5bd [file] [log] [blame]
/*
* Copyright 2006, The Android Open Source Project
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "RenderSkinCombo.h"
#include "Document.h"
#include "Element.h"
#include "Node.h"
#include "NodeRenderStyle.h"
#include "RenderStyle.h"
#include "SkCanvas.h"
#include "SkNinePatch.h"
#include <utils/AssetManager.h>
#include <wtf/text/CString.h>
extern android::AssetManager* globalAssetManager();
namespace WebCore {
// Indicates if the entire asset is being drawn, or if the border is being
// excluded and just the arrow drawn.
enum BorderStyle {
FullAsset,
NoBorder,
BorderStyleCount // Keep at the end.
};
// There are 2.5 different concepts of a 'border' here, which results
// in rather a lot of magic constants.
// Firstly, we have the extra padding that webkit needs to know about,
// which defines how much bigger this element is made by the
// asset. This is actually a bit broader than the actual border on the
// asset, to make things look less cramped. The border is the same
// width on all sides, except on the right when it's significantly
// wider to allow for the arrow.
const int RenderSkinCombo::arrowMargin[ResolutionCount] = {
16, // Medium resolution
25, // High resolution
34 // Extra high resolution
};
const int RenderSkinCombo::padMargin[ResolutionCount] = {
1, // Medium resolution
1, // High resolution
1 // Extra high resolution
};
namespace {
// Then we have the borders used for the 9-patch stretch. The
// rectangle at the centre of these borders is entirely below and to
// the left of the arrow in the asset. Hence the border widths are the
// same for the bottom and left, but are different for the top. The
// right hand border width happens to be the same as arrowMargin
// defined above.
const int stretchMargin[RenderSkinAndroid::ResolutionCount] = { // border width for the bottom and left of the 9-patch
2, // Medium resolution
2, // High resolution
3 // Extra high resolution
};
const int stretchTop[RenderSkinAndroid::ResolutionCount] = { // border width for the top of the 9-patch
16, // Medium resolution
23, // High resolution
32 // Extra high resolution
};
// Finally, if the border is defined by the CSS, we only draw the
// arrow and not the border. We do this by drawing the relevant subset
// of the bitmap, which must now be precisely determined by what's in
// the asset with no extra padding to make things look properly
// spaced. The border to remove at the top, right and bottom of the
// image is the same as stretchMargin above, but we need to know the width
// of the arrow.
const int arrowWidth[RenderSkinAndroid::ResolutionCount] = {
18, // Medium resolution
27, // High resolution
36 // Extra high resolution
};
// scale factors for various resolutions
const float scaleFactor[RenderSkinAndroid::ResolutionCount] = {
1.0f, // medium res
1.5f, // high res
2.0f // extra high res
};
// Store the calculated 9 patch margins for each border style.
SkIRect margin[BorderStyleCount];
SkBitmap bitmaps[2][BorderStyleCount]; // Collection of assets for a combo box - 2 states (enabled/disabled)
bool isDecodingAttempted = false; // True if we've tried to decode the assets
bool isDecoded = false; // True if all assets were decoded
} // namespace
int RenderSkinCombo::minHeight() {
return SkScalarRound(stretchTop[RenderSkinAndroid::DrawableResolution()]
/ scaleFactor[RenderSkinAndroid::DrawableResolution()]);
}
void RenderSkinCombo::Decode()
{
if (isDecodingAttempted)
return;
isDecodingAttempted = true;
isDecoded = false;
android::AssetManager* am = globalAssetManager();
String drawableDirectory = RenderSkinAndroid::DrawableDirectory();
Resolution res = RenderSkinAndroid::DrawableResolution();
isDecoded = RenderSkinAndroid::DecodeBitmap(am, (drawableDirectory + "combobox_nohighlight.png").utf8().data(), &bitmaps[kNormal][FullAsset]);
isDecoded &= RenderSkinAndroid::DecodeBitmap(am, (drawableDirectory + "combobox_disabled.png").utf8().data(), &bitmaps[kDisabled][FullAsset]);
int width = bitmaps[kNormal][FullAsset].width();
int height = bitmaps[kNormal][FullAsset].height();
SkIRect subset;
subset.set(width - arrowWidth[res], 0, width, height);
bitmaps[kNormal][FullAsset].extractSubset(&bitmaps[kNormal][NoBorder], subset);
bitmaps[kDisabled][FullAsset].extractSubset(&bitmaps[kDisabled][NoBorder], subset);
// Calculate 9 patch margins.
SkIRect fullAssetMargin;
fullAssetMargin.fLeft = stretchMargin[res];
fullAssetMargin.fTop = stretchMargin[res];
fullAssetMargin.fRight = arrowMargin[res] + stretchMargin[res];
fullAssetMargin.fBottom = stretchTop[res];
SkIRect noBorderMargin;
noBorderMargin.fLeft = 0;
noBorderMargin.fTop = stretchTop[res];
noBorderMargin.fRight = 0;
noBorderMargin.fBottom = stretchMargin[res];
margin[FullAsset] = fullAssetMargin;
margin[NoBorder] = noBorderMargin;
}
bool RenderSkinCombo::Draw(SkCanvas* canvas, Node* element, int x, int y, int width, int height)
{
if (!isDecodingAttempted)
Decode();
if (!isDecoded)
return true;
int resolution = RenderSkinAndroid::DrawableResolution();
State state = (element->isElementNode() && static_cast<Element*>(element)->isEnabledFormControl()) ? kNormal : kDisabled;
height = std::max(height, (stretchMargin[resolution] * 2));
SkRect bounds;
BorderStyle drawBorder = FullAsset;
bounds.set(SkIntToScalar(x+1), SkIntToScalar(y+1), SkIntToScalar(x + width-1), SkIntToScalar(y + height-1));
RenderStyle* style = element->renderStyle();
SkPaint paint;
paint.setColor(style->visitedDependentColor(CSSPropertyBackgroundColor).rgb());
canvas->drawRect(bounds, paint);
bounds.set(SkIntToScalar(x), SkIntToScalar(y), SkIntToScalar(x + width), SkIntToScalar(y + height));
// If this is an appearance where RenderTheme::paint returns true
// without doing anything, this means that
// RenderBox::PaintBoxDecorationWithSize will end up painting the
// border, so we shouldn't paint a border here.
if (style->appearance() == MenulistButtonPart ||
style->appearance() == ListboxPart ||
style->appearance() == TextFieldPart ||
style->appearance() == TextAreaPart) {
bounds.fLeft += SkIntToScalar(width - RenderSkinCombo::extraWidth());
bounds.fRight -= SkIntToScalar(style->borderRightWidth());
bounds.fTop += SkIntToScalar(style->borderTopWidth());
bounds.fBottom -= SkIntToScalar(style->borderBottomWidth());
drawBorder = NoBorder;
}
float scale = scaleFactor[resolution];
bounds.fLeft = bounds.fLeft * scale;
bounds.fRight = bounds.fRight * scale;
bounds.fTop = bounds.fTop * scale;
bounds.fBottom = bounds.fBottom * scale;
int count = canvas->save();
canvas->scale(1.0f / scale, 1.0f / scale);
SkNinePatch::DrawNine(canvas, bounds, bitmaps[state][drawBorder], margin[drawBorder]);
canvas->restoreToCount(count);
return false;
}
} // namspace WebCore