bool RenderEmbeddedObject::getReplacementTextGeometry(const LayoutPoint& accumulatedOffset, FloatRect& contentRect, FloatRect& indicatorRect, FloatRect& replacementTextRect, FloatRect& arrowRect, FontCascade& font, TextRun& run, float& textWidth) const { bool includesArrow = shouldUnavailablePluginMessageBeButton(document(), m_pluginUnavailabilityReason); contentRect = contentBoxRect(); contentRect.moveBy(roundedIntPoint(accumulatedOffset)); FontCascadeDescription fontDescription; RenderTheme::defaultTheme()->systemFont(CSSValueWebkitSmallControl, fontDescription); fontDescription.setWeight(FontWeightBold); fontDescription.setRenderingMode(frame().settings().fontRenderingMode()); fontDescription.setComputedSize(12); font = FontCascade(fontDescription, 0, 0); font.update(0); run = TextRun(m_unavailablePluginReplacementText); textWidth = font.width(run); replacementTextRect.setSize(FloatSize(textWidth + replacementTextRoundedRectLeftTextMargin + (includesArrow ? replacementTextRoundedRectRightTextMarginWithArrow : replacementTextRoundedRectRightTextMargin), replacementTextRoundedRectHeight)); float x = (contentRect.size().width() / 2 - replacementTextRect.size().width() / 2) + contentRect.location().x(); float y = (contentRect.size().height() / 2 - replacementTextRect.size().height() / 2) + contentRect.location().y(); replacementTextRect.setLocation(FloatPoint(x, y)); indicatorRect = replacementTextRect; // Expand the background rect to include the arrow, if it will be used. if (includesArrow) { arrowRect = indicatorRect; arrowRect.setX(ceilf(arrowRect.maxX() + replacementArrowLeftMargin)); arrowRect.setWidth(arrowRect.height()); indicatorRect.unite(arrowRect); } return true; }
static void doDrawTextAtPoint(GraphicsContext& context, const String& text, const IntPoint& point, const FontCascade& font, const Color& color, int underlinedIndex) { TextRun run(text); context.setFillColor(color); if (isOneLeftToRightRun(run)) font.drawText(context, run, point); else context.drawBidiText(font, run, point); if (underlinedIndex >= 0) { ASSERT_WITH_SECURITY_IMPLICATION(underlinedIndex < static_cast<int>(text.length())); int beforeWidth; if (underlinedIndex > 0) { TextRun beforeRun(StringView(text).substring(0, underlinedIndex)); beforeWidth = font.width(beforeRun); } else beforeWidth = 0; TextRun underlinedRun(StringView(text).substring(underlinedIndex, 1)); int underlinedWidth = font.width(underlinedRun); IntPoint underlinePoint(point); underlinePoint.move(beforeWidth, 1); context.setStrokeColor(color); context.drawLineForText(underlinePoint, underlinedWidth, false); } }
void RenderEmbeddedObject::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (!showsUnavailablePluginIndicator()) return; if (paintInfo.phase == PaintPhaseSelection) return; GraphicsContext& context = paintInfo.context(); if (context.paintingDisabled()) return; FloatRect contentRect; FloatRect indicatorRect; FloatRect replacementTextRect; FloatRect arrowRect; FontCascade font; TextRun run(emptyString()); float textWidth; if (!getReplacementTextGeometry(paintOffset, contentRect, indicatorRect, replacementTextRect, arrowRect, font, run, textWidth)) return; Path background; background.addRoundedRect(indicatorRect, FloatSize(replacementTextRoundedRectRadius, replacementTextRoundedRectRadius)); GraphicsContextStateSaver stateSaver(context); context.clip(contentRect); context.setFillColor(m_unavailablePluginIndicatorIsPressed ? replacementTextRoundedRectPressedColor() : replacementTextRoundedRectColor()); context.fillPath(background); Path strokePath; FloatRect strokeRect(indicatorRect); strokeRect.inflate(1); strokePath.addRoundedRect(strokeRect, FloatSize(replacementTextRoundedRectRadius + 1, replacementTextRoundedRectRadius + 1)); context.setStrokeColor(unavailablePluginBorderColor()); context.setStrokeThickness(2); context.strokePath(strokePath); const FontMetrics& fontMetrics = font.fontMetrics(); float labelX = roundf(replacementTextRect.location().x() + replacementTextRoundedRectLeftTextMargin); float labelY = roundf(replacementTextRect.location().y() + (replacementTextRect.size().height() - fontMetrics.height()) / 2 + fontMetrics.ascent() + replacementTextRoundedRectTopTextMargin); context.setFillColor(replacementTextColor()); context.drawBidiText(font, run, FloatPoint(labelX, labelY)); if (shouldUnavailablePluginMessageBeButton(document(), m_pluginUnavailabilityReason)) { arrowRect.inflate(-replacementArrowCirclePadding); context.beginTransparencyLayer(1.0); context.setFillColor(replacementTextColor()); context.fillEllipse(arrowRect); context.setCompositeOperation(CompositeClear); drawReplacementArrow(context, arrowRect); context.endTransparencyLayer(); } }
void GraphicsContext::drawGlyphs(const FontCascade& fontCascade, const Font& font, const GlyphBuffer& buffer, int from, int numGlyphs, const FloatPoint& point) { if (paintingDisabled()) return; if (isRecording()) { m_displayListRecorder->drawGlyphs(font, buffer, from, numGlyphs, point, fontCascade.fontDescription().fontSmoothing()); return; } fontCascade.drawGlyphs(*this, font, buffer, from, numGlyphs, point, fontCascade.fontDescription().fontSmoothing()); }
bool FontLoader::checkFont(const String& fontString, const String&) { // FIXME: The second parameter (text) is ignored. FontCascade font; if (!resolveFontStyle(fontString, font)) return false; for (unsigned i = 0; i < font.familyCount(); i++) { CSSSegmentedFontFace* face = m_document->fontSelector().getFontFace(font.fontDescription(), font.familyAt(i)); if (!face || !face->checkFont()) return false; } return true; }
float RenderCombineText::width(unsigned from, unsigned length, const FontCascade& font, float xPosition, HashSet<const Font*>* fallbackFonts, GlyphOverflow* glyphOverflow) const { if (m_isCombined) return font.size(); return RenderText::width(from, length, font, xPosition, fallbackFonts, glyphOverflow); }
float GraphicsContext::drawText(const FontCascade& font, const TextRun& run, const FloatPoint& point, int from, int to) { if (paintingDisabled()) return 0; return font.drawText(*this, run, point, from, to); }
void GraphicsContext::drawGlyphs(const FontCascade& fontCascade, const Font& font, const GlyphBuffer& buffer, int from, int numGlyphs, const FloatPoint& point) { if (paintingDisabled()) return; fontCascade.drawGlyphs(*this, font, buffer, from, numGlyphs, point); }
void GraphicsContext::drawEmphasisMarks(const FontCascade& font, const TextRun& run, const AtomicString& mark, const FloatPoint& point, int from, int to) { if (paintingDisabled()) return; font.drawEmphasisMarks(*this, run, mark, point, from, to); }
void GraphicsContext::drawBidiText(const FontCascade& font, const TextRun& run, const FloatPoint& point, FontCascade::CustomFontNotReadyAction customFontNotReadyAction) { if (paintingDisabled()) return; BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver; bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride())); bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0)); // FIXME: This ownership should be reversed. We should pass BidiRunList // to BidiResolver in createBidiRunsForLine. BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs(); bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length())); if (!bidiRuns.runCount()) return; FloatPoint currPoint = point; BidiCharacterRun* bidiRun = bidiRuns.firstRun(); while (bidiRun) { TextRun subrun = run.subRun(bidiRun->start(), bidiRun->stop() - bidiRun->start()); bool isRTL = bidiRun->level() % 2; subrun.setDirection(isRTL ? RTL : LTR); subrun.setDirectionalOverride(bidiRun->dirOverride(false)); float width = font.drawText(*this, subrun, currPoint, 0, -1, customFontNotReadyAction); currPoint.move(width, 0); bidiRun = bidiRun->next(); } bidiRuns.deleteRuns(); }
static float stringWidth(const FontCascade& renderer, const UChar* characters, unsigned length, bool disableRoundingHacks) { TextRun run(characters, length); if (disableRoundingHacks) run.disableRoundingHacks(); return renderer.width(run); }
float GraphicsContext::drawText(const FontCascade& font, const TextRun& run, const FloatPoint& point, int from, int to) { if (paintingDisabled()) return 0; // Display list recording for text content is done at glyphs level. See GraphicsContext::drawGlyphs. return font.drawText(*this, run, point, from, to); }
static FontCascade dragLabelFont(int size, bool bold, FontRenderingMode renderingMode) { FontCascade result; NONCLIENTMETRICS metrics; metrics.cbSize = sizeof(metrics); SystemParametersInfo(SPI_GETNONCLIENTMETRICS, metrics.cbSize, &metrics, 0); FontCascadeDescription description; description.setWeight(bold ? FontWeightBold : FontWeightNormal); description.setOneFamily(metrics.lfSmCaptionFont.lfFaceName); description.setSpecifiedSize((float)size); description.setComputedSize((float)size); description.setRenderingMode(renderingMode); result = FontCascade(description, 0, 0); result.update(0); return result; }
void PlatformCALayerWin::drawTextAtPoint(CGContextRef context, CGFloat x, CGFloat y, CGSize scale, CGFloat fontSize, const char* message, size_t length) const { String text(message, length); FontCascadeDescription desc; NONCLIENTMETRICS metrics; metrics.cbSize = sizeof(metrics); SystemParametersInfo(SPI_GETNONCLIENTMETRICS, metrics.cbSize, &metrics, 0); desc.setOneFamily(metrics.lfSmCaptionFont.lfFaceName); desc.setComputedSize(scale.width * fontSize); FontCascade font = FontCascade(desc, 0, 0); font.update(nullptr); GraphicsContext cg(context); cg.setFillColor(Color::black, ColorSpaceDeviceRGB); cg.drawText(font, TextRun(text), IntPoint(x, y)); }
static void drawSkipInkUnderline(GraphicsContext& context, const FontCascade& font, const TextRun& textRun, const FloatPoint& textOrigin, const FloatPoint& localOrigin, float underlineOffset, float width, bool isPrinting, bool doubleLines, StrokeStyle strokeStyle) { FloatPoint adjustedLocalOrigin = localOrigin; adjustedLocalOrigin.move(0, underlineOffset); FloatRect underlineBoundingBox = context.computeUnderlineBoundsForText(adjustedLocalOrigin, width, isPrinting); DashArray intersections = font.dashesForIntersectionsWithRect(textRun, textOrigin, underlineBoundingBox); DashArray a = translateIntersectionPointsToSkipInkBoundaries(intersections, underlineBoundingBox.height(), width); ASSERT(!(a.size() % 2)); context.drawLinesForText(adjustedLocalOrigin, a, isPrinting, doubleLines, strokeStyle); }
static PassRefPtr<LoadFontCallback> createFromParams(const Dictionary& params, FontLoader& fontLoader, const FontCascade& font) { RefPtr<VoidCallback> onsuccess; RefPtr<VoidCallback> onerror; params.get("onsuccess", onsuccess); params.get("onerror", onerror); if (!onsuccess && !onerror) return 0; int numFamilies = font.familyCount(); return LoadFontCallback::create(numFamilies, fontLoader, onsuccess, onerror); }
void FontLoader::loadFont(const Dictionary& params) { // FIXME: The text member of params is ignored. String fontString; if (!params.get("font", fontString)) return; FontCascade font; if (!resolveFontStyle(fontString, font)) return; RefPtr<LoadFontCallback> callback = LoadFontCallback::createFromParams(params, *this, font); m_numLoadingFromJS += callback->familyCount(); for (unsigned i = 0; i < font.familyCount(); i++) { CSSSegmentedFontFace* face = m_document->fontSelector().getFontFace(font.fontDescription(), font.familyAt(i)); if (!face) { if (callback) callback->notifyError(); continue; } face->loadFont(font.fontDescription(), callback); } }
void SVGInlineTextBox::paintDecorationWithStyle(GraphicsContext& context, TextDecoration decoration, const SVGTextFragment& fragment, RenderBoxModelObject& decorationRenderer) { ASSERT(!m_paintingResource); ASSERT(m_paintingResourceMode != ApplyToDefaultMode); RenderStyle& decorationStyle = decorationRenderer.style(); float scalingFactor = 1; FontCascade scaledFont; RenderSVGInlineText::computeNewScaledFontForStyle(decorationRenderer, decorationStyle, scalingFactor, scaledFont); ASSERT(scalingFactor); // The initial y value refers to overline position. float thickness = thicknessForDecoration(decoration, scaledFont); if (fragment.width <= 0 && thickness <= 0) return; FloatPoint decorationOrigin(fragment.x, fragment.y); float width = fragment.width; const FontMetrics& scaledFontMetrics = scaledFont.fontMetrics(); GraphicsContextStateSaver stateSaver(context); if (scalingFactor != 1) { width *= scalingFactor; decorationOrigin.scale(scalingFactor, scalingFactor); context.scale(FloatSize(1 / scalingFactor, 1 / scalingFactor)); } decorationOrigin.move(0, -scaledFontMetrics.floatAscent() + positionOffsetForDecoration(decoration, scaledFontMetrics, thickness)); Path path; path.addRect(FloatRect(decorationOrigin, FloatSize(width, thickness))); GraphicsContext* contextPtr = &context; if (acquirePaintingResource(contextPtr, scalingFactor, decorationRenderer, &decorationStyle)) releasePaintingResource(contextPtr, &path); }
void RenderSVGInlineText::computeNewScaledFontForStyle(const RenderObject& renderer, const RenderStyle& style, float& scalingFactor, FontCascade& scaledFont) { // Alter font-size to the right on-screen value to avoid scaling the glyphs themselves, except when GeometricPrecision is specified scalingFactor = SVGRenderingContext::calculateScreenFontSizeScalingFactor(renderer); if (scalingFactor == 1 || !scalingFactor || style.fontDescription().textRenderingMode() == GeometricPrecision) { scalingFactor = 1; scaledFont = style.fontCascade(); return; } FontDescription fontDescription(style.fontDescription()); // FIXME: We need to better handle the case when we compute very small fonts below (below 1pt). fontDescription.setComputedSize(Style::computedFontSizeFromSpecifiedSizeForSVGInlineText(fontDescription.computedSize(), fontDescription.isAbsoluteSize(), scalingFactor, renderer.document())); scaledFont = FontCascade(fontDescription, 0, 0); scaledFont.update(&renderer.document().fontSelector()); }
bool FontLoader::resolveFontStyle(const String& fontString, FontCascade& font) { // Interpret fontString in the same way as the 'font' attribute of CanvasRenderingContext2D. RefPtr<MutableStyleProperties> parsedStyle = MutableStyleProperties::create(); CSSParser::parseValue(parsedStyle.get(), CSSPropertyFont, fontString, true, CSSStrictMode, 0); if (parsedStyle->isEmpty()) return false; String fontValue = parsedStyle->getPropertyValue(CSSPropertyFont); if (fontValue == "inherit" || fontValue == "initial") return false; RefPtr<RenderStyle> style = RenderStyle::create(); FontDescription defaultFontDescription; defaultFontDescription.setOneFamily(defaultFontFamily); defaultFontDescription.setSpecifiedSize(defaultFontSize); defaultFontDescription.setComputedSize(defaultFontSize); style->setFontDescription(defaultFontDescription); style->fontCascade().update(style->fontCascade().fontSelector()); // Now map the font property longhands into the style. StyleResolver& styleResolver = m_document->ensureStyleResolver(); styleResolver.applyPropertyToStyle(CSSPropertyFontFamily, parsedStyle->getPropertyCSSValue(CSSPropertyFontFamily).get(), style.get()); applyPropertyToCurrentStyle(styleResolver, CSSPropertyFontStyle, parsedStyle); applyPropertyToCurrentStyle(styleResolver, CSSPropertyFontVariant, parsedStyle); applyPropertyToCurrentStyle(styleResolver, CSSPropertyFontWeight, parsedStyle); // As described in BUG66291, setting font-size and line-height on a font may entail a CSSPrimitiveValue::computeLengthDouble call, // which assumes the fontMetrics are available for the affected font, otherwise a crash occurs (see http://trac.webkit.org/changeset/96122). // The updateFont() calls below update the fontMetrics and ensure the proper setting of font-size and line-height. styleResolver.updateFont(); applyPropertyToCurrentStyle(styleResolver, CSSPropertyFontSize, parsedStyle); styleResolver.updateFont(); applyPropertyToCurrentStyle(styleResolver, CSSPropertyLineHeight, parsedStyle); font = style->fontCascade(); font.update(&m_document->fontSelector()); return true; }
void EllipsisBox::paintSelection(GraphicsContext& context, const LayoutPoint& paintOffset, const RenderStyle& style, const FontCascade& font) { Color textColor = style.visitedDependentColor(CSSPropertyColor); Color c = blockFlow().selectionBackgroundColor(); if (!c.isValid() || !c.alpha()) return; // If the text color ends up being the same as the selection background, invert the selection // background. if (textColor == c) c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue()); const RootInlineBox& rootBox = root(); GraphicsContextStateSaver stateSaver(context); // FIXME: Why is this always LTR? Fix by passing correct text run flags below. LayoutRect selectionRect = LayoutRect(x() + paintOffset.x(), y() + paintOffset.y() + rootBox.selectionTop(), 0, rootBox.selectionHeight()); TextRun run = RenderBlock::constructTextRun(&blockFlow(), font, m_str, style, AllowTrailingExpansion); font.adjustSelectionRectForText(run, selectionRect, 0, -1); context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), run.ltr()), c); }
// The screen that the popup is placed on should be whichever one the popup menu button lies on. // We fake an hwnd (here we use the popup's hwnd) on top of the button which we can then use to determine the screen. // We can then proceed with our final position/size calculations. void PopupMenuWin::calculatePositionAndSize(const IntRect& r, FrameView* v) { // First get the screen coordinates of the popup menu client. HWND hostWindow = v->hostWindow()->platformPageClient(); IntRect absoluteBounds = ((RenderMenuList*)m_popupClient)->absoluteBoundingBoxRect(); IntRect absoluteScreenCoords(v->contentsToWindow(absoluteBounds.location()), absoluteBounds.size()); POINT absoluteLocation(absoluteScreenCoords.location()); if (!::ClientToScreen(hostWindow, &absoluteLocation)) return; absoluteScreenCoords.setLocation(absoluteLocation); // Now set the popup menu's location temporarily to these coordinates so we can determine which screen the popup should lie on. // We create or move m_popup as necessary. if (!m_popup) { registerClass(); DWORD exStyle = WS_EX_LTRREADING; m_popup = ::CreateWindowExW(exStyle, kPopupWindowClassName, L"PopupMenu", WS_POPUP | WS_BORDER, absoluteScreenCoords.x(), absoluteScreenCoords.y(), absoluteScreenCoords.width(), absoluteScreenCoords.height(), hostWindow, 0, WebCore::instanceHandle(), this); if (!m_popup) return; } else ::MoveWindow(m_popup, absoluteScreenCoords.x(), absoluteScreenCoords.y(), absoluteScreenCoords.width(), absoluteScreenCoords.height(), false); FloatRect screen = monitorFromHwnd(m_popup); // Now we determine the actual location and measurements of the popup itself. // r is in absolute document coordinates, but we want to be in screen coordinates. // First, move to WebView coordinates IntRect rScreenCoords(v->contentsToWindow(r.location()), r.size()); if (Page* page = v->frame().page()) rScreenCoords.scale(page->deviceScaleFactor()); // Then, translate to screen coordinates POINT location(rScreenCoords.location()); if (!::ClientToScreen(hostWindow, &location)) return; rScreenCoords.setLocation(location); // First, determine the popup's height int itemCount = client()->listSize(); m_itemHeight = client()->menuStyle().font().fontMetrics().height() + optionSpacingMiddle; int naturalHeight = m_itemHeight * itemCount; int popupHeight = std::min(maxPopupHeight, naturalHeight); // The popup should show an integral number of items (i.e. no partial items should be visible) popupHeight -= popupHeight % m_itemHeight; // Next determine its width int popupWidth = 0; for (int i = 0; i < itemCount; ++i) { String text = client()->itemText(i); if (text.isEmpty()) continue; FontCascade itemFont = client()->menuStyle().font(); if (client()->itemIsLabel(i)) { auto d = itemFont.fontDescription(); d.setWeight(d.bolderWeight()); itemFont = FontCascade(d, itemFont.letterSpacing(), itemFont.wordSpacing()); itemFont.update(m_popupClient->fontSelector()); } popupWidth = std::max(popupWidth, static_cast<int>(ceilf(itemFont.width(TextRun(text))))); } if (naturalHeight > maxPopupHeight) // We need room for a scrollbar popupWidth += ScrollbarTheme::theme().scrollbarThickness(SmallScrollbar); // Add padding to align the popup text with the <select> text popupWidth += std::max<int>(0, client()->clientPaddingRight() - client()->clientInsetRight()) + std::max<int>(0, client()->clientPaddingLeft() - client()->clientInsetLeft()); // Leave room for the border popupWidth += 2 * popupWindowBorderWidth; popupHeight += 2 * popupWindowBorderWidth; // The popup should be at least as wide as the control on the page popupWidth = std::max(rScreenCoords.width() - client()->clientInsetLeft() - client()->clientInsetRight(), popupWidth); // Always left-align items in the popup. This matches popup menus on the mac. int popupX = rScreenCoords.x() + client()->clientInsetLeft(); IntRect popupRect(popupX, rScreenCoords.maxY(), popupWidth, popupHeight); // Check that we don't go off the screen vertically if (popupRect.maxY() > screen.height()) { // The popup will go off the screen, so try placing it above the client if (rScreenCoords.y() - popupRect.height() < 0) { // The popup won't fit above, either, so place it whereever's bigger and resize it to fit if ((rScreenCoords.y() + rScreenCoords.height() / 2) < (screen.height() / 2)) { // Below is bigger popupRect.setHeight(screen.height() - popupRect.y()); } else { // Above is bigger popupRect.setY(0); popupRect.setHeight(rScreenCoords.y()); } } else { // The popup fits above, so reposition it popupRect.setY(rScreenCoords.y() - popupRect.height()); } } // Check that we don't go off the screen horizontally if (popupRect.x() + popupRect.width() > screen.width() + screen.x()) popupRect.setX(screen.x() + screen.width() - popupRect.width()); if (popupRect.x() < screen.x()) popupRect.setX(screen.x()); m_windowRect = popupRect; return; }
void PopupMenuWin::paint(const IntRect& damageRect, HDC hdc) { if (!m_popup) return; if (!m_DC) { m_DC = adoptGDIObject(::CreateCompatibleDC(HWndDC(m_popup))); if (!m_DC) return; } if (m_bmp) { bool keepBitmap = false; BITMAP bitmap; if (::GetObject(m_bmp.get(), sizeof(bitmap), &bitmap)) keepBitmap = bitmap.bmWidth == clientRect().width() && bitmap.bmHeight == clientRect().height(); if (!keepBitmap) m_bmp.clear(); } if (!m_bmp) { BitmapInfo bitmapInfo = BitmapInfo::createBottomUp(clientRect().size()); void* pixels = 0; m_bmp = adoptGDIObject(::CreateDIBSection(m_DC.get(), &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0)); if (!m_bmp) return; ::SelectObject(m_DC.get(), m_bmp.get()); } GraphicsContext context(m_DC.get()); int itemCount = client()->listSize(); // listRect is the damageRect translated into the coordinates of the entire menu list (which is itemCount * m_itemHeight pixels tall) IntRect listRect = damageRect; listRect.move(IntSize(0, m_scrollOffset * m_itemHeight)); for (int y = listRect.y(); y < listRect.maxY(); y += m_itemHeight) { int index = y / m_itemHeight; Color optionBackgroundColor, optionTextColor; PopupMenuStyle itemStyle = client()->itemStyle(index); if (index == focusedIndex()) { optionBackgroundColor = RenderTheme::defaultTheme()->activeListBoxSelectionBackgroundColor(); optionTextColor = RenderTheme::defaultTheme()->activeListBoxSelectionForegroundColor(); } else { optionBackgroundColor = itemStyle.backgroundColor(); optionTextColor = itemStyle.foregroundColor(); } // itemRect is in client coordinates IntRect itemRect(0, (index - m_scrollOffset) * m_itemHeight, damageRect.width(), m_itemHeight); // Draw the background for this menu item if (itemStyle.isVisible()) context.fillRect(itemRect, optionBackgroundColor); if (client()->itemIsSeparator(index)) { IntRect separatorRect(itemRect.x() + separatorPadding, itemRect.y() + (itemRect.height() - separatorHeight) / 2, itemRect.width() - 2 * separatorPadding, separatorHeight); context.fillRect(separatorRect, optionTextColor); continue; } String itemText = client()->itemText(index); TextRun textRun(itemText, 0, 0, AllowTrailingExpansion, itemStyle.textDirection(), itemStyle.hasTextDirectionOverride()); context.setFillColor(optionTextColor); FontCascade itemFont = client()->menuStyle().font(); if (client()->itemIsLabel(index)) { auto d = itemFont.fontDescription(); d.setWeight(d.bolderWeight()); itemFont = FontCascade(d, itemFont.letterSpacing(), itemFont.wordSpacing()); itemFont.update(m_popupClient->fontSelector()); } // Draw the item text if (itemStyle.isVisible()) { int textX = 0; if (client()->menuStyle().textDirection() == LTR) { textX = std::max<int>(0, client()->clientPaddingLeft() - client()->clientInsetLeft()); if (RenderTheme::defaultTheme()->popupOptionSupportsTextIndent()) textX += minimumIntValueForLength(itemStyle.textIndent(), itemRect.width()); } else { textX = itemRect.width() - client()->menuStyle().font().width(textRun); textX = std::min<int>(textX, textX - client()->clientPaddingRight() + client()->clientInsetRight()); if (RenderTheme::defaultTheme()->popupOptionSupportsTextIndent()) textX -= minimumIntValueForLength(itemStyle.textIndent(), itemRect.width()); } int textY = itemRect.y() + itemFont.fontMetrics().ascent() + (itemRect.height() - itemFont.fontMetrics().height()) / 2; context.drawBidiText(itemFont, textRun, IntPoint(textX, textY)); } } if (m_scrollbar) m_scrollbar->paint(context, damageRect); HWndDC hWndDC; HDC localDC = hdc ? hdc : hWndDC.setHWnd(m_popup); ::BitBlt(localDC, damageRect.x(), damageRect.y(), damageRect.width(), damageRect.height(), m_DC.get(), damageRect.x(), damageRect.y(), SRCCOPY); }
void FullscreenVideoController::draw() { auto bitmapDC = adoptGDIObject(::CreateCompatibleDC(HWndDC(m_hudWindow))); HGDIOBJ oldBitmap = SelectObject(bitmapDC.get(), m_bitmap.get()); GraphicsContext context(bitmapDC.get(), true); context.save(); // Draw the background IntSize outerRadius(borderRadius, borderRadius); IntRect outerRect(0, 0, windowWidth, windowHeight); IntSize innerRadius(borderRadius - borderThickness, borderRadius - borderThickness); IntRect innerRect(borderThickness, borderThickness, windowWidth - borderThickness * 2, windowHeight - borderThickness * 2); context.fillRoundedRect(FloatRoundedRect(outerRect, outerRadius, outerRadius, outerRadius, outerRadius), Color(borderColor)); context.setCompositeOperation(CompositeCopy); context.fillRoundedRect(FloatRoundedRect(innerRect, innerRadius, innerRadius, innerRadius, innerRadius), Color(backgroundColor)); // Draw the widgets m_playPauseButton.draw(context); m_volumeUpButton.draw(context); m_volumeSliderButton.draw(context); m_volumeDownButton.draw(context); m_timeSliderButton.draw(context); m_exitFullscreenButton.draw(context); m_volumeSlider.draw(context); m_timeSlider.draw(context); // Draw the text strings FontCascadeDescription desc; NONCLIENTMETRICS metrics; metrics.cbSize = sizeof(metrics); SystemParametersInfo(SPI_GETNONCLIENTMETRICS, metrics.cbSize, &metrics, 0); desc.setOneFamily(metrics.lfSmCaptionFont.lfFaceName); desc.setComputedSize(textSize); FontCascade font = FontCascade(desc, 0, 0); font.update(0); String s; // The y positioning of these two text strings is tricky because they are so small. They // are currently positioned relative to the center of the slider and then down the font // height / 4 (which is actually half of font height /2), which positions the center of // the text at the center of the slider. // Left string s = timeToString(currentTime()); int fontHeight = font.fontMetrics().height(); TextRun leftText(s); context.setFillColor(Color(textColor)); context.drawText(font, leftText, IntPoint(windowWidth / 2 - timeSliderWidth / 2 - margin - font.width(leftText), windowHeight - margin - sliderHeight / 2 + fontHeight / 4)); // Right string s = timeToString(currentTime() - duration()); TextRun rightText(s); context.setFillColor(Color(textColor)); context.drawText(font, rightText, IntPoint(windowWidth / 2 + timeSliderWidth / 2 + margin, windowHeight - margin - sliderHeight / 2 + fontHeight / 4)); // Copy to the window BLENDFUNCTION blendFunction = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA}; SIZE size = { windowWidth, windowHeight }; POINT sourcePoint = {0, 0}; POINT destPoint = { m_hudPosition.x(), m_hudPosition.y() }; BOOL result = UpdateLayeredWindow(m_hudWindow, 0, &destPoint, &size, bitmapDC.get(), &sourcePoint, 0, &blendFunction, ULW_ALPHA); context.restore(); ::SelectObject(bitmapDC.get(), oldBitmap); }
static inline float thicknessForDecoration(TextDecoration, const FontCascade& font) { // FIXME: For SVG Fonts we need to use the attributes defined in the <font-face> if specified. // Compatible with Batik/Opera return font.size() / 20.0f; }