perf(ui): 缓存卡片阴影、TextBlock 字体格式和候选列表文字度量
- Card:每帧为每道阴影新建离屏位图、画刷和高斯模糊特效。改为把全部阴影 合成到一张位图,按尺寸、圆角、DPI、阴影参数和 render target 复用; 尺寸不变的重绘(选中项移动、译文与云候选补齐)直接贴图。合成失败时仍 走原来的逐帧绘制。 - TextBlock:每次测量都新建 IDWriteTextFormat,自定义回退字体还要把系统 回退表整份复制进新 builder。格式与文字、光标无关,改为只在字体、字号、 对齐或回退字体变化时重建。 - CandidateList:一帧里会测量多次(自然尺寸、保留尺寸、按最终宽度排版), 每次都为每段文字新建 DirectWrite layout,一页九项每键约一两百个。文字 宽高只取决于文字、字号、宽度和字体,改为记忆化,字体或外观变化时清空, 超过上限整体重来。空的辅助码和译文不再创建 layout。
This commit is contained in:
1 parent
f87809cf59
commit
7bbb9042fc
4 files changed
+263
-42
No files matched your search
@@ -5,6 +5,7 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <wrl/client.h>
|
||||
|
||||
@@ -630,10 +631,20 @@ class CandidateList : public Visual
|
||||
size_t HitTestItem(const PointF &point) const;
|
||||
float EstimateTextWidth(const std::wstring &text, float fontSize) const;
|
||||
RectF ItemRect(size_t index) const;
|
||||
const std::wstring &ResolvedFontFamily() const;
|
||||
// Results of EstimateTextWidth / MeasureTextHeight. A frame measures the
|
||||
// list several times (natural size, sticky size, arrange at the final
|
||||
// width) and each measurement used to build a fresh DirectWrite layout per
|
||||
// string; the metrics depend only on the text, size, width and font, so
|
||||
// they are memoized until the font changes.
|
||||
bool LookupTextMetric(wchar_t kind, const std::wstring &text, float fontSize, float width, float &value) const;
|
||||
void StoreTextMetric(wchar_t kind, const std::wstring &text, float fontSize, float width, float value) const;
|
||||
|
||||
std::vector<Item> items_;
|
||||
std::vector<ItemLayoutCache> layoutCache_;
|
||||
std::vector<ItemGeometry> itemGeometry_;
|
||||
mutable std::unordered_map<std::wstring, float> textMetricCache_;
|
||||
mutable std::wstring textMetricFamily_;
|
||||
float layoutWidth_ = 0.0f;
|
||||
Appearance appearance_{};
|
||||
Orientation orientation_ = Orientation::Vertical;
|
||||
|
||||
@@ -368,6 +368,9 @@ class Card : public Panel
|
||||
void Render(DeviceResources &deviceResources) override;
|
||||
|
||||
private:
|
||||
void RenderShadow(ID2D1RenderTarget *target);
|
||||
bool RenderCachedShadow(ID2D1RenderTarget *target, const ShadowPass *passes, size_t count);
|
||||
|
||||
Brush brush_;
|
||||
float padding_ = 0.0f;
|
||||
float shadowScale_ = 1.0f;
|
||||
@@ -375,6 +378,21 @@ class Card : public Panel
|
||||
std::vector<ShadowPass> shadowPasses_;
|
||||
bool shadowEnabled_ = true;
|
||||
SizeF childSize_ = {};
|
||||
|
||||
// All blur passes composed into one bitmap. Rebuilding the blur inputs and
|
||||
// effects on every frame dominated repaints that do not change the card's
|
||||
// size (selection moves, late candidate enrichment); the bitmap is reused
|
||||
// while the key below matches and belongs to cachedShadowTarget_.
|
||||
Microsoft::WRL::ComPtr<ID2D1RenderTarget> cachedShadowTarget_;
|
||||
Microsoft::WRL::ComPtr<ID2D1Bitmap> cachedShadowBitmap_;
|
||||
SizeF cachedShadowSize_ = {};
|
||||
float cachedShadowRadius_ = 0.0f;
|
||||
float cachedShadowScale_ = 0.0f;
|
||||
float cachedShadowOpacity_ = 0.0f;
|
||||
float cachedShadowDpiX_ = 0.0f;
|
||||
float cachedShadowDpiY_ = 0.0f;
|
||||
float cachedShadowPad_ = 0.0f;
|
||||
std::vector<ShadowPass> cachedShadowPasses_;
|
||||
};
|
||||
|
||||
class TextBlock : public Visual
|
||||
@@ -399,6 +417,7 @@ class TextBlock : public Visual
|
||||
|
||||
private:
|
||||
void InvalidateTextLayoutCache();
|
||||
void InvalidateTextFormatCache();
|
||||
|
||||
std::wstring text_;
|
||||
float fontSize_ = 16.0f;
|
||||
@@ -417,6 +436,11 @@ class TextBlock : public Visual
|
||||
std::wstring cachedFontFamily_;
|
||||
float cachedLayoutWidth_ = -1.0f;
|
||||
Microsoft::WRL::ComPtr<IDWriteTextLayout> cachedTextLayout_;
|
||||
// The format (family, size, weight, alignment, fallback) outlives text and
|
||||
// caret changes; building it, and a custom fallback chain in particular,
|
||||
// cost more than the layout itself on every keystroke.
|
||||
std::wstring cachedFormatFamily_;
|
||||
Microsoft::WRL::ComPtr<IDWriteTextFormat> cachedTextFormat_;
|
||||
};
|
||||
|
||||
class Spacer : public Visual
|
||||
|
||||
+89
-13
@@ -7,7 +7,9 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <d2d1_3.h>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
@@ -2705,9 +2707,70 @@ void CandidateList::SetAppearance(Appearance appearance)
|
||||
{
|
||||
appearance_ = appearance;
|
||||
InvalidateLayoutCache();
|
||||
textMetricCache_.clear();
|
||||
InvalidateMeasure();
|
||||
}
|
||||
|
||||
const std::wstring &CandidateList::ResolvedFontFamily() const
|
||||
{
|
||||
const Theme &theme = ThemeManager::GetCurrent();
|
||||
return appearance_.fontFamily.empty() ? theme.textInputFontFamily : appearance_.fontFamily;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
std::wstring TextMetricKey(wchar_t kind, const std::wstring &text, float fontSize, float width)
|
||||
{
|
||||
uint32_t sizeBits = 0;
|
||||
uint32_t widthBits = 0;
|
||||
static_assert(sizeof(float) == sizeof(uint32_t));
|
||||
std::memcpy(&sizeBits, &fontSize, sizeof(sizeBits));
|
||||
std::memcpy(&widthBits, &width, sizeof(widthBits));
|
||||
std::wstring key;
|
||||
key.reserve(text.size() + 5);
|
||||
key.push_back(kind);
|
||||
key.push_back(static_cast<wchar_t>(sizeBits & 0xFFFF));
|
||||
key.push_back(static_cast<wchar_t>(sizeBits >> 16));
|
||||
key.push_back(static_cast<wchar_t>(widthBits & 0xFFFF));
|
||||
key.push_back(static_cast<wchar_t>(widthBits >> 16));
|
||||
key.append(text);
|
||||
return key;
|
||||
}
|
||||
|
||||
// Enough for several pages of candidates with labels, annotations and
|
||||
// translations; past it the cache simply starts over.
|
||||
constexpr size_t kMaxTextMetricEntries = 2048;
|
||||
} // namespace
|
||||
|
||||
bool CandidateList::LookupTextMetric(wchar_t kind, const std::wstring &text, float fontSize, float width,
|
||||
float &value) const
|
||||
{
|
||||
const std::wstring &family = ResolvedFontFamily();
|
||||
if (textMetricFamily_ != family)
|
||||
{
|
||||
textMetricCache_.clear();
|
||||
textMetricFamily_ = family;
|
||||
return false;
|
||||
}
|
||||
const auto found = textMetricCache_.find(TextMetricKey(kind, text, fontSize, width));
|
||||
if (found == textMetricCache_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
value = found->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CandidateList::StoreTextMetric(wchar_t kind, const std::wstring &text, float fontSize, float width,
|
||||
float value) const
|
||||
{
|
||||
if (textMetricCache_.size() >= kMaxTextMetricEntries)
|
||||
{
|
||||
textMetricCache_.clear();
|
||||
}
|
||||
textMetricCache_[TextMetricKey(kind, text, fontSize, width)] = value;
|
||||
}
|
||||
|
||||
void CandidateList::SetOrientation(Orientation orientation)
|
||||
{
|
||||
orientation_ = orientation;
|
||||
@@ -2732,10 +2795,14 @@ float CandidateList::EstimateTextWidth(const std::wstring &text, float fontSize)
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float cached = 0.0f;
|
||||
if (LookupTextMetric(L'w', text, fontSize, 0.0f, cached))
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
|
||||
IDWriteFactory *factory = GetSharedDWriteFactory();
|
||||
const Theme &theme = ThemeManager::GetCurrent();
|
||||
const std::wstring &fontFamily =
|
||||
appearance_.fontFamily.empty() ? theme.textInputFontFamily : appearance_.fontFamily;
|
||||
const std::wstring &fontFamily = ResolvedFontFamily();
|
||||
ComPtr<IDWriteTextLayout> layout = CreateCachedTextLayout(
|
||||
factory, fontFamily, text, fontSize, DWRITE_FONT_WEIGHT_NORMAL, 4096.0f, std::max(fontSize * 2.0f, 1.0f),
|
||||
DWRITE_TEXT_ALIGNMENT_LEADING, DWRITE_PARAGRAPH_ALIGNMENT_CENTER, DWRITE_WORD_WRAPPING_NO_WRAP,
|
||||
@@ -2754,22 +2821,29 @@ float CandidateList::EstimateTextWidth(const std::wstring &text, float fontSize)
|
||||
layout->GetMetrics(&metrics);
|
||||
DWRITE_OVERHANG_METRICS overhang = {};
|
||||
layout->GetOverhangMetrics(&overhang);
|
||||
return std::ceil(metrics.widthIncludingTrailingWhitespace + std::max(overhang.right, 0.0f) + 1.0f);
|
||||
const float width = std::ceil(metrics.widthIncludingTrailingWhitespace + std::max(overhang.right, 0.0f) + 1.0f);
|
||||
StoreTextMetric(L'w', text, fontSize, 0.0f, width);
|
||||
return width;
|
||||
}
|
||||
|
||||
float CandidateList::MeasureTextHeight(const std::wstring &text, float fontSize, float width) const
|
||||
{
|
||||
if (text.empty())
|
||||
return 0.0f;
|
||||
const Theme &theme = ThemeManager::GetCurrent();
|
||||
const auto &family = appearance_.fontFamily.empty() ? theme.textInputFontFamily : appearance_.fontFamily;
|
||||
const auto layout =
|
||||
CreateCachedTextLayout(GetSharedDWriteFactory(), family, text, fontSize, DWRITE_FONT_WEIGHT_NORMAL, width,
|
||||
65536.0f, DWRITE_TEXT_ALIGNMENT_LEADING, DWRITE_PARAGRAPH_ALIGNMENT_NEAR,
|
||||
DWRITE_WORD_WRAPPING_WRAP, appearance_.fallbackFontFamilies);
|
||||
float cached = 0.0f;
|
||||
if (LookupTextMetric(L'h', text, fontSize, width, cached))
|
||||
return cached;
|
||||
const auto layout = CreateCachedTextLayout(GetSharedDWriteFactory(), ResolvedFontFamily(), text, fontSize,
|
||||
DWRITE_FONT_WEIGHT_NORMAL, width, 65536.0f,
|
||||
DWRITE_TEXT_ALIGNMENT_LEADING, DWRITE_PARAGRAPH_ALIGNMENT_NEAR,
|
||||
DWRITE_WORD_WRAPPING_WRAP, appearance_.fallbackFontFamilies);
|
||||
DWRITE_TEXT_METRICS metrics{};
|
||||
if (layout && SUCCEEDED(layout->GetMetrics(&metrics)))
|
||||
return std::ceil(metrics.height);
|
||||
{
|
||||
const float height = std::ceil(metrics.height);
|
||||
StoreTextMetric(L'h', text, fontSize, width, height);
|
||||
return height;
|
||||
}
|
||||
return std::ceil(EstimateTextWidth(text, fontSize) / width) * fontSize * 1.25f;
|
||||
}
|
||||
|
||||
@@ -3017,7 +3091,8 @@ void CandidateList::Render(DeviceResources &deviceResources)
|
||||
cache.textWidth = textRect.width;
|
||||
cache.fontFamily = fontFamily;
|
||||
}
|
||||
if (cache.fontFamily != fontFamily || cache.annotationWidth != annotationRect.width)
|
||||
if (!items_[index].annotation.empty() &&
|
||||
(cache.fontFamily != fontFamily || cache.annotationWidth != annotationRect.width))
|
||||
{
|
||||
cache.annotationLayout = CreateCachedTextLayout(
|
||||
factory, fontFamily, items_[index].annotation, appearance_.annotationFontSize,
|
||||
@@ -3027,7 +3102,8 @@ void CandidateList::Render(DeviceResources &deviceResources)
|
||||
cache.annotationWidth = annotationRect.width;
|
||||
cache.fontFamily = fontFamily;
|
||||
}
|
||||
if (cache.fontFamily != fontFamily || cache.translationWidth != translationRect.width)
|
||||
if (!items_[index].translation.empty() &&
|
||||
(cache.fontFamily != fontFamily || cache.translationWidth != translationRect.width))
|
||||
{
|
||||
cache.translationLayout = CreateCachedTextLayout(
|
||||
factory, fontFamily, items_[index].translation, translationFontSize, DWRITE_FONT_WEIGHT_NORMAL,
|
||||
|
||||
+139
-29
@@ -1912,15 +1912,7 @@ void Card::Render(DeviceResources &deviceResources)
|
||||
|
||||
if (shadowEnabled_)
|
||||
{
|
||||
if (shadowPasses_.empty())
|
||||
{
|
||||
DrawWin11WindowShadow(target, bounds_, brush_.radiusX, shadowScale_, shadowOpacity_);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawGaussianPasses(target, bounds_, brush_.radiusX, shadowPasses_.data(), shadowPasses_.size(),
|
||||
shadowScale_, shadowOpacity_);
|
||||
}
|
||||
RenderShadow(target);
|
||||
}
|
||||
|
||||
const auto roundedRect =
|
||||
@@ -1935,6 +1927,110 @@ void Card::Render(DeviceResources &deviceResources)
|
||||
}
|
||||
}
|
||||
|
||||
void Card::RenderShadow(ID2D1RenderTarget *target)
|
||||
{
|
||||
const bool builtIn = shadowPasses_.empty();
|
||||
const ShadowPass *passes = builtIn ? kMistShadowPasses : shadowPasses_.data();
|
||||
const size_t count = builtIn ? std::size(kMistShadowPasses) : shadowPasses_.size();
|
||||
if (RenderCachedShadow(target, passes, count))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Uncached path, unchanged from before the cache existed.
|
||||
cachedShadowBitmap_.Reset();
|
||||
cachedShadowTarget_.Reset();
|
||||
if (builtIn)
|
||||
{
|
||||
DrawWin11WindowShadow(target, bounds_, brush_.radiusX, shadowScale_, shadowOpacity_);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawGaussianPasses(target, bounds_, brush_.radiusX, passes, count, shadowScale_, shadowOpacity_);
|
||||
}
|
||||
}
|
||||
|
||||
bool Card::RenderCachedShadow(ID2D1RenderTarget *target, const ShadowPass *passes, size_t count)
|
||||
{
|
||||
if (bounds_.width <= 0.0f || bounds_.height <= 0.0f || count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
float dpiX = 96.0f;
|
||||
float dpiY = 96.0f;
|
||||
target->GetDpi(&dpiX, &dpiY);
|
||||
const bool hit = cachedShadowBitmap_ && cachedShadowTarget_.Get() == target &&
|
||||
IsSameSize(cachedShadowSize_, {bounds_.width, bounds_.height}) &&
|
||||
cachedShadowRadius_ == brush_.radiusX && cachedShadowScale_ == shadowScale_ &&
|
||||
cachedShadowOpacity_ == shadowOpacity_ && cachedShadowDpiX_ == dpiX && cachedShadowDpiY_ == dpiY &&
|
||||
cachedShadowPasses_.size() == count &&
|
||||
std::equal(cachedShadowPasses_.begin(), cachedShadowPasses_.end(), passes);
|
||||
if (!hit)
|
||||
{
|
||||
cachedShadowBitmap_.Reset();
|
||||
cachedShadowTarget_.Reset();
|
||||
|
||||
const float s = std::max(shadowScale_, 0.15f);
|
||||
float pad = 0.0f;
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
// Same margin DrawGaussianShadowPass gives each pass, plus that pass's offset.
|
||||
const float passPad = passes[i].sigma * s * 3.0f + 4.0f +
|
||||
std::max(std::fabs(passes[i].offsetX), std::fabs(passes[i].offsetY)) * s;
|
||||
pad = std::max(pad, passPad);
|
||||
}
|
||||
pad = std::ceil(pad);
|
||||
|
||||
ComPtr<ID2D1BitmapRenderTarget> composed;
|
||||
if (FAILED(target->CreateCompatibleRenderTarget({bounds_.width + pad * 2.0f, bounds_.height + pad * 2.0f},
|
||||
composed.GetAddressOf())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
ComPtr<ID2D1DeviceContext> composedDc;
|
||||
if (FAILED(composed.As(&composedDc)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
composed->BeginDraw();
|
||||
composed->Clear(D2D1::ColorF(0, 0.0f));
|
||||
const RectF local = {pad, pad, bounds_.width, bounds_.height};
|
||||
bool drew = false;
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
if (DrawGaussianShadowPass(composed.Get(), composedDc.Get(), local, brush_.radiusX, passes[i].sigma * s,
|
||||
passes[i].alpha * shadowOpacity_, passes[i].offsetX * s, passes[i].offsetY * s))
|
||||
{
|
||||
drew = true;
|
||||
}
|
||||
}
|
||||
ComPtr<ID2D1Bitmap> bitmap;
|
||||
if (FAILED(composed->EndDraw()) || !drew || FAILED(composed->GetBitmap(bitmap.GetAddressOf())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
cachedShadowTarget_ = target;
|
||||
cachedShadowBitmap_ = bitmap;
|
||||
cachedShadowSize_ = {bounds_.width, bounds_.height};
|
||||
cachedShadowRadius_ = brush_.radiusX;
|
||||
cachedShadowScale_ = shadowScale_;
|
||||
cachedShadowOpacity_ = shadowOpacity_;
|
||||
cachedShadowDpiX_ = dpiX;
|
||||
cachedShadowDpiY_ = dpiY;
|
||||
cachedShadowPad_ = pad;
|
||||
cachedShadowPasses_.assign(passes, passes + count);
|
||||
}
|
||||
|
||||
const float pad = cachedShadowPad_;
|
||||
target->DrawBitmap(cachedShadowBitmap_.Get(),
|
||||
D2D1::RectF(bounds_.x - pad, bounds_.y - pad, bounds_.x + bounds_.width + pad,
|
||||
bounds_.y + bounds_.height + pad),
|
||||
1.0f, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR);
|
||||
return true;
|
||||
}
|
||||
|
||||
TextBlock::TextBlock(std::wstring text, float fontSize, D2D1_COLOR_F color, bool bold)
|
||||
: text_(std::move(text)), fontSize_(fontSize), color_(color), caretColor_(color), bold_(bold)
|
||||
{
|
||||
@@ -1960,7 +2056,7 @@ void TextBlock::SetFontSize(float fontSize)
|
||||
return;
|
||||
}
|
||||
fontSize_ = fontSize;
|
||||
InvalidateTextLayoutCache();
|
||||
InvalidateTextFormatCache();
|
||||
InvalidateMeasure();
|
||||
}
|
||||
|
||||
@@ -1972,7 +2068,7 @@ void TextBlock::SetTextAlignment(DWRITE_TEXT_ALIGNMENT alignment)
|
||||
}
|
||||
|
||||
textAlignment_ = alignment;
|
||||
InvalidateTextLayoutCache();
|
||||
InvalidateTextFormatCache();
|
||||
InvalidateVisual();
|
||||
}
|
||||
|
||||
@@ -1984,7 +2080,7 @@ void TextBlock::SetFontFamily(std::wstring fontFamily)
|
||||
}
|
||||
|
||||
fontFamilyOverride_ = std::move(fontFamily);
|
||||
InvalidateTextLayoutCache();
|
||||
InvalidateTextFormatCache();
|
||||
InvalidateMeasure();
|
||||
}
|
||||
|
||||
@@ -1995,13 +2091,20 @@ void TextBlock::InvalidateTextLayoutCache()
|
||||
cachedLayoutWidth_ = -1.0f;
|
||||
}
|
||||
|
||||
void TextBlock::InvalidateTextFormatCache()
|
||||
{
|
||||
cachedTextFormat_.Reset();
|
||||
cachedFormatFamily_.clear();
|
||||
InvalidateTextLayoutCache();
|
||||
}
|
||||
|
||||
void TextBlock::SetFallbackFontFamilies(std::vector<std::wstring> families)
|
||||
{
|
||||
if (hasCustomFontFallback_ && fallbackFontFamilies_ == families)
|
||||
return;
|
||||
hasCustomFontFallback_ = true;
|
||||
fallbackFontFamilies_ = std::move(families);
|
||||
InvalidateTextLayoutCache();
|
||||
InvalidateTextFormatCache();
|
||||
InvalidateMeasure();
|
||||
}
|
||||
|
||||
@@ -2066,26 +2169,33 @@ SizeF TextBlock::Measure(const SizeF &availableSize)
|
||||
const bool needsLayout = !cachedTextLayout_ || cachedLayoutWidth_ != maxWidth || cachedFontFamily_ != fontFamily;
|
||||
if (needsLayout)
|
||||
{
|
||||
ComPtr<IDWriteTextFormat> format;
|
||||
if (FAILED(dwriteFactory->CreateTextFormat(
|
||||
fontFamily.c_str(), nullptr, bold_ ? DWRITE_FONT_WEIGHT_SEMI_BOLD : DWRITE_FONT_WEIGHT_NORMAL,
|
||||
DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, fontSize_, L"", format.GetAddressOf())))
|
||||
if (!cachedTextFormat_ || cachedFormatFamily_ != fontFamily)
|
||||
{
|
||||
ComPtr<IDWriteTextFormat> format;
|
||||
if (FAILED(dwriteFactory->CreateTextFormat(
|
||||
UiFontFallbackFamily(), nullptr, bold_ ? DWRITE_FONT_WEIGHT_SEMI_BOLD : DWRITE_FONT_WEIGHT_NORMAL,
|
||||
fontFamily.c_str(), nullptr, bold_ ? DWRITE_FONT_WEIGHT_SEMI_BOLD : DWRITE_FONT_WEIGHT_NORMAL,
|
||||
DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, fontSize_, L"", format.GetAddressOf())))
|
||||
{
|
||||
measured_ = {maxWidth, fontSize_ + textLayoutPadding_.top + textLayoutPadding_.bottom};
|
||||
return measured_;
|
||||
if (FAILED(dwriteFactory->CreateTextFormat(
|
||||
UiFontFallbackFamily(), nullptr,
|
||||
bold_ ? DWRITE_FONT_WEIGHT_SEMI_BOLD : DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
|
||||
DWRITE_FONT_STRETCH_NORMAL, fontSize_, L"", format.GetAddressOf())))
|
||||
{
|
||||
measured_ = {maxWidth, fontSize_ + textLayoutPadding_.top + textLayoutPadding_.bottom};
|
||||
return measured_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
format->SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP);
|
||||
format->SetTextAlignment(textAlignment_);
|
||||
if (hasCustomFontFallback_)
|
||||
ApplyFontFallback(dwriteFactory, format.Get(), fallbackFontFamilies_);
|
||||
else
|
||||
ApplyUiFontFallback(dwriteFactory, format.Get());
|
||||
format->SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP);
|
||||
format->SetTextAlignment(textAlignment_);
|
||||
if (hasCustomFontFallback_)
|
||||
ApplyFontFallback(dwriteFactory, format.Get(), fallbackFontFamilies_);
|
||||
else
|
||||
ApplyUiFontFallback(dwriteFactory, format.Get());
|
||||
cachedTextFormat_ = format;
|
||||
cachedFormatFamily_ = fontFamily;
|
||||
}
|
||||
IDWriteTextFormat *format = cachedTextFormat_.Get();
|
||||
|
||||
const UINT32 caretPos = static_cast<UINT32>((std::min)(caretIndex_, text_.size()));
|
||||
const bool insertSlot = showCaret_ && caretPos < text_.size();
|
||||
@@ -2095,8 +2205,8 @@ SizeF TextBlock::Measure(const SizeF &availableSize)
|
||||
layoutText.insert(layoutText.begin() + static_cast<std::ptrdiff_t>(caretPos), kCaretSlotChar);
|
||||
}
|
||||
|
||||
if (FAILED(dwriteFactory->CreateTextLayout(layoutText.c_str(), static_cast<UINT32>(layoutText.size()),
|
||||
format.Get(), maxWidth, std::numeric_limits<float>::max(),
|
||||
if (FAILED(dwriteFactory->CreateTextLayout(layoutText.c_str(), static_cast<UINT32>(layoutText.size()), format,
|
||||
maxWidth, std::numeric_limits<float>::max(),
|
||||
cachedTextLayout_.ReleaseAndGetAddressOf())))
|
||||
{
|
||||
measured_ = {maxWidth, fontSize_ + textLayoutPadding_.top + textLayoutPadding_.bottom};
|
||||
|
||||
Reference in new issue
Block a user