/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This pushes and pops a clip rect to the draw target. // // This is done to reduce fuzz in places where we may have antialiasing, // because skia is not clip-invariant: given different clips, it does not // guarantee the same result, even if the painted content doesn't intersect // the clips. // // This is a bit sad, overall, but... struct MOZ_RAII AutoClipRect {
AutoClipRect(DrawTarget& aDt, const LayoutDeviceRect& aRect) : mDt(aDt) {
mDt.PushClipRect(aRect.ToUnknownRect());
}
/* static */ void Theme::LookAndFeelChanged() {
ThemeColors::RecomputeAccentColors(); if (gNonNativeInstance) {
gNonNativeInstance->SetScrollbarDrawing(ScrollbarStyle());
} if (gNativeInstance) {
gNativeInstance->SetScrollbarDrawing(ScrollbarStyle());
}
}
auto Theme::GetDPIRatio(nsIFrame* aFrame, StyleAppearance aAppearance)
-> DPIRatio { // Widgets react to zoom, except scrollbars.
nsPresContext* pc = aFrame->PresContext(); if (IsWidgetScrollbarPart(aAppearance)) { return GetScrollbarDrawing().GetDPIRatioForScrollbarPart(pc);
} return DPIRatio(aFrame->Style()->EffectiveZoom().Zoom( float(AppUnitsPerCSSPixel()) / pc->AppUnitsPerDevPixel()));
}
// Checkbox and radio need to preserve aspect-ratio for compat. We also snap the // size to exact device pixels to avoid snapping disorting the circles. static LayoutDeviceRect CheckBoxRadioRect(const LayoutDeviceRect& aRect) { // Place a square rect in the center of aRect. auto size = std::trunc(std::min(aRect.width, aRect.height)); auto position = aRect.Center() - LayoutDevicePoint(size * 0.5, size * 0.5); return LayoutDeviceRect(position, LayoutDeviceSize(size, size));
}
auto [bg, border] =
ComputeTextfieldColors(aState, aColors, OutlineCoversBorder::No); // We don't paint a checkmark in this case so any color would do. return std::make_tuple(bg, border, sTransparent);
}
sRGBColor Theme::ComputeBorderColor(const ElementState& aState, const Colors& aColors,
OutlineCoversBorder aOutlineCoversBorder) { bool isDisabled = aState.HasState(ElementState::DISABLED); bool isActive =
aState.HasAllStates(ElementState::HOVER | ElementState::ACTIVE); bool isHovered = aState.HasState(ElementState::HOVER); if (aColors.HighContrast()) { return aColors.System(isDisabled ? StyleSystemColor::Graytext
: (isHovered && !isActive)
? StyleSystemColor::Selecteditem
: StyleSystemColor::Buttontext);
} bool isFocused = aState.HasState(ElementState::FOCUSRING); if (isDisabled) { return sColorGrey40Alpha50;
} if (isFocused && aOutlineCoversBorder == OutlineCoversBorder::Yes) { // If we draw the outline over the border, prevent issues where the border // shows underneath if it snaps in the wrong direction by using a // transparent border. An alternative to this is ensuring that we snap the // offset in PaintRoundedFocusRect the same was a we snap border widths, so // that negative offsets are guaranteed to cover the border. // But this looks harder to mess up. return sTransparent;
} bool dark = aColors.IsDark(); if (isActive) { return dark ? sColorGrey20 : sColorGrey60;
} if (isHovered) { return dark ? sColorGrey30 : sColorGrey50;
} return sColorGrey40;
}
if (aState.HasState(ElementState::CHECKED)) {
LayoutDeviceRect rect(aRect); // Make the inner white dot take ~half of the checkbox (snapped to device // pixels).
rect.Deflate(std::max(LayoutDeviceIntCoord(1),
(LayoutDeviceCoord(rect.width) * 0.25f).Truncated()));
PaintStrokedCircle(aPaintData, rect, checkColor, sTransparent, 0,
aDpiRatio);
}
constbool isHorizontal = !nsNativeTheme::IsVerticalProgress(aFrame); if (isHorizontal) { // Center it vertically.
rect.y += (rect.height - thickness) / 2;
rect.height = thickness;
} else { // Center it horizontally.
rect.x += (rect.width - thickness) / 2;
rect.width = thickness;
}
{ // Paint the track, unclipped. auto [backgroundColor, borderColor] = ComputeProgressTrackColors(aColors);
ThemeDrawing::PaintRoundedRectWithRadius(aPaintData, rect, rect,
backgroundColor, borderColor,
borderWidth, radius, aDpiRatio);
}
// Now paint the chunk, clipped as needed.
LayoutDeviceRect clipRect = rect; if (aState.HasState(ElementState::INDETERMINATE)) { // For indeterminate progress, we paint an animated chunk of 1/3 of the // progress size. // // Animation speed and math borrowed from GTK. const LayoutDeviceCoord size = isHorizontal ? rect.width : rect.height; const LayoutDeviceCoord barSize = size * 0.3333f; const LayoutDeviceCoord travel = 2.0f * (size - barSize);
// Period equals to travel / pixelsPerMillisecond where pixelsPerMillisecond // equals progressSize / 1000.0. This is equivalent to 1600. constunsigned kPeriod = 1600;
constint t = PR_IntervalToMilliseconds(PR_IntervalNow()) % kPeriod; const LayoutDeviceCoord dx = travel * float(t) / float(kPeriod); if (isHorizontal) {
rect.width = barSize;
rect.x += (dx < travel * .5f) ? dx : travel - dx;
} else {
rect.height = barSize;
rect.y += (dx < travel * .5f) ? dx : travel - dx;
}
clipRect = rect; // Queue the next frame if needed. if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 60)) {
NS_WARNING("Couldn't refresh indeterminate );
}
} else { // This is the progress chunk, clip it to the right amount. double position = [&] { if (aIsMeter) { auto* meter = dom::HTMLMeterElement::FromNode(aFrame->GetContent()); if (!meter) { return 0.0;
} return meter->Position();
} auto* progress = dom::HTMLProgressElement::FromNode(aFrame->GetContent()); if (!progress) { return 0.0;
} return progress->Position();
}(); if (isHorizontal) { double clipWidth = rect.width * position;
clipRect.width = clipWidth; if (IsFrameRTL(aFrame)) {
clipRect.x += rect.width - clipWidth;
}
} else { double clipHeight = rect.height * position;
clipRect.height = clipHeight;
clipRect.y += rect.height - clipHeight;
}
}
static LayoutDeviceRect ToSnappedRect(const nsRect& aRect,
nscoord aTwipsPerPixel,
WebRenderBackendData& aDt) { // TODO: Do we need to do any more snapping here? return LayoutDeviceRect::FromAppUnits(aRect, aTwipsPerPixel);
}
switch (aAppearance) { case StyleAppearance::Radio: { auto rect = CheckBoxRadioRect(devPxRect);
PaintRadioControl(aPaintData, rect, elementState, colors, dpiRatio); break;
} case StyleAppearance::Checkbox: { if constexpr (std::is_same_v<PaintBackendData, WebRenderBackendData>) { // TODO: Need to figure out how to best draw this using WR. returnfalse;
} else { auto rect = CheckBoxRadioRect(devPxRect);
PaintCheckboxControl(aPaintData, rect, elementState, colors, dpiRatio);
} break;
} case StyleAppearance::Textarea: case StyleAppearance::Textfield: case StyleAppearance::NumberInput: case StyleAppearance::PasswordInput:
PaintTextField(aPaintData, devPxRect, elementState, colors, dpiRatio); break; case StyleAppearance::Listbox:
PaintListbox(aPaintData, devPxRect, elementState, colors, dpiRatio); break; case StyleAppearance::MenulistButton: case StyleAppearance::Menulist:
PaintMenulist(aPaintData, devPxRect, elementState, colors, dpiRatio); break; case StyleAppearance::MozMenulistArrowButton: if constexpr (std::is_same_v<PaintBackendData, WebRenderBackendData>) { // TODO: Need to figure out how to best draw this using WR. returnfalse;
} else {
PaintMenulistArrow(aFrame, aPaintData, devPxRect);
} break; case StyleAppearance::Tooltip: { const CSSCoord strokeWidth(1.0f); const CSSCoord strokeRadius(2.0f);
ThemeDrawing::PaintRoundedRectWithRadius(
aPaintData, devPxRect,
colors.System(StyleSystemColor::Infobackground),
colors.System(StyleSystemColor::Infotext), strokeWidth, strokeRadius,
dpiRatio); break;
} case StyleAppearance::SpinnerUpbutton: case StyleAppearance::SpinnerDownbutton: if constexpr (std::is_same_v<PaintBackendData, WebRenderBackendData>) { // TODO: Need to figure out how to best draw this using WR. returnfalse;
} else {
PaintSpinnerButton(aFrame, aPaintData, devPxRect, elementState,
aAppearance, colors, dpiRatio);
} break; case StyleAppearance::Range:
PaintRange(aFrame, aPaintData, devPxRect, elementState, colors, dpiRatio,
IsRangeHorizontal(aFrame)); break; case StyleAppearance::RangeThumb: // Painted as part of StyleAppearance::Range. break; case StyleAppearance::ProgressBar:
PaintProgress(aFrame, aPaintData, devPxRect, elementState, colors,
dpiRatio, /* aIsMeter = */ false); break; case StyleAppearance::Progresschunk: /* Painted as part of the progress bar */ break; case StyleAppearance::Meter:
PaintProgress(aFrame, aPaintData, devPxRect, elementState, colors,
dpiRatio, /* aIsMeter = */ true); break; case StyleAppearance::Meterchunk: /* Painted as part of the meter bar */ break; case StyleAppearance::ScrollbarthumbHorizontal: case StyleAppearance::ScrollbarthumbVertical: { bool isHorizontal =
aAppearance == StyleAppearance::ScrollbarthumbHorizontal; auto kind = ComputeScrollbarKind(aFrame, isHorizontal); return GetScrollbarDrawing().PaintScrollbarThumb(
aPaintData, devPxRect, kind, aFrame,
*nsLayoutUtils::StyleForScrollbar(aFrame), elementState, docState,
colors, dpiRatio);
} case StyleAppearance::ScrollbarHorizontal: case StyleAppearance::ScrollbarVertical: { bool isHorizontal = aAppearance == StyleAppearance::ScrollbarHorizontal; auto kind = ComputeScrollbarKind(aFrame, isHorizontal); return GetScrollbarDrawing().PaintScrollbar(
aPaintData, devPxRect, kind, aFrame,
*nsLayoutUtils::StyleForScrollbar(aFrame), elementState, docState,
colors, dpiRatio);
} case StyleAppearance::Scrollcorner: { auto kind = ComputeScrollbarKindForScrollCorner(aFrame); return GetScrollbarDrawing().PaintScrollCorner(
aPaintData, devPxRect, kind, aFrame,
*nsLayoutUtils::StyleForScrollbar(aFrame), docState, colors,
dpiRatio);
} case StyleAppearance::ScrollbarbuttonUp: case StyleAppearance::ScrollbarbuttonDown: case StyleAppearance::ScrollbarbuttonLeft: case StyleAppearance::ScrollbarbuttonRight: { // For scrollbar-width:thin, we don't display the buttons. if (!ScrollbarDrawing::IsScrollbarWidthThin(aFrame)) { if constexpr (std::is_same_v<PaintBackendData, WebRenderBackendData>) { // TODO: Need to figure out how to best draw this using WR. returnfalse;
} else { bool isHorizontal =
aAppearance == StyleAppearance::ScrollbarbuttonLeft ||
aAppearance == StyleAppearance::ScrollbarbuttonRight; auto kind = ComputeScrollbarKind(aFrame, isHorizontal);
GetScrollbarDrawing().PaintScrollbarButton(
aPaintData, aAppearance, devPxRect, kind, aFrame,
*nsLayoutUtils::StyleForScrollbar(aFrame), elementState, docState,
colors, dpiRatio);
}
} break;
} case StyleAppearance::Button: case StyleAppearance::Toolbarbutton:
PaintButton(aPaintData, devPxRect, aAppearance, elementState, colors,
dpiRatio); break; case StyleAppearance::FocusOutline:
PaintAutoStyleOutline(aFrame, aPaintData, devPxRect, colors, dpiRatio); break; default: // Various appearance values are used for XUL elements. Normally these // will not be available in content documents (and thus in the content // processes where the native basic theme can be used), but tests are // run with the remote XUL pref enabled and so we can get in here. So // we just return an error rather than assert. returnfalse;
}
LayoutDeviceIntMargin Theme::GetWidgetBorder(nsDeviceContext* aContext,
nsIFrame* aFrame,
StyleAppearance aAppearance) { switch (aAppearance) { case StyleAppearance::Textfield: case StyleAppearance::Textarea: case StyleAppearance::NumberInput: case StyleAppearance::PasswordInput: case StyleAppearance::Listbox: case StyleAppearance::Menulist: case StyleAppearance::MenulistButton: case StyleAppearance::Button: case StyleAppearance::Toolbarbutton: // Return the border size from the UA sheet, even though what we paint // doesn't actually match that. We know this is the UA sheet border // because we disable native theming when different border widths are // specified by authors, see Theme::IsWidgetStyled. // // The Rounded() bit is technically redundant, but needed to appease the // type system, we should always end up with full device pixels due to // round_border_to_device_pixels at style time. return LayoutDeviceIntMargin::FromAppUnits(
aFrame->StyleBorder()->GetComputedBorder(),
aFrame->PresContext()->AppUnitsPerDevPixel())
.Rounded(); default: return LayoutDeviceIntMargin();
}
}
bool Theme::GetWidgetPadding(nsDeviceContext* aContext, nsIFrame* aFrame,
StyleAppearance aAppearance,
LayoutDeviceIntMargin* aResult) { switch (aAppearance) { // Radios and checkboxes return a fixed size in GetMinimumWidgetSize // and have a meaningful baseline, so they can't have // author-specified padding. case StyleAppearance::Radio: case StyleAppearance::Checkbox:
aResult->SizeTo(0, 0, 0, 0); returntrue; default: break;
} returnfalse;
}
bool Theme::GetWidgetOverflow(nsDeviceContext* aContext, nsIFrame* aFrame,
StyleAppearance aAppearance,
nsRect* aOverflowRect) { // NOTE: This should theoretically use SnapBorderWidth to account for DPI, // but that would end up truncating in most cases (unless you're really // zoomed out maybe), so should be ~fine.
CSSCoord outlineWidth = 3;
CSSCoord outlineOffset = 0; switch (aAppearance) { case StyleAppearance::Range:
outlineOffset = kRangeOutlineOffset; break; case StyleAppearance::Radio: case StyleAppearance::Checkbox: case StyleAppearance::FocusOutline: break; case StyleAppearance::Textarea: case StyleAppearance::Listbox: case StyleAppearance::Textfield: case StyleAppearance::NumberInput: case StyleAppearance::PasswordInput:
outlineOffset = -kTextFieldBorderWidth; break; case StyleAppearance::MenulistButton: case StyleAppearance::Menulist: case StyleAppearance::Button: case StyleAppearance::Toolbarbutton:
outlineOffset = -kButtonBorderWidth; break; default: returnfalse;
}
nsITheme::Transparency Theme::GetWidgetTransparency(
nsIFrame* aFrame, StyleAppearance aAppearance) { if (auto scrollbar = GetScrollbarDrawing().GetScrollbarPartTransparency(
aFrame, aAppearance)) { return *scrollbar;
} if (aAppearance == StyleAppearance::Tooltip) { // We draw a rounded rect, so we need transparency. return eTransparent;
} return eUnknownTransparency;
}
bool Theme::WidgetAttributeChangeRequiresRepaint(StyleAppearance aAppearance,
nsAtom* aAttribute) { // Check the attribute to see if it's relevant. // TODO(emilio): The non-native theme doesn't use these attributes. Other // themes do, but not all of them (and not all of the ones they check are // here). return aAttribute == nsGkAtoms::disabled ||
aAttribute == nsGkAtoms::checked ||
aAttribute == nsGkAtoms::selected ||
aAttribute == nsGkAtoms::visuallyselected ||
aAttribute == nsGkAtoms::menuactive ||
aAttribute == nsGkAtoms::sortDirection ||
aAttribute == nsGkAtoms::focused ||
aAttribute == nsGkAtoms::_default || aAttribute == nsGkAtoms::open;
}
bool Theme::ThemeSupportsWidget(nsPresContext* aPresContext, nsIFrame* aFrame,
StyleAppearance aAppearance) { switch (aAppearance) { case StyleAppearance::Radio: case StyleAppearance::Checkbox: case StyleAppearance::FocusOutline: case StyleAppearance::Textarea: case StyleAppearance::Textfield: case StyleAppearance::Range: case StyleAppearance::RangeThumb: case StyleAppearance::ProgressBar: case StyleAppearance::Progresschunk: case StyleAppearance::Meter: case StyleAppearance::Meterchunk: case StyleAppearance::ScrollbarbuttonUp: case StyleAppearance::ScrollbarbuttonDown: case StyleAppearance::ScrollbarbuttonLeft: case StyleAppearance::ScrollbarbuttonRight: case StyleAppearance::ScrollbarthumbHorizontal: case StyleAppearance::ScrollbarthumbVertical: case StyleAppearance::ScrollbarHorizontal: case StyleAppearance::ScrollbarVertical: case StyleAppearance::Scrollcorner: case StyleAppearance::Button: case StyleAppearance::Toolbarbutton: case StyleAppearance::Listbox: case StyleAppearance::Menulist: case StyleAppearance::MenulistButton: case StyleAppearance::NumberInput: case StyleAppearance::PasswordInput: case StyleAppearance::MozMenulistArrowButton: case StyleAppearance::SpinnerUpbutton: case StyleAppearance::SpinnerDownbutton: case StyleAppearance::Tooltip: return !IsWidgetStyled(aPresContext, aFrame, aAppearance); default: returnfalse;
}
}
bool Theme::WidgetIsContainer(StyleAppearance aAppearance) { switch (aAppearance) { case StyleAppearance::MozMenulistArrowButton: case StyleAppearance::Radio: case StyleAppearance::Checkbox: returnfalse; default: returntrue;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.