Wrote much of the new text layout code on Windows. Now to test.
This commit is contained in:
parent
f7121774e1
commit
cac390a821
|
@ -77,152 +77,8 @@ uiDrawTextFont *mkTextFont(IDWriteFont *df, BOOL addRef, WCHAR *family, BOOL cop
|
||||||
return font;
|
return font;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO consider moving these all to dwrite.cpp
|
|
||||||
|
|
||||||
// TODO MinGW-w64 is missing this one
|
// TODO MinGW-w64 is missing this one
|
||||||
#define DWRITE_FONT_WEIGHT_SEMI_LIGHT (DWRITE_FONT_WEIGHT(350))
|
#define DWRITE_FONT_WEIGHT_SEMI_LIGHT (DWRITE_FONT_WEIGHT(350))
|
||||||
static const struct {
|
|
||||||
bool lastOne;
|
|
||||||
uiDrawTextWeight uival;
|
|
||||||
DWRITE_FONT_WEIGHT dwval;
|
|
||||||
} dwriteWeights[] = {
|
|
||||||
{ false, uiDrawTextWeightThin, DWRITE_FONT_WEIGHT_THIN },
|
|
||||||
{ false, uiDrawTextWeightUltraLight, DWRITE_FONT_WEIGHT_ULTRA_LIGHT },
|
|
||||||
{ false, uiDrawTextWeightLight, DWRITE_FONT_WEIGHT_LIGHT },
|
|
||||||
{ false, uiDrawTextWeightBook, DWRITE_FONT_WEIGHT_SEMI_LIGHT },
|
|
||||||
{ false, uiDrawTextWeightNormal, DWRITE_FONT_WEIGHT_NORMAL },
|
|
||||||
{ false, uiDrawTextWeightMedium, DWRITE_FONT_WEIGHT_MEDIUM },
|
|
||||||
{ false, uiDrawTextWeightSemiBold, DWRITE_FONT_WEIGHT_SEMI_BOLD },
|
|
||||||
{ false, uiDrawTextWeightBold, DWRITE_FONT_WEIGHT_BOLD },
|
|
||||||
{ false, uiDrawTextWeightUltraBold, DWRITE_FONT_WEIGHT_ULTRA_BOLD },
|
|
||||||
{ false, uiDrawTextWeightHeavy, DWRITE_FONT_WEIGHT_HEAVY },
|
|
||||||
{ true, uiDrawTextWeightUltraHeavy, DWRITE_FONT_WEIGHT_ULTRA_BLACK, },
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct {
|
|
||||||
bool lastOne;
|
|
||||||
uiDrawTextItalic uival;
|
|
||||||
DWRITE_FONT_STYLE dwval;
|
|
||||||
} dwriteItalics[] = {
|
|
||||||
{ false, uiDrawTextItalicNormal, DWRITE_FONT_STYLE_NORMAL },
|
|
||||||
{ false, uiDrawTextItalicOblique, DWRITE_FONT_STYLE_OBLIQUE },
|
|
||||||
{ true, uiDrawTextItalicItalic, DWRITE_FONT_STYLE_ITALIC },
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct {
|
|
||||||
bool lastOne;
|
|
||||||
uiDrawTextStretch uival;
|
|
||||||
DWRITE_FONT_STRETCH dwval;
|
|
||||||
} dwriteStretches[] = {
|
|
||||||
{ false, uiDrawTextStretchUltraCondensed, DWRITE_FONT_STRETCH_ULTRA_CONDENSED },
|
|
||||||
{ false, uiDrawTextStretchExtraCondensed, DWRITE_FONT_STRETCH_EXTRA_CONDENSED },
|
|
||||||
{ false, uiDrawTextStretchCondensed, DWRITE_FONT_STRETCH_CONDENSED },
|
|
||||||
{ false, uiDrawTextStretchSemiCondensed, DWRITE_FONT_STRETCH_SEMI_CONDENSED },
|
|
||||||
{ false, uiDrawTextStretchNormal, DWRITE_FONT_STRETCH_NORMAL },
|
|
||||||
{ false, uiDrawTextStretchSemiExpanded, DWRITE_FONT_STRETCH_SEMI_EXPANDED },
|
|
||||||
{ false, uiDrawTextStretchExpanded, DWRITE_FONT_STRETCH_EXPANDED },
|
|
||||||
{ false, uiDrawTextStretchExtraExpanded, DWRITE_FONT_STRETCH_EXTRA_EXPANDED },
|
|
||||||
{ true, uiDrawTextStretchUltraExpanded, DWRITE_FONT_STRETCH_ULTRA_EXPANDED },
|
|
||||||
};
|
|
||||||
|
|
||||||
void attrToDWriteAttr(struct dwriteAttr *attr)
|
|
||||||
{
|
|
||||||
bool found;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
found = false;
|
|
||||||
for (i = 0; ; i++) {
|
|
||||||
if (dwriteWeights[i].uival == attr->weight) {
|
|
||||||
attr->dweight = dwriteWeights[i].dwval;
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (dwriteWeights[i].lastOne)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!found)
|
|
||||||
userbug("Invalid text weight %d passed to text function.", attr->weight);
|
|
||||||
|
|
||||||
found = false;
|
|
||||||
for (i = 0; ; i++) {
|
|
||||||
if (dwriteItalics[i].uival == attr->italic) {
|
|
||||||
attr->ditalic = dwriteItalics[i].dwval;
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (dwriteItalics[i].lastOne)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!found)
|
|
||||||
userbug("Invalid text italic %d passed to text function.", attr->italic);
|
|
||||||
|
|
||||||
found = false;
|
|
||||||
for (i = 0; ; i++) {
|
|
||||||
if (dwriteStretches[i].uival == attr->stretch) {
|
|
||||||
attr->dstretch = dwriteStretches[i].dwval;
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (dwriteStretches[i].lastOne)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!found)
|
|
||||||
// TODO on other platforms too
|
|
||||||
userbug("Invalid text stretch %d passed to text function.", attr->stretch);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dwriteAttrToAttr(struct dwriteAttr *attr)
|
|
||||||
{
|
|
||||||
int weight, against, n;
|
|
||||||
int curdiff, curindex;
|
|
||||||
bool found;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
// weight is scaled; we need to test to see what's nearest
|
|
||||||
weight = (int) (attr->dweight);
|
|
||||||
against = (int) (dwriteWeights[0].dwval);
|
|
||||||
curdiff = abs(against - weight);
|
|
||||||
curindex = 0;
|
|
||||||
for (i = 1; ; i++) {
|
|
||||||
against = (int) (dwriteWeights[i].dwval);
|
|
||||||
n = abs(against - weight);
|
|
||||||
if (n < curdiff) {
|
|
||||||
curdiff = n;
|
|
||||||
curindex = i;
|
|
||||||
}
|
|
||||||
if (dwriteWeights[i].lastOne)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
attr->weight = dwriteWeights[i].uival;
|
|
||||||
|
|
||||||
// italic and stretch are simple values; we can just do a matching search
|
|
||||||
found = false;
|
|
||||||
for (i = 0; ; i++) {
|
|
||||||
if (dwriteItalics[i].dwval == attr->ditalic) {
|
|
||||||
attr->italic = dwriteItalics[i].uival;
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (dwriteItalics[i].lastOne)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!found)
|
|
||||||
// these are implbug()s because users shouldn't be able to get here directly; TODO?
|
|
||||||
implbug("invalid italic %d passed to dwriteAttrToAttr()", attr->ditalic);
|
|
||||||
|
|
||||||
found = false;
|
|
||||||
for (i = 0; ; i++) {
|
|
||||||
if (dwriteStretches[i].dwval == attr->dstretch) {
|
|
||||||
attr->stretch = dwriteStretches[i].uival;
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (dwriteStretches[i].lastOne)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!found)
|
|
||||||
implbug("invalid stretch %d passed to dwriteAttrToAttr()", attr->dstretch);
|
|
||||||
}
|
|
||||||
|
|
||||||
uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc)
|
uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc)
|
||||||
{
|
{
|
||||||
|
@ -357,13 +213,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultF
|
||||||
defaultFont->f->GetWeight(),
|
defaultFont->f->GetWeight(),
|
||||||
defaultFont->f->GetStyle(),
|
defaultFont->f->GetStyle(),
|
||||||
defaultFont->f->GetStretch(),
|
defaultFont->f->GetStretch(),
|
||||||
// typographic points are 1/72 inch; this parameter is 1/96 inch
|
|
||||||
// fortunately Microsoft does this too, in https://msdn.microsoft.com/en-us/library/windows/desktop/dd371554%28v=vs.85%29.aspx
|
|
||||||
defaultFont->size * (96.0 / 72.0),
|
|
||||||
// see http://stackoverflow.com/questions/28397971/idwritefactorycreatetextformat-failing and https://msdn.microsoft.com/en-us/library/windows/desktop/dd368203.aspx
|
|
||||||
// TODO use the current locale again?
|
|
||||||
L"",
|
|
||||||
&(layout->format));
|
|
||||||
if (hr != S_OK)
|
if (hr != S_OK)
|
||||||
logHRESULT(L"error creating IDWriteTextFormat", hr);
|
logHRESULT(L"error creating IDWriteTextFormat", hr);
|
||||||
|
|
||||||
|
@ -417,18 +267,8 @@ IDWriteTextLayout *prepareLayout(uiDrawTextLayout *layout, ID2D1RenderTarget *rt
|
||||||
IDWriteTextLayout *dl;
|
IDWriteTextLayout *dl;
|
||||||
DWRITE_TEXT_RANGE range;
|
DWRITE_TEXT_RANGE range;
|
||||||
IUnknown *unkBrush;
|
IUnknown *unkBrush;
|
||||||
DWRITE_WORD_WRAPPING wrap;
|
|
||||||
FLOAT maxWidth;
|
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
hr = dwfactory->CreateTextLayout(layout->text, layout->textlen,
|
|
||||||
layout->format,
|
|
||||||
// FLOAT is float, not double, so this should work... TODO
|
|
||||||
FLT_MAX, FLT_MAX,
|
|
||||||
&dl);
|
|
||||||
if (hr != S_OK)
|
|
||||||
logHRESULT(L"error creating IDWriteTextLayout", hr);
|
|
||||||
|
|
||||||
for (const struct layoutAttr &attr : *(layout->attrs)) {
|
for (const struct layoutAttr &attr : *(layout->attrs)) {
|
||||||
range.startPosition = layout->graphemes[attr.start];
|
range.startPosition = layout->graphemes[attr.start];
|
||||||
range.length = layout->graphemes[attr.end] - layout->graphemes[attr.start];
|
range.length = layout->graphemes[attr.end] - layout->graphemes[attr.start];
|
||||||
|
@ -452,48 +292,12 @@ IDWriteTextLayout *prepareLayout(uiDrawTextLayout *layout, ID2D1RenderTarget *rt
|
||||||
logHRESULT(L"error adding attribute to text layout", hr);
|
logHRESULT(L"error adding attribute to text layout", hr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// and set the width
|
|
||||||
// this is the only wrapping mode (apart from "no wrap") available prior to Windows 8.1
|
|
||||||
wrap = DWRITE_WORD_WRAPPING_WRAP;
|
|
||||||
maxWidth = layout->width;
|
|
||||||
if (layout->width < 0) {
|
|
||||||
wrap = DWRITE_WORD_WRAPPING_NO_WRAP;
|
|
||||||
// setting the max width in this case technically isn't needed since the wrap mode will simply ignore the max width, but let's do it just to be safe
|
|
||||||
maxWidth = FLT_MAX; // see TODO above
|
|
||||||
}
|
|
||||||
hr = dl->SetWordWrapping(wrap);
|
|
||||||
if (hr != S_OK)
|
|
||||||
logHRESULT(L"error setting word wrapping mode", hr);
|
|
||||||
hr = dl->SetMaxWidth(maxWidth);
|
|
||||||
if (hr != S_OK)
|
|
||||||
logHRESULT(L"error setting max layout width", hr);
|
|
||||||
|
|
||||||
return dl;
|
return dl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width)
|
|
||||||
{
|
|
||||||
layout->width = width;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO for a single line the height includes the leading; it should not
|
|
||||||
void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height)
|
|
||||||
{
|
|
||||||
IDWriteTextLayout *dl;
|
|
||||||
DWRITE_TEXT_METRICS metrics;
|
|
||||||
HRESULT hr;
|
|
||||||
|
|
||||||
dl = prepareLayout(layout, NULL);
|
|
||||||
hr = dl->GetMetrics(&metrics);
|
|
||||||
if (hr != S_OK)
|
|
||||||
logHRESULT(L"error getting layout metrics", hr);
|
|
||||||
*width = metrics.width;
|
|
||||||
// TODO make sure the behavior of this on empty strings is the same on all platforms
|
|
||||||
*height = metrics.height;
|
|
||||||
dl->Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout)
|
void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout)
|
||||||
{
|
{
|
||||||
IDWriteTextLayout *dl;
|
IDWriteTextLayout *dl;
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
#include "uipriv_windows.hpp"
|
#include "uipriv_windows.hpp"
|
||||||
#include "area.hpp"
|
#include "area.hpp"
|
||||||
|
|
||||||
|
// TODO https://github.com/Microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/multimedia/DirectWrite/PadWrite/TextEditor.cpp notes on explicit RTL handling under MirrorXCoordinate(); also in areadraw.cpp too?
|
||||||
|
|
||||||
static uiModifiers getModifiers(void)
|
static uiModifiers getModifiers(void)
|
||||||
{
|
{
|
||||||
uiModifiers m = 0;
|
uiModifiers m = 0;
|
||||||
|
|
|
@ -0,0 +1,244 @@
|
||||||
|
// 17 january 2017
|
||||||
|
#include "uipriv_windows.hpp"
|
||||||
|
|
||||||
|
struct uiDrawTextLayout {
|
||||||
|
IDWriteTextFormat *format;
|
||||||
|
IDWriteTextLayout *layout;
|
||||||
|
UINT32 *nLines;
|
||||||
|
struct lineInfo *lineInfo;
|
||||||
|
// for converting DirectWrite indices to byte offsets
|
||||||
|
size_t *u16tou8;
|
||||||
|
size_t nu16tou8; // TODO I don't like the casing of this name; is it even necessary?
|
||||||
|
};
|
||||||
|
|
||||||
|
// typographic points are 1/72 inch; this parameter is 1/96 inch
|
||||||
|
// fortunately Microsoft does this too, in https://msdn.microsoft.com/en-us/library/windows/desktop/dd371554%28v=vs.85%29.aspx
|
||||||
|
#define pointSizeToDWriteSize(size) (size * (96.0 / 72.0))
|
||||||
|
|
||||||
|
static const DWRITE_FONT_STYLE dwriteItalics[] = {
|
||||||
|
[uiDrawTextItalicNormal] = DWRITE_FONT_STYLE_NORMAL,
|
||||||
|
[uiDrawTextItalicOblique] = DWRITE_FONT_STYLE_OBLIQUE,
|
||||||
|
[uiDrawTextItalicItalic] = DWRITE_FONT_STYLE_ITALIC,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const DWRITE_FONT_STRETCH dwriteStretches[] = {
|
||||||
|
[uiDrawTextStretchUltraCondensed] = DWRITE_FONT_STRETCH_ULTRA_CONDENSED,
|
||||||
|
[uiDrawTextStretchExtraCondensed] = DWRITE_FONT_STRETCH_EXTRA_CONDENSED,
|
||||||
|
[uiDrawTextStretchCondensed] = DWRITE_FONT_STRETCH_CONDENSED,
|
||||||
|
[uiDrawTextStretchSemiCondensed] = DWRITE_FONT_STRETCH_SEMI_CONDENSED,
|
||||||
|
[uiDrawTextStretchNormal] = DWRITE_FONT_STRETCH_NORMAL,
|
||||||
|
[uiDrawTextStretchSemiExpanded] = DWRITE_FONT_STRETCH_SEMI_EXPANDED,
|
||||||
|
[uiDrawTextStretchExpanded] = DWRITE_FONT_STRETCH_EXPANDED,
|
||||||
|
[uiDrawTextStretchExtraExpanded] = DWRITE_FONT_STRETCH_EXTRA_EXPANDED,
|
||||||
|
[uiDrawTextStretchUltraExpanded] = DWRITE_FONT_STRETCH_ULTRA_EXPANDED,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct lineInfo {
|
||||||
|
size_t startPos;
|
||||||
|
size_t endPos;
|
||||||
|
size_t newlineCount;
|
||||||
|
double x;
|
||||||
|
double y;
|
||||||
|
double width;
|
||||||
|
double height;
|
||||||
|
double baseline;
|
||||||
|
};
|
||||||
|
|
||||||
|
// this function is deeply indebted to the PadWrite sample: https://github.com/Microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/multimedia/DirectWrite/PadWrite/TextEditor.cpp
|
||||||
|
static void computeLineInfo(uiDrawTextLayout *tl)
|
||||||
|
{
|
||||||
|
DWRITE_LINE_METRICS *dlm;
|
||||||
|
size_t nextStart;
|
||||||
|
UINT32 i;
|
||||||
|
DWRITE_HIT_TEST_METRICS htm;
|
||||||
|
UINT32 unused;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
// TODO make sure this is legal; if not, switch to GetMetrics() and use its line count field instead
|
||||||
|
hr = tl->layout->GetLineMetrics(NULL, 0, &(tl->nLines));
|
||||||
|
switch (hr) {
|
||||||
|
case S_OK:
|
||||||
|
// TODO what do we do here
|
||||||
|
case E_NOT_SUFFICIENT_BUFFER:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logHRESULT(L"error getting number of lines in IDWriteTextLayout", hr);
|
||||||
|
}
|
||||||
|
tl->lineInfo = (struct lineInfo *) uiAlloc(tl->nLines * sizeof (struct lineInfo), "struct lineInfo[] (text layout)");
|
||||||
|
|
||||||
|
dlm = new DWRITE_LINE_METRICS[tl->nLines];
|
||||||
|
// TODO make sure pasisng NULL here is legal
|
||||||
|
hr = tl->layout->GetLineMetrics(dlm, tl->nLines, NULL);
|
||||||
|
if (hr != S_OK)
|
||||||
|
logHRESULT(L"error getting IDWriteTextLayout line metrics", hr);
|
||||||
|
|
||||||
|
// assume the first line starts at position 0 and the string flow is incremental
|
||||||
|
nextStart = 0;
|
||||||
|
for (i = 0; i < tl->nLines; i++) {
|
||||||
|
tl->lineinfo[i].startPos = nextStart;
|
||||||
|
tl->lineinfo[i].endPos = nextStart + dlm[i].length;
|
||||||
|
tl->lineinfo[i].newlineCount = dlm[i].newlineLength;
|
||||||
|
nextStart = tl->lineinfo[i].endpos;
|
||||||
|
|
||||||
|
hr = layout->HitTestTextRange(line->startPos, (line->endPos - line->newlineCount) - line->startPos,
|
||||||
|
0, 0,
|
||||||
|
&htm, 1, &unused);
|
||||||
|
if (hr == E_NOT_SUFFICIENT_BUFFER)
|
||||||
|
logHRESULT(L"TODO CONTACT ANDLABS — IDWriteTextLayout::HitTestTextRange() can return multiple ranges for a single line", hr);
|
||||||
|
if (hr != S_OK)
|
||||||
|
logHRESULT(L"error getting IDWriteTextLayout line rect", hr);
|
||||||
|
// TODO verify htm.textPosition and htm.length?
|
||||||
|
tl->lineInfo[i].x = htm.left;
|
||||||
|
tl->lineInfo[i].y = htm.top;
|
||||||
|
tl->lineInfo[i].width = htm.width;
|
||||||
|
tl->lineInfo[i].height = htm.height;
|
||||||
|
// TODO verify dlm[i].height == htm.height
|
||||||
|
|
||||||
|
// TODO on Windows 8.1 and/or 10 we can use DWRITE_LINE_METRICS1 to get specific info about the ascent and descent; do we have an alternative?
|
||||||
|
// TODO and even on those platforms can we somehow split tyographic leading from spacing?
|
||||||
|
// TODO and on that note, can we have both line spacing proportionally above and uniformly below?
|
||||||
|
tl->lineinfo[i].baseline = dlm[i].baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] dlm;
|
||||||
|
}
|
||||||
|
|
||||||
|
uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width)
|
||||||
|
{
|
||||||
|
uiDrawTextLayout *tl;
|
||||||
|
WCHAR *wDefaultFamily;
|
||||||
|
DWRITE_WORD_WRAPPING wrap;
|
||||||
|
FLOAT maxWidth;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
tl = uiNew(uiDrawTextLayout);
|
||||||
|
|
||||||
|
wDefaultFamily = toUTF16(defaultFont->Family);
|
||||||
|
hr = dwfactory->CreateTextFormat(
|
||||||
|
wDefaultFamily, NULL,
|
||||||
|
// for the most part, DirectWrite weights correlate to ours
|
||||||
|
// the differences:
|
||||||
|
// - Minimum — libui: 0, DirectWrite: 1
|
||||||
|
// - Maximum — libui: 1000, DirectWrite: 999
|
||||||
|
// TODO figure out what to do about this shorter range (the actual major values are the same (but with different names), so it's just a range issue)
|
||||||
|
(DWRITE_FONT_WEIGHT) (defaultFont->Weight),
|
||||||
|
dwriteItalics[defaultFont->Italic],
|
||||||
|
dwriteStrecthes[defaultFont->Stretch],
|
||||||
|
pointSizeToDWriteSize(defaultFont->Size),
|
||||||
|
// see http://stackoverflow.com/questions/28397971/idwritefactorycreatetextformat-failing and https://msdn.microsoft.com/en-us/library/windows/desktop/dd368203.aspx
|
||||||
|
// TODO use the current locale?
|
||||||
|
L"",
|
||||||
|
&(tl->format));
|
||||||
|
if (hr != S_OK)
|
||||||
|
logHRESULT(L"error creating IDWriteTextFormat", hr);
|
||||||
|
|
||||||
|
hr = dwfactory->CreateTextLayout(
|
||||||
|
attrstrUTF16(s), attrstrUTF16Len(s),
|
||||||
|
tl->format,
|
||||||
|
// FLOAT is float, not double, so this should work... TODO
|
||||||
|
FLT_MAX, FLT_MAX,
|
||||||
|
&(tl->layout));
|
||||||
|
if (hr != S_OK)
|
||||||
|
logHRESULT(L"error creating IDWriteTextLayout", hr);
|
||||||
|
|
||||||
|
// and set the width
|
||||||
|
// this is the only wrapping mode (apart from "no wrap") available prior to Windows 8.1 (TODO verify this fact) (TODO this should be the default anyway)
|
||||||
|
wrap = DWRITE_WORD_WRAPPING_WRAP;
|
||||||
|
maxWidth = (FLOAT) width;
|
||||||
|
if (width < 0) {
|
||||||
|
// TODO is this wrapping juggling even necessary?
|
||||||
|
wrap = DWRITE_WORD_WRAPPING_NO_WRAP;
|
||||||
|
// setting the max width in this case technically isn't needed since the wrap mode will simply ignore the max width, but let's do it just to be safe
|
||||||
|
maxWidth = FLT_MAX; // see TODO above
|
||||||
|
}
|
||||||
|
hr = tl->layout->SetWordWrapping(wrap);
|
||||||
|
if (hr != S_OK)
|
||||||
|
logHRESULT(L"error setting IDWriteTextLayout word wrapping mode", hr);
|
||||||
|
hr = tl->layout->SetMaxWidth(maxWidth);
|
||||||
|
if (hr != S_OK)
|
||||||
|
logHRESULT(L"error setting IDWriteTextLayout max layout width", hr);
|
||||||
|
|
||||||
|
computeLineInfo(tl);
|
||||||
|
|
||||||
|
// and finally copy the UTF-16 to UTF-8 index conversion table
|
||||||
|
tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nu16tou8));
|
||||||
|
|
||||||
|
// TODO can/should this be moved elsewhere?
|
||||||
|
uiFree(wDefaultFamily);
|
||||||
|
return tl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawFreeTextLayout(uiDrawTextLayout *tl)
|
||||||
|
{
|
||||||
|
uiFree(tl->u16tou8);
|
||||||
|
uiFree(tl->lineInfo);
|
||||||
|
tl->layout->Release();
|
||||||
|
tl->format->Release();
|
||||||
|
uiFree(tl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y)
|
||||||
|
{
|
||||||
|
D2D1_POINT_2F pt;
|
||||||
|
ID2D1Brush *black;
|
||||||
|
|
||||||
|
// TODO document that fully opaque black is the default text color; figure out whether this is upheld in various scenarios on other platforms
|
||||||
|
// TODO figure out if this needs to be cleaned out
|
||||||
|
black = mkSolidBrush(c->rt, 0.0, 0.0, 0.0, 1.0);
|
||||||
|
|
||||||
|
pt.x = x;
|
||||||
|
pt.y = y;
|
||||||
|
// TODO D2D1_DRAW_TEXT_OPTIONS_NO_SNAP?
|
||||||
|
// TODO D2D1_DRAW_TEXT_OPTIONS_CLIP?
|
||||||
|
// TODO when setting 8.1 as minimum (TODO verify), D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT?
|
||||||
|
// TODO what is our pixel snapping setting related to the OPTIONS enum values?
|
||||||
|
c->rt->DrawTextLayout(pt, tl->layout, black, D2D1_DRAW_TEXT_OPTIONS_NONE);
|
||||||
|
|
||||||
|
black->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO for a single line the height includes the leading; should it? TextEdit on OS X always includes the leading and/or paragraph spacing, otherwise Klee won't work...
|
||||||
|
void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height)
|
||||||
|
{
|
||||||
|
DWRITE_TEXT_METRICS metrics;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
hr = tl->layout->GetMetrics(&metrics);
|
||||||
|
if (hr != S_OK)
|
||||||
|
logHRESULT(L"error getting IDWriteTextLayout layout metrics", hr);
|
||||||
|
*width = metrics.width;
|
||||||
|
// TODO make sure the behavior of this on empty strings is the same on all platforms (ideally should be 0-width, line height-height; TODO note this in the docs too)
|
||||||
|
*height = metrics.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl)
|
||||||
|
{
|
||||||
|
return tl->nLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DirectWrite doesn't provide a direct way to do this, so we have to do this manually
|
||||||
|
void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end)
|
||||||
|
{
|
||||||
|
*start = tl->lineInfo[line].startPos;
|
||||||
|
*start = tl->u16tou8[*start];
|
||||||
|
*end = tl->lineInfo[line].endPos - tl->lineInfo[line].newlineCount;
|
||||||
|
*end = tl->u16tou8[*end];
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r)
|
||||||
|
{
|
||||||
|
}
|
Loading…
Reference in New Issue