unit PngBitBtn; {$I ..\Include\Thany.inc} interface uses Windows, Messages, SysUtils, Classes, Controls, StdCtrls, Buttons, Graphics, ImgList, ActnList, PngImageList, PngFunctions, PngButtonFunctions, pngimage {$IFDEF ThemeSupport}, Themes{$ENDIF}; type TPngBitBtn = class(TBitBtn) private FPngImage: TPngObject; FPngOptions: TPngOptions; FCanvas: TCanvas; FLastKind: TBitBtnKind; FImageFromAction: Boolean; {$IFDEF ThemeSupport} FMouseInControl: Boolean; {$ENDIF} IsFocused: Boolean; function PngImageStored: Boolean; procedure SetPngImage(const Value: TPngObject); procedure SetPngOptions(const Value: TPngOptions); procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM; procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER; procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE; protected procedure ActionChange(Sender: TObject; CheckDefaults: Boolean); override; procedure SetButtonStyle(ADefault: Boolean); override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; published property PngImage: TPngObject read FPngImage write SetPngImage stored PngImageStored; property PngOptions: TPngOptions read FPngOptions write SetPngOptions default [pngBlendOnDisabled]; property Glyph stored False; property NumGlyphs stored False; end; implementation { TPngBitBtn } constructor TPngBitBtn.Create(AOwner: TComponent); begin inherited Create(AOwner); FPngImage := TPNGObject.Create; FPngOptions := [pngBlendOnDisabled]; FCanvas := TCanvas.Create; FLastKind := bkCustom; FImageFromAction := False; end; destructor TPngBitBtn.Destroy; begin FPngImage.Free; FCanvas.Free; inherited Destroy; end; procedure TPngBitBtn.ActionChange(Sender: TObject; CheckDefaults: Boolean); begin inherited ActionChange(Sender, CheckDefaults); if Sender is TCustomAction then with TCustomAction(Sender) do begin //Copy image from action's imagelist if (PngImage.Empty or FImageFromAction) and (ActionList <> nil) and (ActionList.Images <> nil) and (ImageIndex >= 0) and (ImageIndex < ActionList.Images.Count) then begin CopyImageFromImageList(FPngImage, ActionList.Images, ImageIndex); FImageFromAction := True; end; end; end; procedure TPngBitBtn.SetButtonStyle(ADefault: Boolean); begin inherited SetButtonStyle(ADefault); if ADefault <> IsFocused then begin IsFocused := ADefault; Refresh; end; end; function TPngBitBtn.PngImageStored: Boolean; begin Result := not FImageFromAction; end; procedure TPngBitBtn.SetPngImage(const Value: TPngObject); begin //This is all neccesary, because you can't assign a nil to a TPNGObject if Value = nil then begin FPngImage.Free; FPngImage := TPNGObject.Create; end else FPngImage.Assign(Value); //To work around the gamma-problem with FPngImage do if not Empty and (Header.ColorType in [COLOR_RGB, COLOR_RGBALPHA, COLOR_PALETTE]) then Chunks.RemoveChunk(Chunks.ItemFromClass(TChunkgAMA)); FImageFromAction := False; Repaint; end; procedure TPngBitBtn.SetPngOptions(const Value: TPngOptions); begin if FPngOptions <> Value then begin FPngOptions := Value; Repaint; end; end; procedure TPngBitBtn.CNDrawItem(var Message: TWMDrawItem); var R, PaintRect: TRect; GlyphPos, TextPos: TPoint; IsDown, IsDefault: Boolean; Flags: Cardinal; {$IFDEF ThemeSupport} Button: TThemedButton; Details: TThemedElementDetails; {$ENDIF} begin R := ClientRect; FCanvas.Handle := Message.DrawItemStruct^.hDC; FCanvas.Font := Self.Font; IsDown := Message.DrawItemStruct^.itemState and ODS_SELECTED <> 0; IsDefault := Message.DrawItemStruct^.itemState and ODS_FOCUS <> 0; //Draw the border if {$IFDEF ThemeSupport}ThemeServices.ThemesEnabled{$ELSE}False{$ENDIF} then begin {$IFDEF ThemeSupport} //Themed border if not Enabled then Button := tbPushButtonDisabled else if IsDown then Button := tbPushButtonPressed else if FMouseInControl then Button := tbPushButtonHot else if IsFocused or IsDefault then Button := tbPushButtonDefaulted else Button := tbPushButtonNormal; //Paint the background, border, and finally get the inner rect Details := ThemeServices.GetElementDetails(Button); ThemeServices.DrawParentBackground(Handle, Message.DrawItemStruct.hDC, @Details, True); ThemeServices.DrawElement(Message.DrawItemStruct.hDC, Details, Message.DrawItemStruct.rcItem); R := ThemeServices.ContentRect(FCanvas.Handle, Details, Message.DrawItemStruct.rcItem); {$ENDIF} end else begin //Draw the outer border, when focused if IsFocused or IsDefault then begin FCanvas.Pen.Color := clWindowFrame; FCanvas.Pen.Width := 1; FCanvas.Brush.Style := bsClear; FCanvas.Rectangle(R.Left, R.Top, R.Right, R.Bottom); InflateRect(R, -1, -1); end; //Draw the inner border if IsDown then begin FCanvas.Pen.Color := clBtnShadow; FCanvas.Pen.Width := 1; FCanvas.Brush.Color := clBtnFace; FCanvas.Rectangle(R.Left, R.Top, R.Right, R.Bottom); InflateRect(R, -1, -1); end else begin Flags := DFCS_BUTTONPUSH or DFCS_ADJUSTRECT; if Message.DrawItemStruct.itemState and ODS_DISABLED <> 0 then Flags := Flags or DFCS_INACTIVE; DrawFrameControl(Message.DrawItemStruct.hDC, R, DFC_BUTTON, Flags); end; //Adjust the rect when focused and/or down if IsFocused then begin R := ClientRect; InflateRect(R, -1, -1); end; if IsDown then OffsetRect(R, 1, 1); end; //Calculate the position of the PNG glyph CalcButtonLayout(FCanvas, FPngImage, ClientRect, IsDown, False, Caption, Layout, Margin, Spacing, GlyphPos, TextPos, DrawTextBiDiModeFlags(0)); //Draw the image if (FPngImage <> nil) and (Kind = bkCustom) and not FPngImage.Empty then begin PaintRect := Rect(GlyphPos.X, GlyphPos.Y, GlyphPos.X + FPngImage.Width, GlyphPos.Y + FPngImage.Height); if Enabled then DrawPNG(FPngImage, FCanvas, PaintRect, []) else DrawPNG(FPngImage, FCanvas, PaintRect, FPngOptions); end; //Draw the text if Length(Caption) > 0 then begin PaintRect := Rect(TextPos.X, TextPos.Y, Width, Height); FCanvas.Brush.Style := bsClear; DrawText(FCanvas.Handle, PChar(Caption), -1, PaintRect, DrawTextBiDiModeFlags(0) or DT_TOP or DT_LEFT or DT_SINGLELINE); end; //Draw the focus rectangle if IsFocused and IsDefault then begin {$IFDEF ThemeSupport} if not ThemeServices.ThemesEnabled then begin R := ClientRect; InflateRect(R, -3, -3); end; {$ELSE} R := ClientRect; InflateRect(R, -3, -3); {$ENDIF} FCanvas.Pen.Color := clWindowFrame; FCanvas.Brush.Color := clBtnFace; DrawFocusRect(FCanvas.Handle, R); end; FLastKind := Kind; FCanvas.Handle := 0; end; procedure TPngBitBtn.CMMouseEnter(var Message: TMessage); begin {$IFDEF ThemeSupport} inherited; if ThemeServices.ThemesEnabled and not FMouseInControl and not (csDesigning in ComponentState) then begin FMouseInControl := True; Repaint; end; {$ENDIF} end; procedure TPngBitBtn.CMMouseLeave(var Message: TMessage); begin {$IFDEF ThemeSupport} inherited; if ThemeServices.ThemesEnabled and FMouseInControl then begin FMouseInControl := False; Repaint; end; {$ENDIF} end; end.