зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1824667 - Factor middle-cropping out of nsFileControlFrame. r=jfkthame,layout-reviewers
Factor a MiddleCroppingBlockFrame that does the double reflow shenanigans. That should both be faster and easier to reason about. The only thing we need to be careful about is to use the current inline coord from the line layout if present to compute the real available isize. Differential Revision: https://phabricator.services.mozilla.com/D173668
This commit is contained in:
Родитель
82828dba9e
Коммит
894fd5d4c3
|
@ -173,6 +173,7 @@ nsIFrame* NS_NewSVGFELeafFrame(PresShell* aPresShell, ComputedStyle* aStyle);
|
|||
nsIFrame* NS_NewSVGFEImageFrame(PresShell* aPresShell, ComputedStyle* aStyle);
|
||||
nsIFrame* NS_NewSVGFEUnstyledLeafFrame(PresShell* aPresShell,
|
||||
ComputedStyle* aStyle);
|
||||
nsIFrame* NS_NewFileControlLabelFrame(PresShell*, ComputedStyle*);
|
||||
|
||||
#include "mozilla/dom/NodeInfo.h"
|
||||
#include "prenv.h"
|
||||
|
@ -3459,6 +3460,15 @@ nsCSSFrameConstructor::FindHTMLData(const Element& aElement,
|
|||
PseudoStyleType::fieldsetContent ||
|
||||
aParentFrame->GetParent()->IsFieldSetFrame(),
|
||||
"Unexpected parent for fieldset content anon box");
|
||||
|
||||
if (aElement.IsInNativeAnonymousSubtree() &&
|
||||
aElement.NodeInfo()->NameAtom() == nsGkAtoms::label &&
|
||||
aParentFrame->IsFileControlFrame()) {
|
||||
static constexpr FrameConstructionData sFileLabelData(
|
||||
NS_NewFileControlLabelFrame);
|
||||
return &sFileLabelData;
|
||||
}
|
||||
|
||||
static constexpr FrameConstructionDataByTag sHTMLData[] = {
|
||||
SIMPLE_TAG_CHAIN(img, nsCSSFrameConstructor::FindImgData),
|
||||
SIMPLE_TAG_CHAIN(mozgeneratedcontentimage,
|
||||
|
|
|
@ -21,11 +21,12 @@
|
|||
#include "mozilla/dom/HTMLButtonElement.h"
|
||||
#include "mozilla/dom/HTMLInputElement.h"
|
||||
#include "mozilla/dom/MutationEventBinding.h"
|
||||
#include "mozilla/intl/Segmenter.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/PresShell.h"
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
#include "mozilla/TextEditor.h"
|
||||
#include "MiddleCroppingBlockFrame.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsNodeInfoManager.h"
|
||||
#include "nsContentCreatorFunctions.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
@ -58,134 +59,6 @@ void nsFileControlFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
|
|||
mMouseListener = new DnDListener(this);
|
||||
}
|
||||
|
||||
bool nsFileControlFrame::CropTextToWidth(gfxContext& aRenderingContext,
|
||||
const nsIFrame* aFrame, nscoord aWidth,
|
||||
nsString& aText) {
|
||||
if (aText.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RefPtr<nsFontMetrics> fm =
|
||||
nsLayoutUtils::GetFontMetricsForFrame(aFrame, 1.0f);
|
||||
|
||||
// see if the text will completely fit in the width given
|
||||
if (const nscoord textWidth = nsLayoutUtils::AppUnitWidthOfStringBidi(
|
||||
aText, aFrame, *fm, aRenderingContext);
|
||||
textWidth <= aWidth) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
|
||||
const nsDependentString& kEllipsis = nsContentUtils::GetLocalizedEllipsis();
|
||||
|
||||
// see if the width is even smaller than the ellipsis
|
||||
fm->SetTextRunRTL(false);
|
||||
const nscoord ellipsisWidth =
|
||||
nsLayoutUtils::AppUnitWidthOfString(kEllipsis, *fm, drawTarget);
|
||||
if (ellipsisWidth >= aWidth) {
|
||||
aText = kEllipsis;
|
||||
return true;
|
||||
}
|
||||
|
||||
// determine how much of the string will fit in the max width
|
||||
nscoord totalWidth = ellipsisWidth;
|
||||
const Span text(aText);
|
||||
intl::GraphemeClusterBreakIteratorUtf16 leftIter(text);
|
||||
intl::GraphemeClusterBreakReverseIteratorUtf16 rightIter(text);
|
||||
uint32_t leftPos = 0;
|
||||
uint32_t rightPos = aText.Length();
|
||||
nsAutoString leftString, rightString;
|
||||
|
||||
while (leftPos < rightPos) {
|
||||
Maybe<uint32_t> pos = leftIter.Next();
|
||||
Span chars = text.FromTo(leftPos, *pos);
|
||||
nscoord charWidth =
|
||||
nsLayoutUtils::AppUnitWidthOfString(chars, *fm, drawTarget);
|
||||
if (totalWidth + charWidth > aWidth) {
|
||||
break;
|
||||
}
|
||||
|
||||
leftString.Append(chars);
|
||||
leftPos = *pos;
|
||||
totalWidth += charWidth;
|
||||
|
||||
if (leftPos >= rightPos) {
|
||||
break;
|
||||
}
|
||||
|
||||
pos = rightIter.Next();
|
||||
chars = text.FromTo(*pos, rightPos);
|
||||
charWidth = nsLayoutUtils::AppUnitWidthOfString(chars, *fm, drawTarget);
|
||||
if (totalWidth + charWidth > aWidth) {
|
||||
break;
|
||||
}
|
||||
|
||||
rightString.Insert(chars, 0);
|
||||
rightPos = *pos;
|
||||
totalWidth += charWidth;
|
||||
}
|
||||
|
||||
aText = leftString + kEllipsis + rightString;
|
||||
return true;
|
||||
}
|
||||
|
||||
void nsFileControlFrame::Reflow(nsPresContext* aPresContext,
|
||||
ReflowOutput& aMetrics,
|
||||
const ReflowInput& aReflowInput,
|
||||
nsReflowStatus& aStatus) {
|
||||
// Restore the uncropped filename.
|
||||
nsAutoString filename;
|
||||
HTMLInputElement::FromNode(mContent)->GetDisplayFileName(filename);
|
||||
|
||||
bool done = false;
|
||||
while (true) {
|
||||
UpdateDisplayedValue(filename, false); // update the text node
|
||||
AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
|
||||
LinesBegin()->MarkDirty();
|
||||
nsBlockFrame::Reflow(aPresContext, aMetrics, aReflowInput, aStatus);
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
nscoord lineISize = LinesBegin()->ISize();
|
||||
const auto cbWM = aMetrics.GetWritingMode();
|
||||
const auto wm = GetWritingMode();
|
||||
nscoord iSize =
|
||||
wm.IsOrthogonalTo(cbWM) ? aMetrics.BSize(cbWM) : aMetrics.ISize(cbWM);
|
||||
auto bp = GetLogicalUsedBorderAndPadding(wm);
|
||||
nscoord contentISize = iSize - bp.IStartEnd(wm);
|
||||
if (lineISize > contentISize) {
|
||||
// The filename overflows - crop it and reflow again (once).
|
||||
// NOTE: the label frame might have bidi-continuations
|
||||
auto* labelFrame = mTextContent->GetPrimaryFrame();
|
||||
nscoord labelBP =
|
||||
labelFrame->GetLogicalUsedBorderAndPadding(wm).IStartEnd(wm);
|
||||
auto* lastLabelCont = labelFrame->LastContinuation();
|
||||
if (lastLabelCont != labelFrame) {
|
||||
labelBP +=
|
||||
lastLabelCont->GetLogicalUsedBorderAndPadding(wm).IStartEnd(wm);
|
||||
}
|
||||
nscoord availableISizeForLabel = contentISize;
|
||||
if (auto* buttonFrame = mBrowseFilesOrDirs->GetPrimaryFrame()) {
|
||||
availableISizeForLabel -=
|
||||
buttonFrame->ISize(wm) +
|
||||
buttonFrame->GetLogicalUsedMargin(wm).IStartEnd(wm);
|
||||
}
|
||||
if (CropTextToWidth(*aReflowInput.mRenderingContext, labelFrame,
|
||||
availableISizeForLabel - labelBP, filename)) {
|
||||
nsBlockFrame::DidReflow(aPresContext, &aReflowInput);
|
||||
aStatus.Reset();
|
||||
labelFrame->MarkSubtreeDirty();
|
||||
labelFrame->AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
|
||||
mCachedMinISize = NS_INTRINSIC_ISIZE_UNKNOWN;
|
||||
mCachedPrefISize = NS_INTRINSIC_ISIZE_UNKNOWN;
|
||||
done = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void nsFileControlFrame::DestroyFrom(nsIFrame* aDestructRoot,
|
||||
PostDestroyData& aPostDestroyData) {
|
||||
NS_ENSURE_TRUE_VOID(mContent);
|
||||
|
@ -256,11 +129,6 @@ nsresult nsFileControlFrame::CreateAnonymousContent(
|
|||
new (doc->NodeInfoManager()) nsTextNode(doc->NodeInfoManager());
|
||||
mTextContent->AppendChildTo(text, false, IgnoreErrors());
|
||||
|
||||
// Update the displayed text to reflect the current element's value.
|
||||
nsAutoString value;
|
||||
fileContent->GetDisplayFileName(value);
|
||||
UpdateDisplayedValue(value, false);
|
||||
|
||||
aElements.AppendElement(mTextContent);
|
||||
|
||||
// We should be able to interact with the element by doing drag and drop.
|
||||
|
@ -489,30 +357,6 @@ bool nsFileControlFrame::DnDListener::CanDropTheseFiles(
|
|||
return listLength <= 1 || aSupportsMultiple;
|
||||
}
|
||||
|
||||
nscoord nsFileControlFrame::GetMinISize(gfxContext* aRenderingContext) {
|
||||
nscoord result;
|
||||
DISPLAY_MIN_INLINE_SIZE(this, result);
|
||||
|
||||
// Our min inline size is our pref inline size
|
||||
result = GetPrefISize(aRenderingContext);
|
||||
return result;
|
||||
}
|
||||
|
||||
nscoord nsFileControlFrame::GetPrefISize(gfxContext* aRenderingContext) {
|
||||
nscoord result;
|
||||
DISPLAY_PREF_INLINE_SIZE(this, result);
|
||||
|
||||
// Make sure we measure with the uncropped filename.
|
||||
if (mCachedPrefISize == NS_INTRINSIC_ISIZE_UNKNOWN) {
|
||||
nsAutoString filename;
|
||||
HTMLInputElement::FromNode(mContent)->GetDisplayFileName(filename);
|
||||
UpdateDisplayedValue(filename, false);
|
||||
}
|
||||
|
||||
result = nsBlockFrame::GetPrefISize(aRenderingContext);
|
||||
return result;
|
||||
}
|
||||
|
||||
void nsFileControlFrame::SyncDisabledState() {
|
||||
if (mContent->AsElement()->State().HasState(ElementState::DISABLED)) {
|
||||
mBrowseFilesOrDirs->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, u""_ns,
|
||||
|
@ -534,29 +378,13 @@ nsresult nsFileControlFrame::GetFrameName(nsAString& aResult) const {
|
|||
}
|
||||
#endif
|
||||
|
||||
void nsFileControlFrame::UpdateDisplayedValue(const nsAString& aValue,
|
||||
bool aNotify) {
|
||||
auto* text = Text::FromNode(mTextContent->GetFirstChild());
|
||||
uint32_t oldLength = aNotify ? 0 : text->TextLength();
|
||||
text->SetText(aValue, aNotify);
|
||||
if (!aNotify) {
|
||||
// We can't notify during Reflow so we need to tell the text frame
|
||||
// about the text content change we just did.
|
||||
if (auto* textFrame = static_cast<nsTextFrame*>(text->GetPrimaryFrame())) {
|
||||
textFrame->NotifyNativeAnonymousTextnodeChange(oldLength);
|
||||
}
|
||||
nsBlockFrame* label = do_QueryFrame(mTextContent->GetPrimaryFrame());
|
||||
if (label && label->LinesBegin() != label->LinesEnd()) {
|
||||
label->AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
|
||||
label->LinesBegin()->MarkDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult nsFileControlFrame::SetFormProperty(nsAtom* aName,
|
||||
const nsAString& aValue) {
|
||||
if (nsGkAtoms::value == aName) {
|
||||
UpdateDisplayedValue(aValue, true);
|
||||
if (MiddleCroppingBlockFrame* f =
|
||||
do_QueryFrame(mTextContent->GetPrimaryFrame())) {
|
||||
f->UpdateDisplayedValueToUncroppedValue(true);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -572,7 +400,32 @@ a11y::AccType nsFileControlFrame::AccessibleType() {
|
|||
}
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Mouse listener implementation
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsFileControlFrame::MouseListener, nsIDOMEventListener)
|
||||
|
||||
class FileControlLabelFrame final : public MiddleCroppingBlockFrame {
|
||||
public:
|
||||
NS_DECL_QUERYFRAME
|
||||
NS_DECL_FRAMEARENA_HELPERS(FileControlLabelFrame)
|
||||
|
||||
FileControlLabelFrame(ComputedStyle* aStyle, nsPresContext* aPresContext)
|
||||
: MiddleCroppingBlockFrame(aStyle, aPresContext, kClassID) {}
|
||||
|
||||
HTMLInputElement& FileInput() const {
|
||||
return *HTMLInputElement::FromNode(mContent->GetParent());
|
||||
}
|
||||
|
||||
void GetUncroppedValue(nsAString& aValue) override {
|
||||
return FileInput().GetDisplayFileName(aValue);
|
||||
}
|
||||
};
|
||||
|
||||
NS_QUERYFRAME_HEAD(FileControlLabelFrame)
|
||||
NS_QUERYFRAME_ENTRY(FileControlLabelFrame)
|
||||
NS_QUERYFRAME_TAIL_INHERITING(MiddleCroppingBlockFrame)
|
||||
NS_IMPL_FRAMEARENA_HELPERS(FileControlLabelFrame)
|
||||
|
||||
nsIFrame* NS_NewFileControlLabelFrame(PresShell* aPresShell,
|
||||
ComputedStyle* aStyle) {
|
||||
return new (aPresShell)
|
||||
FileControlLabelFrame(aStyle, aPresShell->GetPresContext());
|
||||
}
|
||||
|
|
|
@ -14,13 +14,11 @@
|
|||
#include "nsIAnonymousContentCreator.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace mozilla::dom {
|
||||
class FileList;
|
||||
class BlobImpl;
|
||||
class DataTransfer;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
} // namespace mozilla::dom
|
||||
|
||||
class nsFileControlFrame final : public nsBlockFrame,
|
||||
public nsIFormControlFrame,
|
||||
|
@ -34,38 +32,29 @@ class nsFileControlFrame final : public nsBlockFrame,
|
|||
explicit nsFileControlFrame(ComputedStyle* aStyle,
|
||||
nsPresContext* aPresContext);
|
||||
|
||||
virtual void Init(nsIContent* aContent, nsContainerFrame* aParent,
|
||||
nsIFrame* aPrevInFlow) override;
|
||||
void Init(nsIContent* aContent, nsContainerFrame* aParent,
|
||||
nsIFrame* aPrevInFlow) override;
|
||||
|
||||
void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
|
||||
const ReflowInput& aReflowInput,
|
||||
nsReflowStatus& aStatus) override;
|
||||
|
||||
virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
const nsDisplayListSet& aLists) override;
|
||||
void BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
const nsDisplayListSet& aLists) override;
|
||||
|
||||
// nsIFormControlFrame
|
||||
virtual nsresult SetFormProperty(nsAtom* aName,
|
||||
const nsAString& aValue) override;
|
||||
virtual void SetFocus(bool aOn, bool aRepaint) override;
|
||||
nsresult SetFormProperty(nsAtom* aName, const nsAString& aValue) override;
|
||||
void SetFocus(bool aOn, bool aRepaint) override;
|
||||
|
||||
nscoord GetMinISize(gfxContext* aRenderingContext) override;
|
||||
nscoord GetPrefISize(gfxContext* aRenderingContext) override;
|
||||
|
||||
virtual void DestroyFrom(nsIFrame* aDestructRoot,
|
||||
PostDestroyData& aPostDestroyData) override;
|
||||
void DestroyFrom(nsIFrame* aDestructRoot,
|
||||
PostDestroyData& aPostDestroyData) override;
|
||||
|
||||
#ifdef DEBUG_FRAME_DUMP
|
||||
virtual nsresult GetFrameName(nsAString& aResult) const override;
|
||||
nsresult GetFrameName(nsAString& aResult) const override;
|
||||
#endif
|
||||
|
||||
void ElementStateChanged(mozilla::dom::ElementState aStates) override;
|
||||
|
||||
// nsIAnonymousContentCreator
|
||||
virtual nsresult CreateAnonymousContent(
|
||||
nsTArray<ContentInfo>& aElements) override;
|
||||
virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
|
||||
uint32_t aFilter) override;
|
||||
nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override;
|
||||
void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
|
||||
uint32_t aFilter) override;
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
virtual mozilla::a11y::AccType AccessibleType() override;
|
||||
|
@ -150,23 +139,10 @@ class nsFileControlFrame final : public nsBlockFrame,
|
|||
RefPtr<DnDListener> mMouseListener;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Crop aText to fit inside aWidth using the styles of aFrame.
|
||||
* @return true if aText was modified
|
||||
*/
|
||||
static bool CropTextToWidth(gfxContext& aRenderingContext,
|
||||
const nsIFrame* aFrame, nscoord aWidth,
|
||||
nsString& aText);
|
||||
|
||||
/**
|
||||
* Sync the disabled state of the content with anonymous children.
|
||||
*/
|
||||
void SyncDisabledState();
|
||||
|
||||
/**
|
||||
* Updates the displayed value by using aValue.
|
||||
*/
|
||||
void UpdateDisplayedValue(const nsAString& aValue, bool aNotify);
|
||||
};
|
||||
|
||||
#endif // nsFileControlFrame_h___
|
||||
|
|
|
@ -23,7 +23,8 @@ FRAME_CLASSES = [
|
|||
Frame("nsContinuingTextFrame", "Text", LEAF),
|
||||
Frame("nsDateTimeControlFrame", "DateTimeControl", NOT_LEAF),
|
||||
Frame("nsFieldSetFrame", "FieldSet", NOT_LEAF),
|
||||
Frame("nsFileControlFrame", "Block", LEAF),
|
||||
Frame("nsFileControlFrame", "FileControl", LEAF),
|
||||
Frame("FileControlLabelFrame", "Block", NOT_LEAF),
|
||||
Frame("nsFirstLetterFrame", "Letter", NOT_LEAF),
|
||||
Frame("nsFirstLineFrame", "Line", NOT_LEAF),
|
||||
Frame("nsFlexContainerFrame", "FlexContainer", NOT_LEAF),
|
||||
|
@ -130,6 +131,7 @@ FRAME_CLASSES = [
|
|||
Frame("ViewportFrame", "Viewport", NOT_LEAF),
|
||||
Frame("WBRFrame", "Wbr", LEAF),
|
||||
# Non-concrete classes (for FrameIID use)
|
||||
AbstractFrame("MiddleCroppingBlockFrame"),
|
||||
AbstractFrame("nsContainerFrame"),
|
||||
AbstractFrame("nsLeafFrame"),
|
||||
AbstractFrame("nsMathMLContainerFrame"),
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#include "MiddleCroppingBlockFrame.h"
|
||||
#include "nsTextFrame.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsTextNode.h"
|
||||
#include "nsLineLayout.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/intl/Segmenter.h"
|
||||
#include "mozilla/ReflowInput.h"
|
||||
#include "mozilla/ReflowOutput.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
NS_QUERYFRAME_HEAD(MiddleCroppingBlockFrame)
|
||||
NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
|
||||
NS_QUERYFRAME_ENTRY(MiddleCroppingBlockFrame)
|
||||
NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
|
||||
|
||||
MiddleCroppingBlockFrame::MiddleCroppingBlockFrame(ComputedStyle* aStyle,
|
||||
nsPresContext* aPresContext,
|
||||
ClassID aClassID)
|
||||
: nsBlockFrame(aStyle, aPresContext, aClassID) {}
|
||||
|
||||
MiddleCroppingBlockFrame::~MiddleCroppingBlockFrame() = default;
|
||||
|
||||
void MiddleCroppingBlockFrame::UpdateDisplayedValue(const nsAString& aValue,
|
||||
bool aNotify) {
|
||||
auto* text = mTextNode.get();
|
||||
uint32_t oldLength = aNotify ? 0 : text->TextLength();
|
||||
text->SetText(aValue, aNotify);
|
||||
if (!aNotify) {
|
||||
// We can't notify during Reflow so we need to tell the text frame about the
|
||||
// text content change we just did.
|
||||
if (auto* textFrame = static_cast<nsTextFrame*>(text->GetPrimaryFrame())) {
|
||||
textFrame->NotifyNativeAnonymousTextnodeChange(oldLength);
|
||||
}
|
||||
if (LinesBegin() != LinesEnd()) {
|
||||
AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
|
||||
LinesBegin()->MarkDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MiddleCroppingBlockFrame::UpdateDisplayedValueToUncroppedValue(
|
||||
bool aNotify) {
|
||||
nsAutoString value;
|
||||
GetUncroppedValue(value);
|
||||
UpdateDisplayedValue(value, aNotify);
|
||||
}
|
||||
|
||||
nscoord MiddleCroppingBlockFrame::GetMinISize(gfxContext* aRenderingContext) {
|
||||
nscoord result;
|
||||
DISPLAY_MIN_INLINE_SIZE(this, result);
|
||||
|
||||
// Our min inline size is our pref inline size
|
||||
result = GetPrefISize(aRenderingContext);
|
||||
return result;
|
||||
}
|
||||
|
||||
nscoord MiddleCroppingBlockFrame::GetPrefISize(gfxContext* aRenderingContext) {
|
||||
nscoord result;
|
||||
DISPLAY_PREF_INLINE_SIZE(this, result);
|
||||
|
||||
// Make sure we measure with the uncropped value.
|
||||
if (mCachedPrefISize == NS_INTRINSIC_ISIZE_UNKNOWN) {
|
||||
UpdateDisplayedValueToUncroppedValue(false);
|
||||
}
|
||||
|
||||
result = nsBlockFrame::GetPrefISize(aRenderingContext);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MiddleCroppingBlockFrame::CropTextToWidth(gfxContext& aRenderingContext,
|
||||
nscoord aWidth,
|
||||
nsString& aText) const {
|
||||
if (aText.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RefPtr<nsFontMetrics> fm = nsLayoutUtils::GetFontMetricsForFrame(this, 1.0f);
|
||||
|
||||
// see if the text will completely fit in the width given
|
||||
if (nsLayoutUtils::AppUnitWidthOfStringBidi(aText, this, *fm,
|
||||
aRenderingContext) <= aWidth) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
|
||||
const nsDependentString& kEllipsis = nsContentUtils::GetLocalizedEllipsis();
|
||||
|
||||
// see if the width is even smaller than the ellipsis
|
||||
fm->SetTextRunRTL(false);
|
||||
const nscoord ellipsisWidth =
|
||||
nsLayoutUtils::AppUnitWidthOfString(kEllipsis, *fm, drawTarget);
|
||||
if (ellipsisWidth >= aWidth) {
|
||||
aText = kEllipsis;
|
||||
return true;
|
||||
}
|
||||
|
||||
// determine how much of the string will fit in the max width
|
||||
nscoord totalWidth = ellipsisWidth;
|
||||
const Span text(aText);
|
||||
intl::GraphemeClusterBreakIteratorUtf16 leftIter(text);
|
||||
intl::GraphemeClusterBreakReverseIteratorUtf16 rightIter(text);
|
||||
uint32_t leftPos = 0;
|
||||
uint32_t rightPos = aText.Length();
|
||||
nsAutoString leftString, rightString;
|
||||
|
||||
while (leftPos < rightPos) {
|
||||
Maybe<uint32_t> pos = leftIter.Next();
|
||||
Span chars = text.FromTo(leftPos, *pos);
|
||||
nscoord charWidth =
|
||||
nsLayoutUtils::AppUnitWidthOfString(chars, *fm, drawTarget);
|
||||
if (totalWidth + charWidth > aWidth) {
|
||||
break;
|
||||
}
|
||||
|
||||
leftString.Append(chars);
|
||||
leftPos = *pos;
|
||||
totalWidth += charWidth;
|
||||
|
||||
if (leftPos >= rightPos) {
|
||||
break;
|
||||
}
|
||||
|
||||
pos = rightIter.Next();
|
||||
chars = text.FromTo(*pos, rightPos);
|
||||
charWidth = nsLayoutUtils::AppUnitWidthOfString(chars, *fm, drawTarget);
|
||||
if (totalWidth + charWidth > aWidth) {
|
||||
break;
|
||||
}
|
||||
|
||||
rightString.Insert(chars, 0);
|
||||
rightPos = *pos;
|
||||
totalWidth += charWidth;
|
||||
}
|
||||
|
||||
aText = leftString + kEllipsis + rightString;
|
||||
return true;
|
||||
}
|
||||
|
||||
void MiddleCroppingBlockFrame::Reflow(nsPresContext* aPresContext,
|
||||
ReflowOutput& aDesiredSize,
|
||||
const ReflowInput& aReflowInput,
|
||||
nsReflowStatus& aStatus) {
|
||||
// Restore the uncropped value.
|
||||
nsAutoString value;
|
||||
GetUncroppedValue(value);
|
||||
bool done = false;
|
||||
while (true) {
|
||||
UpdateDisplayedValue(value, false); // update the text node
|
||||
AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
|
||||
LinesBegin()->MarkDirty();
|
||||
nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
nscoord currentICoord = aReflowInput.mLineLayout
|
||||
? aReflowInput.mLineLayout->GetCurrentICoord()
|
||||
: 0;
|
||||
nscoord availSize = aReflowInput.AvailableISize() - currentICoord;
|
||||
if (LinesBegin()->ISize() > availSize) {
|
||||
// The value overflows - crop it and reflow again (once).
|
||||
if (CropTextToWidth(*aReflowInput.mRenderingContext, availSize, value)) {
|
||||
nsBlockFrame::DidReflow(aPresContext, &aReflowInput);
|
||||
aStatus.Reset();
|
||||
MarkSubtreeDirty();
|
||||
AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
|
||||
mCachedMinISize = NS_INTRINSIC_ISIZE_UNKNOWN;
|
||||
mCachedPrefISize = NS_INTRINSIC_ISIZE_UNKNOWN;
|
||||
done = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult MiddleCroppingBlockFrame::CreateAnonymousContent(
|
||||
nsTArray<ContentInfo>& aContent) {
|
||||
auto* doc = PresContext()->Document();
|
||||
mTextNode = new (doc->NodeInfoManager()) nsTextNode(doc->NodeInfoManager());
|
||||
// Update the displayed text to reflect the current element's value.
|
||||
UpdateDisplayedValueToUncroppedValue(false);
|
||||
aContent.AppendElement(mTextNode);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void MiddleCroppingBlockFrame::AppendAnonymousContentTo(
|
||||
nsTArray<nsIContent*>& aContent, uint32_t aFilter) {
|
||||
aContent.AppendElement(mTextNode);
|
||||
}
|
||||
|
||||
void MiddleCroppingBlockFrame::DestroyFrom(nsIFrame* aDestructRoot,
|
||||
PostDestroyData& aPostDestroyData) {
|
||||
aPostDestroyData.AddAnonymousContent(mTextNode.forget());
|
||||
nsBlockFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,63 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_MiddleCroppingBlockFrame_h
|
||||
#define mozilla_MiddleCroppingBlockFrame_h
|
||||
|
||||
#include "nsBlockFrame.h"
|
||||
#include "nsIAnonymousContentCreator.h"
|
||||
#include "nsQueryFrame.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// A block frame that implements a simplistic version of middle-cropping. Note
|
||||
// that this is not fully l10n aware or complex-writing-system-friendly, so it's
|
||||
// generally mostly useful for filenames / urls / etc.
|
||||
class MiddleCroppingBlockFrame : public nsBlockFrame,
|
||||
public nsIAnonymousContentCreator {
|
||||
public:
|
||||
virtual void GetUncroppedValue(nsAString&) = 0;
|
||||
void UpdateDisplayedValueToUncroppedValue(bool aNotify);
|
||||
|
||||
NS_DECL_QUERYFRAME_TARGET(MiddleCroppingBlockFrame)
|
||||
NS_DECL_QUERYFRAME
|
||||
NS_DECL_ABSTRACT_FRAME(MiddleCroppingBlockFrame)
|
||||
|
||||
protected:
|
||||
MiddleCroppingBlockFrame(ComputedStyle*, nsPresContext*, ClassID);
|
||||
|
||||
~MiddleCroppingBlockFrame();
|
||||
|
||||
void Reflow(nsPresContext*, ReflowOutput&, const ReflowInput&,
|
||||
nsReflowStatus&) override;
|
||||
|
||||
nscoord GetMinISize(gfxContext*) override;
|
||||
nscoord GetPrefISize(gfxContext*) override;
|
||||
|
||||
/**
|
||||
* Crop aText to fit inside aWidth using the styles of aFrame.
|
||||
* @return true if aText was modified
|
||||
*/
|
||||
bool CropTextToWidth(gfxContext& aRenderingContext, nscoord aWidth,
|
||||
nsString& aText) const;
|
||||
|
||||
nsresult CreateAnonymousContent(nsTArray<ContentInfo>&) override;
|
||||
void AppendAnonymousContentTo(nsTArray<nsIContent*>&,
|
||||
uint32_t aFilter) override;
|
||||
|
||||
/**
|
||||
* Updates the displayed value by using aValue.
|
||||
*/
|
||||
void UpdateDisplayedValue(const nsAString& aValue, bool aNotify);
|
||||
void DestroyFrom(nsIFrame* aDestructRoot,
|
||||
PostDestroyData& aPostDestroyData) override;
|
||||
|
||||
RefPtr<dom::Text> mTextNode;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -165,6 +165,7 @@ UNIFIED_SOURCES += [
|
|||
"CSSAlignUtils.cpp",
|
||||
"CSSOrderAwareFrameIterator.cpp",
|
||||
"MathMLTextRunFactory.cpp",
|
||||
"MiddleCroppingBlockFrame.cpp",
|
||||
"nsAbsoluteContainingBlock.cpp",
|
||||
"nsBackdropFrame.cpp",
|
||||
"nsBlockFrame.cpp",
|
||||
|
|
Загрузка…
Ссылка в новой задаче