2017-02-22 11:49:55 -06:00
// 12 february 2017
# include "uipriv_windows.hpp"
# include "draw.hpp"
2017-05-17 15:15:54 -05:00
// TODO this whole file needs cleanup
2017-02-22 14:19:11 -06:00
// we need to combine color and underline style into one unit for IDWriteLayout::SetDrawingEffect()
2017-05-31 17:45:11 -05:00
// we also need to collect all the background blocks and add them all at once
2017-02-22 11:49:55 -06:00
// TODO contextual alternates override ligatures?
// TODO rename this struct to something that isn't exclusively foreach-ing?
struct foreachParams {
const uint16_t * s ;
2017-05-31 21:24:34 -05:00
size_t len ;
2017-02-22 11:49:55 -06:00
IDWriteTextLayout * layout ;
2017-02-22 14:19:11 -06:00
std : : map < size_t , textDrawingEffect * > * effects ;
2017-02-22 18:13:36 -06:00
std : : vector < backgroundFunc > * backgroundFuncs ;
2017-02-22 11:49:55 -06:00
} ;
2017-02-22 14:19:11 -06:00
static void ensureEffectsInRange ( struct foreachParams * p , size_t start , size_t end , std : : function < void ( textDrawingEffect * ) > f )
{
size_t i ;
size_t * key ;
textDrawingEffect * t ;
2017-05-31 21:24:34 -05:00
// TODO explain why we make one for every character
2017-02-22 14:19:11 -06:00
for ( i = start ; i < end ; i + + ) {
t = ( * ( p - > effects ) ) [ i ] ;
if ( t ! = NULL ) {
f ( t ) ;
continue ;
}
t = new textDrawingEffect ;
f ( t ) ;
( * ( p - > effects ) ) [ i ] = t ;
}
}
2017-02-22 18:13:36 -06:00
static backgroundFunc mkBackgroundFunc ( size_t start , size_t end , double r , double g , double b , double a )
2017-02-22 11:49:55 -06:00
{
2017-02-22 18:13:36 -06:00
return [ = ] ( uiDrawContext * c , uiDrawTextLayout * layout , double x , double y ) {
uiDrawBrush brush ;
2017-02-22 11:49:55 -06:00
2017-02-22 18:13:36 -06:00
brush . Type = uiDrawBrushTypeSolid ;
brush . R = r ;
brush . G = g ;
brush . B = b ;
brush . A = a ;
drawTextBackground ( c , x , y , layout , start , end , & brush , 0 ) ;
} ;
2017-02-22 11:49:55 -06:00
}
2017-06-06 11:47:07 -05:00
static uiForEach processAttribute ( uiAttributedString * s , uiAttributeSpec * spec , size_t start , size_t end , void * data )
2017-02-22 11:49:55 -06:00
{
struct foreachParams * p = ( struct foreachParams * ) data ;
DWRITE_TEXT_RANGE range ;
WCHAR * wfamily ;
2017-02-22 20:46:15 -06:00
size_t ostart , oend ;
2017-02-22 18:13:36 -06:00
BOOL hasUnderline ;
2017-05-31 17:45:11 -05:00
IDWriteTypography * dt ;
2017-02-22 11:49:55 -06:00
HRESULT hr ;
2017-02-22 20:46:15 -06:00
ostart = start ;
oend = end ;
2017-02-22 11:49:55 -06:00
start = attrstrUTF8ToUTF16 ( s , start ) ;
end = attrstrUTF8ToUTF16 ( s , end ) ;
range . startPosition = start ;
range . length = end - start ;
switch ( spec - > Type ) {
case uiAttributeFamily :
2017-05-30 15:57:25 -05:00
wfamily = toUTF16 ( spec - > Family ) ;
2017-02-22 11:49:55 -06:00
hr = p - > layout - > SetFontFamilyName ( wfamily , range ) ;
if ( hr ! = S_OK )
logHRESULT ( L " error applying family name attribute " , hr ) ;
uiFree ( wfamily ) ;
break ;
case uiAttributeSize :
hr = p - > layout - > SetFontSize (
// TODO unify with drawtext.cpp
# define pointSizeToDWriteSize(size) (size * (96.0 / 72.0))
pointSizeToDWriteSize ( spec - > Double ) ,
range ) ;
if ( hr ! = S_OK )
logHRESULT ( L " error applying size attribute " , hr ) ;
break ;
case uiAttributeWeight :
2017-02-22 14:19:11 -06:00
// TODO reverse the misalignment from drawtext.cpp if it is corrected
hr = p - > layout - > SetFontWeight (
( DWRITE_FONT_WEIGHT ) ( spec - > Value ) ,
range ) ;
if ( hr ! = S_OK )
logHRESULT ( L " error applying weight attribute " , hr ) ;
2017-02-22 11:49:55 -06:00
break ;
case uiAttributeItalic :
2017-02-22 14:19:11 -06:00
hr = p - > layout - > SetFontStyle (
dwriteItalics [ ( uiDrawTextItalic ) ( spec - > Value ) ] ,
range ) ;
if ( hr ! = S_OK )
logHRESULT ( L " error applying italic attribute " , hr ) ;
2017-02-22 11:49:55 -06:00
break ;
case uiAttributeStretch :
2017-02-22 14:19:11 -06:00
hr = p - > layout - > SetFontStretch (
dwriteStretches [ ( uiDrawTextStretch ) ( spec - > Value ) ] ,
range ) ;
if ( hr ! = S_OK )
logHRESULT ( L " error applying stretch attribute " , hr ) ;
2017-02-22 11:49:55 -06:00
break ;
case uiAttributeColor :
2017-02-22 14:19:11 -06:00
ensureEffectsInRange ( p , start , end , [ = ] ( textDrawingEffect * t ) {
t - > hasColor = true ;
t - > r = spec - > R ;
t - > g = spec - > G ;
t - > b = spec - > B ;
t - > a = spec - > A ;
} ) ;
2017-02-22 11:49:55 -06:00
break ;
case uiAttributeBackground :
2017-02-22 18:13:36 -06:00
p - > backgroundFuncs - > push_back (
2017-02-22 20:46:15 -06:00
mkBackgroundFunc ( ostart , oend ,
2017-02-22 18:13:36 -06:00
spec - > R , spec - > G , spec - > B , spec - > A ) ) ;
2017-02-22 11:49:55 -06:00
break ;
case uiAttributeUnderline :
2017-02-22 18:13:36 -06:00
ensureEffectsInRange ( p , start , end , [ = ] ( textDrawingEffect * t ) {
t - > hasUnderline = true ;
t - > u = ( uiDrawUnderlineStyle ) ( spec - > Value ) ;
} ) ;
// mark that we have an underline; otherwise, DirectWrite will never call our custom renderer's DrawUnderline() method
hasUnderline = FALSE ;
if ( ( uiDrawUnderlineStyle ) ( spec - > Value ) ! = uiDrawUnderlineStyleNone )
hasUnderline = TRUE ;
hr = p - > layout - > SetUnderline ( hasUnderline , range ) ;
if ( hr ! = S_OK )
logHRESULT ( L " error applying underline attribute " , hr ) ;
2017-02-22 11:49:55 -06:00
break ;
case uiAttributeUnderlineColor :
switch ( spec - > Value ) {
case uiDrawUnderlineColorCustom :
2017-02-22 18:13:36 -06:00
ensureEffectsInRange ( p , start , end , [ = ] ( textDrawingEffect * t ) {
t - > hasUnderlineColor = true ;
t - > ur = spec - > R ;
t - > ug = spec - > G ;
t - > ub = spec - > B ;
t - > ua = spec - > A ;
} ) ;
2017-02-22 11:49:55 -06:00
break ;
2017-02-22 18:13:36 -06:00
// TODO see if Microsoft has any standard colors for this
2017-02-22 11:49:55 -06:00
case uiDrawUnderlineColorSpelling :
// TODO GtkTextView style property error-underline-color
2017-02-22 18:13:36 -06:00
ensureEffectsInRange ( p , start , end , [ = ] ( textDrawingEffect * t ) {
t - > hasUnderlineColor = true ;
t - > ur = 1.0 ;
t - > ug = 0.0 ;
t - > ub = 0.0 ;
t - > ua = 1.0 ;
} ) ;
2017-02-22 11:49:55 -06:00
break ;
case uiDrawUnderlineColorGrammar :
2017-02-22 18:13:36 -06:00
ensureEffectsInRange ( p , start , end , [ = ] ( textDrawingEffect * t ) {
t - > hasUnderlineColor = true ;
t - > ur = 0.0 ;
t - > ug = 1.0 ;
t - > ub = 0.0 ;
t - > ua = 1.0 ;
} ) ;
2017-02-22 11:49:55 -06:00
break ;
case uiDrawUnderlineColorAuxiliary :
2017-02-22 18:13:36 -06:00
ensureEffectsInRange ( p , start , end , [ = ] ( textDrawingEffect * t ) {
t - > hasUnderlineColor = true ;
t - > ur = 0.0 ;
t - > ug = 0.0 ;
t - > ub = 1.0 ;
t - > ua = 1.0 ;
} ) ;
2017-02-22 11:49:55 -06:00
break ;
}
break ;
2017-05-17 18:21:27 -05:00
case uiAttributeFeatures :
2017-06-09 18:59:48 -05:00
// TODO make sure this behaves properly if spec->Features is NULL
2017-05-31 17:45:11 -05:00
dt = otfToDirectWrite ( spec - > Features ) ;
hr = p - > layout - > SetTypography ( dt , range ) ;
if ( hr ! = S_OK )
logHRESULT ( L " error applying features attribute " , hr ) ;
dt - > Release ( ) ;
2017-02-22 11:49:55 -06:00
break ;
2017-05-17 15:15:54 -05:00
default :
// TODO complain
;
2017-02-22 11:49:55 -06:00
}
2017-06-06 11:47:07 -05:00
return uiForEachContinue ;
2017-02-22 11:49:55 -06:00
}
2017-02-22 14:19:11 -06:00
static void applyAndFreeEffectsAttributes ( struct foreachParams * p )
{
2017-05-31 21:24:34 -05:00
size_t i , n ;
textDrawingEffect * effect , * effectb ;
2017-02-22 14:19:11 -06:00
DWRITE_TEXT_RANGE range ;
2017-05-31 21:25:10 -05:00
static auto apply = [ ] ( IDWriteTextLayout * layout , textDrawingEffect * effect , DWRITE_TEXT_RANGE range ) {
2017-05-31 21:24:34 -05:00
HRESULT hr ;
2017-02-22 14:19:11 -06:00
2017-05-31 21:24:34 -05:00
if ( effect = = NULL )
return ;
hr = layout - > SetDrawingEffect ( effect , range ) ;
2017-02-22 14:19:11 -06:00
if ( hr ! = S_OK )
logHRESULT ( L " error applying drawing effects attributes " , hr ) ;
2017-05-31 21:24:34 -05:00
effect - > Release ( ) ;
} ;
// go through, fililng in the effect attribute for successive ranges of identical textDrawingEffects
// we are best off treating series of identical effects as single ranges ourselves for parity across platforms, even if Windows does something similar itself
// this also avoids breaking apart surrogate pairs (though IIRC Windows doing the something similar itself might make this a non-issue here)
effect = NULL ;
n = p - > len ;
range . startPosition = 0 ;
for ( i = 0 ; i < n ; i + + ) {
effectb = ( * ( p - > effects ) ) [ i ] ;
// run of no effect?
if ( effect = = NULL & & effectb = = NULL )
continue ;
// run of the same effect?
if ( effect ! = NULL & & effectb ! = NULL )
if ( effect - > same ( effectb ) ) {
effectb - > Release ( ) ;
continue ;
}
// the effect has changed; commit the old effect
range . length = i - range . startPosition ;
apply ( p - > layout , effect , range ) ;
range . startPosition = i ;
effect = effectb ;
2017-02-22 14:19:11 -06:00
}
2017-05-31 21:24:34 -05:00
// and apply the last effect
range . length = i - range . startPosition ;
apply ( p - > layout , effect , range ) ;
2017-02-22 14:19:11 -06:00
delete p - > effects ;
}
2017-02-22 18:13:36 -06:00
void attrstrToIDWriteTextLayoutAttrs ( uiDrawTextLayoutParams * p , IDWriteTextLayout * layout , std : : vector < backgroundFunc > * * backgroundFuncs )
2017-02-22 11:49:55 -06:00
{
struct foreachParams fep ;
fep . s = attrstrUTF16 ( p - > String ) ;
2017-05-31 21:24:34 -05:00
fep . len = attrstrUTF16Len ( p - > String ) ;
2017-02-22 11:49:55 -06:00
fep . layout = layout ;
2017-02-22 14:19:11 -06:00
fep . effects = new std : : map < size_t , textDrawingEffect * > ;
2017-02-22 18:13:36 -06:00
fep . backgroundFuncs = new std : : vector < backgroundFunc > ;
2017-02-22 11:49:55 -06:00
uiAttributedStringForEachAttribute ( p - > String , processAttribute , & fep ) ;
2017-02-22 14:19:11 -06:00
applyAndFreeEffectsAttributes ( & fep ) ;
2017-02-22 18:13:36 -06:00
* backgroundFuncs = fep . backgroundFuncs ;
2017-02-22 11:49:55 -06:00
}