2017-02-22 11:49:55 -06:00
// 12 february 2017
# include "uipriv_windows.hpp"
2018-03-13 17:43:32 -05:00
# include "attrstr.hpp"
2017-02-22 11:49:55 -06:00
2017-05-17 15:15:54 -05:00
// TODO this whole file needs cleanup
2018-03-13 17:43:32 -05:00
// we 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 18:13:36 -06:00
std : : vector < backgroundFunc > * backgroundFuncs ;
2017-02-22 11:49:55 -06:00
} ;
2018-03-13 17:43:32 -05:00
// we need to combine color and underline style into one unit for IDWriteLayout::SetDrawingEffect()
// we also want to combine identical effects, which DirectWrite doesn't seem to provide a way to do
// we can at least try to goad it into doing so if we can deduplicate effects once they're all computed
// so what we do is use this class to store in-progress effects, much like uiprivCombinedFontAttr on the OS X code
// we then deduplicate them later while converting them into a form suitable for drawing with; see applyEffectsAttributes() below
class combinedEffectsAttr : public IUnknown {
ULONG refcount ;
uiAttribute * colorAttr ;
uiAttribute * underlineAttr ;
uiAttribute * underlineColorAttr ;
void setAttribute ( uiAttribute * a )
{
if ( a = = NULL )
return ;
switch ( uiAttributeGetType ( a ) ) {
case uiAttributeTypeColor :
if ( this - > colorAttr ! = NULL )
uiprivAttributeRelease ( this - > colorAttr ) ;
this - > colorAttr = uiprivAttributeRetain ( a ) ;
break ;
case uiAttributeTypeUnderline :
if ( this - > underlineAttr ! = NULL )
uiprivAttributeRelease ( this - > underlineAttr ) ;
this - > underlineAttr = uiprivAttributeRetain ( a ) ;
break ;
case uiAttributeTypeUnderlineColor :
if ( this - > underlineAttr ! = NULL )
uiprivAttributeRelease ( this - > underlineAttr ) ;
this - > underlineColorAttr = uiprivAttributeRetain ( a ) ;
break ;
}
}
public :
combinedEffectsAttr ( uiAttribute * a )
{
this - > refcount = 1 ;
this - > colorAttr = NULL ;
this - > underlineAttr = NULL ;
this - > underlineColorAttr = NULL ;
this - > setAttribute ( a ) ;
}
~ combinedEffectsAttr ( )
{
if ( this - > colorAttr ! = NULL )
uiprivAttributeRelease ( this - > colorAttr ) ;
if ( this - > underlineAttr ! = NULL )
uiprivAttributeRelease ( this - > underlineAttr ) ;
if ( this - > underlineColorAttr ! = NULL )
uiprivAttributeRelease ( this - > underlineColorAttr ) ;
}
// IUnknown
virtual HRESULT STDMETHODCALLTYPE QueryInterface ( REFIID riid , void * * ppvObject )
{
if ( ppvObject = = NULL )
return E_POINTER ;
if ( riid = = IID_IUnknown ) {
this - > AddRef ( ) ;
* ppvObject = this ;
return S_OK ;
}
* ppvObject = NULL ;
return E_NOINTERFACE ;
}
virtual ULONG STDMETHODCALLTYPE AddRef ( void )
{
this - > refcount + + ;
return this - > refcount ;
}
virtual ULONG STDMETHODCALLTYPE Release ( void )
{
this - > refcount - - ;
if ( this - > refcount = = 0 ) {
delete this ;
return 0 ;
}
return this - > refcount ;
}
combinedEffectsAttr * cloneWith ( uiAttribute * a )
{
combinedEffectsAttr * b ;
b = new combinedEffectsAttr ( this - > colorAttr ) ;
b - > setAttribute ( this - > underlineAttr ) ;
b - > setAttribute ( this - > underlineColorAttr ) ;
b - > setAttribute ( a ) ;
return b ;
}
} ;
static HRESULT addEffectAttributeToRange ( struct foreachParams * p , size_t start , size_t end , uiAttribute * attr )
2017-02-22 14:19:11 -06:00
{
2018-03-13 17:43:32 -05:00
IUnknown * u ;
combinedEffectsAttr * cea ;
DWRITE_TEXT_RANGE range ;
size_t diff ;
HRESULT hr ;
while ( start < end ) {
hr = p - > layout - > GetDrawingEffect ( start , & u , & range ) ;
if ( hr ! = S_OK )
{ logHRESULT ( L " HELP " , hr ) ;
return hr ;
} cea = ( combinedEffectsAttr * ) u ;
if ( cea = = NULL )
cea = new combinedEffectsAttr ( attr ) ;
else
cea = cea - > cloneWith ( attr ) ;
// clamp range within [start, end)
if ( range . startPosition < start ) {
diff = start - range . startPosition ;
range . startPosition = start ;
range . length - = diff ;
2017-02-22 14:19:11 -06:00
}
2018-03-13 17:43:32 -05:00
if ( ( range . startPosition + range . length ) > end )
range . length = end - range . startPosition ;
hr = p - > layout - > SetDrawingEffect ( cea , range ) ;
if ( hr ! = S_OK )
return hr ;
// TODO figure out what and when needs to be released
start + = range . length ;
2017-02-22 14:19:11 -06:00
}
2018-03-13 17:43:32 -05:00
return S_OK ;
2017-02-22 14:19:11 -06:00
}
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
}
2018-03-13 17:43:32 -05:00
static uiForEach processAttribute ( const uiAttributedString * s , const uiAttribute * attr , 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-11-04 20:44:28 -05:00
// TODO fix const correctness
start = attrstrUTF8ToUTF16 ( ( uiAttributedString * ) s , start ) ;
end = attrstrUTF8ToUTF16 ( ( uiAttributedString * ) s , end ) ;
2017-02-22 11:49:55 -06:00
range . startPosition = start ;
range . length = end - start ;
2018-03-13 17:43:32 -05:00
switch ( uiAttributeGetType ( attr ) ) {
case uiAttributeTypeFamily :
wfamily = toUTF16 ( uiAttributeFamily ( attr ) ) ;
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 ;
2018-03-13 17:43:32 -05:00
case uiAttributeTypeSize :
2017-02-22 11:49:55 -06:00
hr = p - > layout - > SetFontSize (
2018-03-13 17:43:32 -05:00
// TODO unify with fontmatch.cpp and/or attrstr.hpp
2017-02-22 11:49:55 -06:00
# define pointSizeToDWriteSize(size) (size * (96.0 / 72.0))
2018-03-13 17:43:32 -05:00
pointSizeToDWriteSize ( uiAttributeSize ( attr ) ) ,
2017-02-22 11:49:55 -06:00
range ) ;
if ( hr ! = S_OK )
logHRESULT ( L " error applying size attribute " , hr ) ;
break ;
2018-03-13 17:43:32 -05:00
case uiAttributeTypeWeight :
2017-02-22 14:19:11 -06:00
hr = p - > layout - > SetFontWeight (
2018-03-13 17:43:32 -05:00
uiprivWeightToDWriteWeight ( uiAttributeWeight ( attr ) ) ,
2017-02-22 14:19:11 -06:00
range ) ;
if ( hr ! = S_OK )
logHRESULT ( L " error applying weight attribute " , hr ) ;
2017-02-22 11:49:55 -06:00
break ;
2018-03-13 17:43:32 -05:00
case uiAttributeTypeItalic :
2017-02-22 14:19:11 -06:00
hr = p - > layout - > SetFontStyle (
2018-03-13 17:43:32 -05:00
uiprivItalicToDWriteStyle ( uiAttributeItalic ( attr ) ) ,
2017-02-22 14:19:11 -06:00
range ) ;
if ( hr ! = S_OK )
logHRESULT ( L " error applying italic attribute " , hr ) ;
2017-02-22 11:49:55 -06:00
break ;
2018-03-13 17:43:32 -05:00
case uiAttributeTypeStretch :
2017-02-22 14:19:11 -06:00
hr = p - > layout - > SetFontStretch (
2018-03-13 17:43:32 -05:00
uiprivStretchToDWriteStretch ( uiAttributeStretch ( attr ) ) ,
2017-02-22 14:19:11 -06:00
range ) ;
if ( hr ! = S_OK )
logHRESULT ( L " error applying stretch attribute " , hr ) ;
2017-02-22 11:49:55 -06:00
break ;
2018-03-13 17:43:32 -05:00
case uiAttributeTypeUnderline :
2017-02-22 18:13:36 -06:00
// mark that we have an underline; otherwise, DirectWrite will never call our custom renderer's DrawUnderline() method
hasUnderline = FALSE ;
2018-03-13 17:43:32 -05:00
if ( uiAttributeUnderline ( attr ) ! = uiUnderlineNone )
2017-02-22 18:13:36 -06:00
hasUnderline = TRUE ;
hr = p - > layout - > SetUnderline ( hasUnderline , range ) ;
if ( hr ! = S_OK )
logHRESULT ( L " error applying underline attribute " , hr ) ;
2018-03-13 17:43:32 -05:00
// and fall through to set the underline style through the drawing effect
case uiAttributeTypeColor :
case uiAttributeTypeUnderlineColor :
hr = addEffectAttributeToRange ( p , start , end , attr ) ;
if ( hr ! = S_OK )
logHRESULT ( L " error applying effect (color, underline, or underline color) attribute " , hr ) ;
break ;
case uiAttributeBackground :
p - > backgroundFuncs - > push_back (
mkBackgroundFunc ( ostart , oend ,
spec - > R , spec - > G , spec - > B , spec - > A ) ) ;
2017-02-22 11:49:55 -06:00
break ;
2018-03-13 17:43:32 -05:00
#if 0
TODO
2017-02-22 11:49:55 -06:00
switch ( spec - > Value ) {
case uiDrawUnderlineColorCustom :
2018-03-13 17:43:32 -05:00
x ( p , start , end , [ = ] ( textDrawingEffect * t ) {
2017-02-22 18:13:36 -06:00
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
2018-03-13 17:43:32 -05:00
x ( p , start , end , [ = ] ( textDrawingEffect * t ) {
2017-02-22 18:13:36 -06:00
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 :
2018-03-13 17:43:32 -05:00
x ( p , start , end , [ = ] ( textDrawingEffect * t ) {
2017-02-22 18:13:36 -06:00
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 :
2018-03-13 17:43:32 -05:00
x ( p , start , end , [ = ] ( textDrawingEffect * t ) {
2017-02-22 18:13:36 -06:00
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 ;
2018-03-13 17:43:32 -05:00
# endif
case uiAttributeTypeFeatures :
// only generate an attribute if not NULL
// TODO do we still need to do this or not...
if ( uiAttributeFeatures ( attr ) = = NULL )
2017-06-10 02:37:17 -05:00
break ;
2018-03-13 17:43:32 -05:00
dt = uiprivOpenTypeFeaturesToIDWriteTypography ( uiAttributeFeatures ( attr ) ) ;
2017-05-31 17:45:11 -05:00
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-06-06 11:47:07 -05:00
return uiForEachContinue ;
2017-02-22 11:49:55 -06:00
}
2018-03-13 17:43:32 -05:00
$ $ $ $ TODO CONTINUE HERE
static void applyEffectsAttributes ( struct foreachParams * p )
2017-02-22 14:19:11 -06:00
{
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
}