mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-13 18:49:19 +02:00
win32: reimplement BitBtn text + image drawing under Vista, Windows 7 (use 32bit alpha bitmaps + text functions which supports alpha) fixes issue #0013920
git-svn-id: trunk@20583 -
This commit is contained in:
parent
6c1a533b21
commit
38f98bdfb0
@ -34,10 +34,11 @@ uses
|
|||||||
// To get as little as posible circles,
|
// To get as little as posible circles,
|
||||||
// uncomment only when needed for registration
|
// uncomment only when needed for registration
|
||||||
////////////////////////////////////////////////////
|
////////////////////////////////////////////////////
|
||||||
CommCtrl, Windows, Classes, Buttons, Graphics, GraphType, Controls,
|
Windows, CommCtrl, Classes, Buttons, Graphics, GraphType, Controls,
|
||||||
|
LCLType, LCLProc, Themes,
|
||||||
////////////////////////////////////////////////////
|
////////////////////////////////////////////////////
|
||||||
WSProc, WSControls, WSButtons, WSLCLClasses,
|
WSProc, WSButtons, Win32WSControls, Win32WSImgList,
|
||||||
Win32WSControls, Win32WSImgList, LCLType, LCLProc, Themes;
|
Win32UxTheme, Win32Themes;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
||||||
@ -72,7 +73,7 @@ procedure DrawBitBtnImage(BitBtn: TCustomBitBtn; const ButtonCaption: string);
|
|||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses
|
uses
|
||||||
Win32Int, InterfaceBase, Win32Proc;
|
Win32Int, Win32Proc;
|
||||||
|
|
||||||
type
|
type
|
||||||
TBitBtnAceess = class(TCustomBitBtn)
|
TBitBtnAceess = class(TCustomBitBtn)
|
||||||
@ -117,6 +118,21 @@ type
|
|||||||
uAlign: UINT;
|
uAlign: UINT;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function Create32BitHBitmap(ADC: HDC; AWidth, AHeight: Integer; out BitsPtr: Pointer): HBitmap;
|
||||||
|
var
|
||||||
|
Info: Windows.TBitmapInfo;
|
||||||
|
begin
|
||||||
|
FillChar(Info, SizeOf(Info), 0);
|
||||||
|
Info.bmiHeader.biSize := SizeOf(Info.bmiHeader);
|
||||||
|
Info.bmiHeader.biWidth := AWidth;
|
||||||
|
Info.bmiHeader.biHeight := -AHeight; // top down
|
||||||
|
Info.bmiHeader.biPlanes := 1;
|
||||||
|
Info.bmiHeader.biBitCount := 32;
|
||||||
|
Info.bmiHeader.biCompression := BI_RGB;
|
||||||
|
BitsPtr := nil;
|
||||||
|
Result := Windows.CreateDIBSection(ADC, Windows.PBitmapInfo(@Info)^, DIB_RGB_COLORS, BitsPtr, 0, 0);
|
||||||
|
end;
|
||||||
|
|
||||||
{------------------------------------------------------------------------------
|
{------------------------------------------------------------------------------
|
||||||
Method: DrawBitBtnImage
|
Method: DrawBitBtnImage
|
||||||
Params: BitBtn: The TCustomBitBtn to update the image of
|
Params: BitBtn: The TCustomBitBtn to update the image of
|
||||||
@ -147,13 +163,26 @@ var
|
|||||||
ButtonCaptionW: widestring;
|
ButtonCaptionW: widestring;
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
|
|
||||||
procedure DrawBitmap(AState: TButtonState; UseThemes: Boolean);
|
procedure DrawBitmap(AState: TButtonState; UseThemes, AlphaDraw: Boolean);
|
||||||
|
const
|
||||||
|
StateToDetail: array[TButtonState] of TThemedButton =
|
||||||
|
(
|
||||||
|
{ bsUp } tbPushButtonNormal,
|
||||||
|
{ bsDisabled } tbPushButtonDisabled,
|
||||||
|
{ bsDown } tbPushButtonPressed,
|
||||||
|
{ bsExclusive } tbPushButtonPressed,
|
||||||
|
{ bsHot } tbPushButtonHot
|
||||||
|
);
|
||||||
var
|
var
|
||||||
TextFlags: integer; // flags for caption (enabled or disabled)
|
TextFlags: integer; // flags for caption (enabled or disabled)
|
||||||
glyphWidth, glyphHeight: integer;
|
glyphWidth, glyphHeight: integer;
|
||||||
OldBitmapHandle: HBITMAP; // Handle of the provious bitmap in hdcNewBitmap
|
OldBitmapHandle: HBITMAP; // Handle of the provious bitmap in hdcNewBitmap
|
||||||
AIndex: Integer;
|
AIndex: Integer;
|
||||||
AEffect: TGraphicsDrawEffect;
|
AEffect: TGraphicsDrawEffect;
|
||||||
|
TmpDC: HDC;
|
||||||
|
PaintBuffer: HPAINTBUFFER;
|
||||||
|
Options: TDTTOpts;
|
||||||
|
Details: TThemedElementDetails;
|
||||||
begin
|
begin
|
||||||
glyphWidth := srcWidth;
|
glyphWidth := srcWidth;
|
||||||
glyphHeight := srcHeight;
|
glyphHeight := srcHeight;
|
||||||
@ -166,21 +195,34 @@ var
|
|||||||
if not UseThemes and (AState = bsDisabled) then
|
if not UseThemes and (AState = bsDisabled) then
|
||||||
TextFlags := TextFlags or DSS_DISABLED;
|
TextFlags := TextFlags or DSS_DISABLED;
|
||||||
|
|
||||||
// fill with background color
|
|
||||||
OldBitmapHandle := SelectObject(hdcNewBitmap, NewBitmap);
|
OldBitmapHandle := SelectObject(hdcNewBitmap, NewBitmap);
|
||||||
|
if UseThemes and AlphaDraw then
|
||||||
|
PaintBuffer := BeginBufferedPaint(hdcNewBitmap, @BitmapRect, BPBF_COMPOSITED, nil, TmpDC)
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
TmpDC := hdcNewBitmap;
|
||||||
|
PaintBuffer := 0;
|
||||||
|
end;
|
||||||
|
OldFontHandle := SelectObject(TmpDC, BitBtn.Font.Reference.Handle);
|
||||||
|
|
||||||
// dont use BitBtn.Brush.Reference.Handle - since button is painted with BtnFace color and
|
// clear background:
|
||||||
// only glyph will have that bg - this will look very ugly
|
// for alpha bitmap clear it with $00000000 else make it solid color for
|
||||||
|
// further masking
|
||||||
|
if PaintBuffer <> 0 then
|
||||||
|
BufferedPaintClear(PaintBuffer, nil)
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
Windows.FillRect(TmpDC, BitmapRect, GetSysColorBrush(COLOR_BTNFACE));
|
||||||
|
SetTextColor(TmpDC, ColorToRGB(BitBtn.Font.Color));
|
||||||
|
end;
|
||||||
|
|
||||||
Windows.FillRect(hdcNewBitmap, BitmapRect, GetSysColorBrush(COLOR_BTNFACE));
|
|
||||||
SetTextColor(hdcNewBitmap, ColorToRGB(BitBtn.Font.Color));
|
|
||||||
if AState <> bsDisabled then
|
if AState <> bsDisabled then
|
||||||
begin
|
begin
|
||||||
if (srcWidth <> 0) and (srcHeight <> 0) then
|
if (srcWidth <> 0) and (srcHeight <> 0) then
|
||||||
begin
|
begin
|
||||||
TBitBtnAceess(BitBtn).FButtonGlyph.GetImageIndexAndEffect(AState, AIndex, AEffect);
|
TBitBtnAceess(BitBtn).FButtonGlyph.GetImageIndexAndEffect(AState, AIndex, AEffect);
|
||||||
TWin32WSCustomImageList.DrawToDC(TBitBtnAceess(BitBtn).FButtonGlyph.Images, AIndex,
|
TWin32WSCustomImageList.DrawToDC(TBitBtnAceess(BitBtn).FButtonGlyph.Images, AIndex,
|
||||||
hdcNewBitmap, Rect(XDestBitmap, YDestBitmap, glyphWidth, glyphHeight),
|
TmpDC, Rect(XDestBitmap, YDestBitmap, glyphWidth, glyphHeight),
|
||||||
TBitBtnAceess(BitBtn).FButtonGlyph.Images.BkColor,
|
TBitBtnAceess(BitBtn).FButtonGlyph.Images.BkColor,
|
||||||
TBitBtnAceess(BitBtn).FButtonGlyph.Images.BlendColor, AEffect,
|
TBitBtnAceess(BitBtn).FButtonGlyph.Images.BlendColor, AEffect,
|
||||||
TBitBtnAceess(BitBtn).FButtonGlyph.Images.DrawingStyle,
|
TBitBtnAceess(BitBtn).FButtonGlyph.Images.DrawingStyle,
|
||||||
@ -190,47 +232,73 @@ var
|
|||||||
begin
|
begin
|
||||||
// when not themed, windows wants a white background picture for disabled button image
|
// when not themed, windows wants a white background picture for disabled button image
|
||||||
if not UseThemes then
|
if not UseThemes then
|
||||||
FillRect(hdcNewBitmap, BitmapRect, GetStockObject(WHITE_BRUSH));
|
FillRect(TmpDC, BitmapRect, GetStockObject(WHITE_BRUSH));
|
||||||
|
|
||||||
if (srcWidth <> 0) and (srcHeight <> 0) then
|
if (srcWidth <> 0) and (srcHeight <> 0) then
|
||||||
begin
|
begin
|
||||||
TBitBtnAceess(BitBtn).FButtonGlyph.GetImageIndexAndEffect(AState, AIndex, AEffect);
|
TBitBtnAceess(BitBtn).FButtonGlyph.GetImageIndexAndEffect(AState, AIndex, AEffect);
|
||||||
if UseThemes then
|
if UseThemes and not AlphaDraw then
|
||||||
begin
|
begin
|
||||||
// non-themed winapi wants white/other as background/picture-disabled colors
|
// non-themed winapi wants white/other as background/picture-disabled colors
|
||||||
// themed winapi draws bitmap-as, with transparency defined by bitbtn.brush color
|
// themed winapi draws bitmap-as, with transparency defined by bitbtn.brush color
|
||||||
SetBkColor(hdcNewBitmap, GetSysColor(COLOR_BTNFACE));
|
SetBkColor(TmpDC, GetSysColor(COLOR_BTNFACE));
|
||||||
SetTextColor(hdcNewBitmap, GetSysColor(COLOR_BTNSHADOW));
|
SetTextColor(TmpDC, GetSysColor(COLOR_BTNSHADOW));
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if AEffect = gdeDisabled then
|
if (AEffect = gdeDisabled) and not AlphaDraw then
|
||||||
AEffect := gde1Bit;
|
AEffect := gde1Bit;
|
||||||
|
|
||||||
TWin32WSCustomImageList.DrawToDC(TBitBtnAceess(BitBtn).FButtonGlyph.Images, AIndex,
|
TWin32WSCustomImageList.DrawToDC(TBitBtnAceess(BitBtn).FButtonGlyph.Images, AIndex,
|
||||||
hdcNewBitmap, Rect(XDestBitmap, YDestBitmap, glyphWidth, glyphHeight),
|
TmpDC, Rect(XDestBitmap, YDestBitmap, glyphWidth, glyphHeight),
|
||||||
TBitBtnAceess(BitBtn).FButtonGlyph.Images.BkColor,
|
TBitBtnAceess(BitBtn).FButtonGlyph.Images.BkColor,
|
||||||
TBitBtnAceess(BitBtn).FButtonGlyph.Images.BlendColor, AEffect,
|
TBitBtnAceess(BitBtn).FButtonGlyph.Images.BlendColor, AEffect,
|
||||||
TBitBtnAceess(BitBtn).FButtonGlyph.Images.DrawingStyle,
|
TBitBtnAceess(BitBtn).FButtonGlyph.Images.DrawingStyle,
|
||||||
TBitBtnAceess(BitBtn).FButtonGlyph.Images.ImageType);
|
TBitBtnAceess(BitBtn).FButtonGlyph.Images.ImageType);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
SetBkMode(hdcNewBitmap, TRANSPARENT);
|
if PaintBuffer = 0 then
|
||||||
{$IFDEF WindowsUnicodeSupport}
|
|
||||||
if UnicodeEnabledOS then
|
|
||||||
begin
|
begin
|
||||||
ButtonCaptionW := UTF8ToUTF16(ButtonCaption);
|
SetBkMode(TmpDC, TRANSPARENT);
|
||||||
DrawStateW(hdcNewBitmap, 0, nil, LPARAM(ButtonCaptionW), 0, XDestText, YDestText, 0, 0, TextFlags);
|
{$IFDEF WindowsUnicodeSupport}
|
||||||
|
if UnicodeEnabledOS then
|
||||||
|
begin
|
||||||
|
ButtonCaptionW := UTF8ToUTF16(ButtonCaption);
|
||||||
|
DrawStateW(TmpDC, 0, nil, LPARAM(ButtonCaptionW), 0, XDestText, YDestText, 0, 0, TextFlags);
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
ButtonCaptionA := Utf8ToAnsi(ButtonCaption);
|
||||||
|
DrawState(TmpDC, 0, nil, LPARAM(ButtonCaptionA), 0, XDestText, YDestText, 0, 0, TextFlags);
|
||||||
|
end;
|
||||||
|
{$ELSE}
|
||||||
|
DrawState(TmpDC, 0, nil, LPARAM(ButtonCaption), 0, XDestText, YDestText, 0, 0, TextFlags);
|
||||||
|
{$ENDIF}
|
||||||
end
|
end
|
||||||
else begin
|
else
|
||||||
ButtonCaptionA := Utf8ToAnsi(ButtonCaption);
|
begin
|
||||||
DrawState(hdcNewBitmap, 0, nil, LPARAM(ButtonCaptionA), 0, XDestText, YDestText, 0, 0, TextFlags);
|
Details := ThemeServices.GetElementDetails(StateToDetail[AState]);
|
||||||
|
FillChar(Options, SizeOf(Options), 0);
|
||||||
|
Options.dwSize := SizeOf(Options);
|
||||||
|
Options.dwFlags := DTT_COMPOSITED;
|
||||||
|
if AState <> bsDisabled then
|
||||||
|
begin
|
||||||
|
// change color to requested or it will be black
|
||||||
|
Options.crText := ThemeServices.ColorToRGB(BitBtn.Font.Color, @Details);
|
||||||
|
Options.crShadow := Options.crText;
|
||||||
|
Options.dwFlags := Options.dwFlags or DTT_TEXTCOLOR;
|
||||||
|
end;
|
||||||
|
TWin32ThemeServices(ThemeServices).DrawTextEx(TmpDC, Details, ButtonCaption,
|
||||||
|
Rect(XDestText, YDestText, XDestText + TextSize.cx, YDestText + TextSize.cy),
|
||||||
|
DT_SINGLELINE, @Options);
|
||||||
end;
|
end;
|
||||||
{$ELSE}
|
|
||||||
DrawState(hdcNewBitmap, 0, nil, LPARAM(ButtonCaption), 0, XDestText, YDestText, 0, 0, TextFlags);
|
|
||||||
{$ENDIF}
|
|
||||||
SelectObject(hdcNewBitmap, OldBitmapHandle);
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
SelectObject(TmpDC, OldFontHandle);
|
||||||
|
if PaintBuffer <> 0 then
|
||||||
|
EndBufferedPaint(PaintBuffer, True);
|
||||||
|
NewBitmap := SelectObject(hdcNewBitmap, OldBitmapHandle);
|
||||||
|
end;
|
||||||
|
var
|
||||||
|
RGBA: PRGBAQuad;
|
||||||
|
AlphaDraw: Boolean;
|
||||||
begin
|
begin
|
||||||
// gather info about bitbtn
|
// gather info about bitbtn
|
||||||
BitBtnHandle := BitBtn.Handle;
|
BitBtnHandle := BitBtn.Handle;
|
||||||
@ -248,7 +316,6 @@ begin
|
|||||||
BitBtnLayout := BitBtn.Layout;
|
BitBtnLayout := BitBtn.Layout;
|
||||||
BitBtnDC := GetDC(BitBtnHandle);
|
BitBtnDC := GetDC(BitBtnHandle);
|
||||||
hdcNewBitmap := CreateCompatibleDC(BitBtnDC);
|
hdcNewBitmap := CreateCompatibleDC(BitBtnDC);
|
||||||
OldFontHandle := SelectObject(hdcNewBitmap, BitBtn.Font.Reference.Handle);
|
|
||||||
MeasureText(BitBtn, ButtonCaption, TextSize.cx, TextSize.cy);
|
MeasureText(BitBtn, ButtonCaption, TextSize.cx, TextSize.cy);
|
||||||
// calculate size of new bitmap
|
// calculate size of new bitmap
|
||||||
case BitBtnLayout of
|
case BitBtnLayout of
|
||||||
@ -323,8 +390,14 @@ begin
|
|||||||
BitmapRect.top := 0;
|
BitmapRect.top := 0;
|
||||||
BitmapRect.right := newWidth;
|
BitmapRect.right := newWidth;
|
||||||
BitmapRect.bottom := newHeight;
|
BitmapRect.bottom := newHeight;
|
||||||
|
|
||||||
|
AlphaDraw := ThemeServices.ThemesEnabled and (BeginBufferedPaint <> nil);
|
||||||
|
|
||||||
if (newWidth = 0) or (newHeight = 0) then
|
if (newWidth = 0) or (newHeight = 0) then
|
||||||
NewBitmap := 0
|
NewBitmap := 0
|
||||||
|
else
|
||||||
|
if AlphaDraw then
|
||||||
|
NewBitmap := Create32BitHBitmap(BitBtnDC, newWidth, newHeight, RGBA)
|
||||||
else
|
else
|
||||||
NewBitmap := CreateCompatibleBitmap(BitBtnDC, newWidth, newHeight);
|
NewBitmap := CreateCompatibleBitmap(BitBtnDC, newWidth, newHeight);
|
||||||
|
|
||||||
@ -338,7 +411,10 @@ begin
|
|||||||
if NewBitmap <> 0 then
|
if NewBitmap <> 0 then
|
||||||
begin
|
begin
|
||||||
if ThemeServices.ThemesEnabled then
|
if ThemeServices.ThemesEnabled then
|
||||||
ButtonImageList.himl := ImageList_Create(newWidth, newHeight, ILC_COLORDDB or ILC_MASK, 5, 0)
|
if AlphaDraw then
|
||||||
|
ButtonImageList.himl := ImageList_Create(newWidth, newHeight, ILC_COLOR32, 5, 0)
|
||||||
|
else
|
||||||
|
ButtonImageList.himl := ImageList_Create(newWidth, newHeight, ILC_COLORDDB or ILC_MASK, 5, 0)
|
||||||
else
|
else
|
||||||
ButtonImageList.himl := ImageList_Create(newWidth, newHeight, ILC_COLORDDB or ILC_MASK, 1, 0);
|
ButtonImageList.himl := ImageList_Create(newWidth, newHeight, ILC_COLORDDB or ILC_MASK, 1, 0);
|
||||||
ButtonImageList.margin.left := 5;
|
ButtonImageList.margin.left := 5;
|
||||||
@ -352,13 +428,16 @@ begin
|
|||||||
begin
|
begin
|
||||||
for I := 1 to 6 do
|
for I := 1 to 6 do
|
||||||
begin
|
begin
|
||||||
DrawBitmap(XPBitBtn_ImageIndexToState[I], True);
|
DrawBitmap(XPBitBtn_ImageIndexToState[I], True, AlphaDraw);
|
||||||
ImageList_AddMasked(ButtonImageList.himl, NewBitmap, GetSysColor(COLOR_BTNFACE));
|
if AlphaDraw then
|
||||||
|
ImageList_Add(ButtonImageList.himl, NewBitmap, 0)
|
||||||
|
else
|
||||||
|
ImageList_AddMasked(ButtonImageList.himl, NewBitmap, GetSysColor(COLOR_BTNFACE));
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
DrawBitmap(BitBtnEnabledToButtonState[IsWindowEnabled(BitBtnHandle) or (csDesigning in BitBtn.ComponentState)], True);
|
DrawBitmap(BitBtnEnabledToButtonState[IsWindowEnabled(BitBtnHandle) or (csDesigning in BitBtn.ComponentState)], True, False);
|
||||||
ImageList_AddMasked(ButtonImageList.himl, NewBitmap, GetSysColor(COLOR_BTNFACE));
|
ImageList_AddMasked(ButtonImageList.himl, NewBitmap, GetSysColor(COLOR_BTNFACE));
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
@ -373,12 +452,11 @@ begin
|
|||||||
begin
|
begin
|
||||||
OldBitmap := HBITMAP(Windows.SendMessage(BitBtnHandle, BM_GETIMAGE, IMAGE_BITMAP, 0));
|
OldBitmap := HBITMAP(Windows.SendMessage(BitBtnHandle, BM_GETIMAGE, IMAGE_BITMAP, 0));
|
||||||
if NewBitmap <> 0 then
|
if NewBitmap <> 0 then
|
||||||
DrawBitmap(BitBtnEnabledToButtonState[IsWindowEnabled(BitBtnHandle) or (csDesigning in BitBtn.ComponentState)], False);
|
DrawBitmap(BitBtnEnabledToButtonState[IsWindowEnabled(BitBtnHandle) or (csDesigning in BitBtn.ComponentState)], False, False);
|
||||||
Windows.SendMessage(BitBtnHandle, BM_SETIMAGE, IMAGE_BITMAP, LPARAM(NewBitmap));
|
Windows.SendMessage(BitBtnHandle, BM_SETIMAGE, IMAGE_BITMAP, LPARAM(NewBitmap));
|
||||||
if OldBitmap <> 0 then
|
if OldBitmap <> 0 then
|
||||||
DeleteObject(OldBitmap);
|
DeleteObject(OldBitmap);
|
||||||
end;
|
end;
|
||||||
SelectObject(hdcNewBitmap, OldFontHandle);
|
|
||||||
DeleteDC(hdcNewBitmap);
|
DeleteDC(hdcNewBitmap);
|
||||||
ReleaseDC(BitBtnHandle, BitBtnDC);
|
ReleaseDC(BitBtnHandle, BitBtnDC);
|
||||||
BitBtn.Invalidate;
|
BitBtn.Invalidate;
|
||||||
|
Loading…
Reference in New Issue
Block a user