BitBtnEx (ビットボタンの拡張ボタン)

 TBitBtnを元に、カラーボタン、楕円ボタンを追加してみました。

楕円ボタンコンポーネントは、TGraphicControlを継承している為、タブストップが有りませんでしたが、BitBtnを元に、楕円ボタンを追加しているので、ここでの楕円ボタンには、タブストップ゛があります。
TBitBtnは、TCustomButtonを継承しています。

(注 Delphi XE3 の場合について記述しています。)

1.まず最初にRAD Studio の sourceホルダーの中の Vcl.Buttons.pas から、Bitbtnに関するものだけ、まず抜き出します。

Vcl.Buttons.pasには、TButton、TSpeedBtn、TBitbtnの三種類のボタンが入っているので、TBitbtnだけにする簡単な方法を記します。
1- 1.新規作成で、VCLアプリケーションを選択します。
1- 2.BitBtnを、Formに3個程度用意します。
1- 3.新しいホルダーを用意して、その中にプロジェクト、名前をつけて保存します。ユニットも同じホルダーに保存します。
1- 4.Vcl.Buttons.pasのコピーをプロジェクトと同じホルダーにコピーして、Buttons.pasにします。
1- 5.uses のVcl.ButtonsをButtonsに変更します。
1- 6.Buttons.pasを開きます。 (プロジェクトを開いたまま、Buttons.pasも開きます、Buttons.pasのタブが追加されます。)
1- 7.Form上のBitBtnの1つにに、画像を追加します。又、他のBitBtnの1つのKindプロパティで、bkCustome以外のものを指定します。
1- 8.Buttons.pasのタブをクリックして、Buttons.pasを表示します。
1- 9.デバッグモードで、ビルドすると、開いているButtons.pasに、デバッグ用ブレークアイコンが付加されるので、使用されていないルーチンを消去します。

 もし、間違って必要な部分を消去してしまった場合は、直ぐに元へ戻しましょう。
 少し消去しては、ビルドをしながら、消していくと問題なくBitBtnの必要な部分だけを残すことが出来、これで、準備完了です。
 これは、他のコンポーネントにも応用が出来ます。

2.TBitBtnEXパッケージ作成

2- 1.BitBtnの部分だけになったButtons.pasの中の"TBitBtn"を"TBitBtnEX"に、検索の置換を使用して全て置き換えます。
2- 2.名前を付けて保存で、新しいホルダーを作成し、ホルダー名を"BitbtnEx"、"BitbtnExPak"等の分かり易いホルダー名にしてその中に "BitbtnEx.pas"のファイル名で保存し閉じます。
2- 3.新規作成でパッケージを作成、プロジェクトに追加で、先ほど保存した"BitbtnEx.pas"を追加します。
2- 4.プロジェクトに名前を付けて保存で、"TBitBtnEx.dproj"の名前で先ほど作成したホルダーに保存します。
2- 5.リソースファイルの指定場所を変更します。(プロジェクトマネージャーの"TBitBtnEx..bpl"を選択、マウス右ボタン、ソースの表示を選択で、dpkファイルをひらきます。)
        BitbtnEx.pasの中の、"{$R Buttons.res}"を探し出し、package TBitBtnEx;へ移動します。

(注) ホルダーの場所は、"C:\Users\Public\Documents\RAD Studio\10.0\MyVcl\BitbtnEx" の様にします。

package TBitBtnEx;

{$R *.res}
{$R Buttons.res}      <- 此処へ移動
{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users}
{$ALIGN 8}
{$ASSERTIONS ON}

2- 6.BitbtnEx.pas 170行目近辺の "BitBtnCaptions: array[TBitBtnEXKind] of Pointer = (
          nil, @SOKButton, @SCancelButton, @SHelpButton, ....
        のPointerが取得できなくなりますので、PCharにして直接 指定します。

  BitBtnExCaptions: array[TBitBtnEXKind] of PChar = (            // pointer から PCharに変更
    nil, 'OK', 'キャンセル', 'ヘルプ(&H)', 'はい(&Y)', 'いいえ(&N)',
    '閉じる(&C)', '中止', '再試行(&R)', '無視(&I)',
    'すべて(&A)');

2- 7.コンポーネントの登録指定を追加します。

procedure Register
;          <-  追加

implementation

uses
Vcl.Consts, Vcl.ActnList, Winapi.UxTheme, Winapi.DwmApi,
System.Math;

// コンポーネントの登録指定
procedure Register;     <-  追加
begin
  RegisterComponents('MyVCL', [TBitBtnEX]);
end;

2- 8.BitBtnExホルダーにツール、オプションでライブラリのパスを追加します。

以上で、準備完了です、とりあえずインストールしてみましょう。

警告が表示されますが、間違いがなければ、追加されたことのメッセージが表示されます。

"[dcc32 警告] BitbtnEx.pas(1): W1025 class constructor : この仕様はサポートされていません" の表示が気になる場合は、プロジェクトのオプション Delphiコンパイラ -C/C++ C/C++出力ファイルの生成 を DCUのみ生成 にします。
"[dcc32 警告] BitbtnEx.pas(32): W1000 シンボル 'TImageIndex' を使用することは推奨されていません : 'Use System.UITypes.TImageIndex'" の表示も気になる場合は、プロジェクトのオプション Delphiコンパイラ ヒントと警告 使用を推奨されていないシンボルを False にすれば表示されなくなります。


2- 9.TBitBtbEx.bplを保存閉じます。

3.TBitBtnExの確認

3- 1.新規VCLフォームアプリケーションで新しいプロジェクトの起動をします。
3- 2.ツールパレットの「MyVcl」 TBitBtnExをフォームに貼り付けます。
3- 3.BitBtnEx1プロパティの Kind 変更 Fontの色変更等をテストしてみて下さい。
  BitBtnと全く同じボタンとなります。
  BitBtnから未だ何も変更していないので当然です。

4.拡張部分の追加変更

 いよいよ、拡張部分の追加です。

4- 1.最初に、BitBtnExのボタンの種類プロパティKindで指定して表示される、画像の変更が出来る様にします。

 プロパティKindで表示している画像は、Buttons.resファイルの中に設定されているので、"C:\Program Files\Embarcadero\RAD Studio\10.0\lib\win32\release"ホルダーの中に入っているButtons.resファイルをBitBtnExフォルダーにコピーします。
コピーしたらファイルの名前を、BitBtnEx.resに変更します。
TBitBtnEx.dprojを開き、TBitBtnEx.bpl のソースの {$R 'Buttons.res'} を {$R 'BitbtnEx.res'}に変更します。

resファイルの画像の編集には、XN Resource Editor 3.0.0.1 [ja] が便利です、ダウンロードして利用しましょう。

リソースエディターは、.ツールパレットに表示されるアイコン用 dcr ファイルの編集にも使用出来ます。

4- 2.ツールパレットに表示されるアイコンの追加

 dcr ファイルを作る必要があります。
XN Resource Editorを起動 新規作成を実行 リソースの追加で Bitmapを選択します。
TBItBtnExBitmapdcrBitmap
上図の内容が表示されたら、Bitmapの下の 1 を TBITBTNEX に変更します。
更に、Width 及び Height を 24 に pixcelFormat を4ビットに設定し、アイコン用画像の編集をします。
編集が終了したら、'BitBtnEx.dcr' のファイル名で、BitBtnExフォルダーへ保存終了します。
TBitBtnEx.dprojを開き、TBitBtnEx.bpl のソースに {$R 'BitBtnEx.dcr'} を追加します。

package TBitBtnEx;

{$R *.res}
{$R 'BitbtnEx.res'}
{$R 'BitbtnEx.dcr'}        <-- 追加

TBitBtn.bpl のアンインストール実行後、再インストールします、これで、新しいアイコンが表示されます。
dcrファイルの作成は"Resource Editor"を使用しなくても作成出来ます、その方法はインターネットで検索してください。
又は、コンポーネント用アイコンの作り方 (2005 以降)を参照してください。

4- 3.プロパティの追加、変更、新しいボタン描画部分の追加変更
    BitBtnEXに必要な新しい property を追加、又は、変更します。

    property BevelWidth: TBevelWidth read FBevelWidth write SetBevelWidth; // BevelWidth設定用追加
    property Color: TColor read FColor write SetColor;                               // OvalButtonの色設定用追加

4- 3-1.Type TButtonStyleの変更
   TButtonStyle = (bsAutoDetect, bsWin31, bsNew);  となっていますが、BitBtnでは、Style を指定しても、無効で何も変りません。
  function DrawButtonFace(Canvas: TCanvas; const Client: TRect;BevelWidth: Integer; Style: TButtonStyle; IsRounded, IsDown,IsFocused: Boolean): TRect;
 で使用されいのてすが、どこからも呼び出されることはありません。
  Delphi 7 で既に使用されなくなっていましたので、何時ごろから使用されなくなったかは不明です。
 それで、これを
 TButtonStyle = (bsStandard, bsOvalDraw, BsColorDRaw);に変更して、楕円ボタン、カラーボタンの指定が出来る様にします。

4- 3-2.
本ホームページでダウンロード出来る、楕円ボタンコンポーネントのソースファイルから、楕円ボタン描画部分を追加します。
    function HLS2RGB(const HLS: TColorVector): TColorVector; // 影部色設定追加
    function
RGB2HLS(const RGB: TColorVector): TColorVector; // 影部色色係数追加
    procedure
Get3DColors(FColor: TColor; var HighLightColor, ShadowColor: TColor; HLFactor, ShFactor: single);   // BevelColorの明暗色設定 追加
    procedure
PaintBorder(FState: Integer);                  // Bevel部描画追加

4- 3-3.procedure
TBitBtnEX.DrawItem に、FStyle の bsOvalDraw, BsColorDRaw を判別して、ボタンの描画するルーチンを追加します。
  if FStyle = BsOvalDRaw then begin // OvalButton指定時の描画部追加 テーマ、非テーマにかかわらず同じボタンが描画されます。
    FCanvas.Pen.Style := psSolid;
           ・
           ・                                      此処には楕円形ボタンの作図が入ります。
           ・
    FCanvas.Ellipse(R.Left + 1, R.Top + 1, R.Right - 1, R.Bottom - 1); // 一番外側のフレーム枠描画
  end
// **************************************************************************
  else begin
    if ThemeControl(Self) then                               テーマの場合
    begin
           ・
           ・
      LStyle.DrawElement(DrawItemStruct.hDC, Details, DrawItemStruct.rcItem);    ボタンの基本部が描画されます。
// **************************************
      if FStyle = BsColorDRaw then begin // BsColorDRaw追加 buttonの中を指定色で再描画 追加  
        R := ClientRect;
           ・                                             ここでボタンの内側を新しく追加した指定色で塗りつぶします。
           ・                                               clBtnFaceで塗りつぶされた場所を指定色で再描画します。
      end;
// **************************************
      LStyle.GetElementContentRect(FCanvas.Handle, Details, DrawItemStruct.rcItem, R);
           ・
           ・   
   end

    else                                            クラシック表示の場合
    begin
      Flags := DFCS_BUTTONPUSH or DFCS_ADJUSTRECT;
           ・
           ・
        if FStyle = BsColorDRaw then // BsColorDRaw追加 buttonの中を指定色で再描画 追加     カラーボタンだったら指定色
          FCanvas.Brush.Color := FColor
        else
          FCanvas.Brush.Color := clBtnFace;
           ・
           ・
        if FStyle = BsColorDRaw then begin // BsColorDRaw追加 buttonの中を指定色で再描画 追加 カラーボタンだったら指定色
          FCanvas.Pen.Style := PsClear;
          FCanvas.Brush.Color := FColor;
          FCanvas.Rectangle(R.Left, R.Top, R.Right, R.Bottom);
        end;
           ・
           ・
      TButtonGlyph(FGlyph).Draw(FCanvas, R, Point(0,0), Caption, FLayout, FMargin,
        FSpacing, State, True, DrawTextBiDiModeFlags(0) or WordBreakFlag[WordWrap]); // TransparentをTrueに設定 Trueにすることで、画像のバックグラウンド色をボタンの色にします。
           ・
           ・
  FCanvas.Handle := 0;
end;

4- 3-4.マウスに応答する範囲の設定の追加
  楕円ボタンの場合、マウスカーソルに応答する範囲も楕円形に変更します。
 ボタン生成時                procedure CreateWnd; override;
 ボタンサイズ変更時   procedure Resize; override;

procedure TBitBtnEX.CreateWnd; // Create時SetRegionの追加
begin
  inherited CreateWnd;
  SetRegion;
end;

procedure TBitBtnEX.Resize; // ResizeのSetRegionの追加
begin
  inherited Resize;
  SetRegion;
//  if assigned(FOnResize) then // BitBtnにOnResizeなし
//  FOnResize(Self);
end;

procedure TBitBtnEX.SetRegion; // 楕円形 又は 四角形のRegionの設定追加
var region: HRGN;
begin
  if FStyle = BsOvalDRaw then begin // 楕円形の場合 Regionの設定変更
    Region := CreateEllipticRgn(0, 0 , width + 1, height + 1); // 楕円形の指定;
//  else // 四角形は設定されているので不要
//    Region := CreateRoundRectRgn (0, 0, Width + 1, Height + 1, 2, 2); // 四角形の指定
    SetWindowRgn(Handle, Region, True);
    DeleteObject(Region);
  end;
end;

その他、細かい部分の修正は、BitBtnEx.pas のリストを参照して下さい。

5.TBitBtnExの実行結果

ボタン例 左側が通常のBitBtnで、右側がBitBtnExの実行結果です。

実行画面は、Windows7の場合です。

BitBtnExで、BitBtnと同じ表示は全て可能で、楕円とカラーボタンが拡張として追加されています。



楕円ボタンの動作 楕円ボタンのマウスカーソルに対する、応答、状態の表示描画です。

Enabled イネーブル状態

MouseEnter マウスカーソルが、ボタンの上に来たら、Bevelの表示
クラシック表示では、MouseEnterモードはありません。

Down マウス左ボタンを押した状態

Focused フォーカスがある状態で、フォーカス点線表示

Disable デスエブル表示、文字グレイ、ディスエブル画像




 古い、テーマモードの無いクラシック画面表示の確認をする必要がある場合、Windows8以降では、クラシックモードが無いので、互換モードを使用して確認を行います。
互換モードを Windows Meにして、8ビットカラー表示にすれば、クラシック表示(非テーマ)の確認ができます。
互換モードをWindows7にしても、ボタンのデザインはWindows7にはならないようです。
新しいソフトを開発する場合は、基本的にはWindowsXP以前のWindowsを使用することは無いので、そろそろクラシック表示は不要なのかもしれません。

互換モード Windows Me 8ビットカラー表示

下図左側がWindowsMe互換モードで、右側がWindows8.1の通常モードです。
楕円のボタンは、Windowsテーマと無関係なので、OSのバージョンには左右されません。

クラシック表示Win8テーマ

function DrawButtonFaceを利用して、参考表示すると、Win3.1タイプは次の様になります。
下図左側のボタンが、昔懐かしいWin3.1のボタンです。(べベル幅2に設定)
Win3.1



BitbtnEx.pas ソースリスト

 unit BitbtnEx;

interface

uses Winapi.Windows, Winapi.Messages, System.Classes, System.Types, VCL.Controls, Vcl.Forms, Vcl.Graphics,
  Vcl.StdCtrls, Vcl.ExtCtrls, Winapi.CommCtrl, Vcl.ImgList,
  Vcl.Themes, System.Generics.Collections, SysUtils;

type
  TButtonLayout = (blGlyphLeft, blGlyphRight, blGlyphTop, blGlyphBottom);
  TButtonState = (bsUp, bsDisabled, bsDown, bsExclusive);
  TButtonStyle = (bsStandard, bsOvalDraw, BsColorDRaw); // ButtonStyleのbsOvalDraw,BsColorDRaw追加変更
  TNumGlyphs = 1..4;

  TColorVector = record          // bsOvalButtonの色設定用追加
    case Integer of
      0: (Coord: array[0..2] of Double);
      1: (R, G, B: Double);
      2: (H, L, S: Double);
  end;

  EColorConv = class(Exception); // 色変換エラー用

  TBitBtnEX = class;             // Tbitbtn -> TBitBtnEX 全て変更

  TBitBtnEXActionLink = class(TControlActionLink)
  protected
    FClient: TBitBtnEX;
    FImageIndex: Integer;
    procedure AssignClient(AClient: TObject); override;
    function IsImageIndexLinked: Boolean; override;
    function IsGlyphLinked(Index: TImageIndex): Boolean;
    procedure SetImageIndex(Value: Integer); override;
  public
    constructor Create(AClient: TObject); override;
  end;

  TBitBtnEXKind = (bkCustom, bkOK, bkCancel, bkHelp, bkYes, bkNo, bkClose,
    bkAbort, bkRetry, bkIgnore, bkAll);

  TBitBtnEX = class(TCustomButton)
  strict private
    class constructor Create;
    class destructor Destroy;
  private
    FBevelWidth: TBevelWidth; // 縁枠の幅追加
    FColor,
    FColorHighlight,
    FColorShadow: TColor;     // OvalButtonの色追加
    FCanvas: TCanvas;
    FGlyph: TObject;
    FStyle: TButtonStyle;
    FKind: TBitBtnEXKind;
    FLayout: TButtonLayout;
    FSpacing: Integer;
    FMargin: Integer;
    IsFocused: Boolean;
    FModifiedGlyph: Boolean;
    FMouseInControl: Boolean;
//    FOnResize : TNotifyEvent;     // BitBtnにOnResizeなし
    function HLS2RGB(const HLS: TColorVector): TColorVector; // 影部色設定追加
    function RGB2HLS(const RGB: TColorVector): TColorVector; // 影部色色係数追加
    procedure Get3DColors(FaceColor: TColor; var HighLightColor, ShadowColor: TColor; HLFactor, ShFactor: single); // BevelColorの明暗色設定 追加
    procedure PaintBorder(FState: Integer);                  // Bevel部描画追加
    procedure DrawItem(const DrawItemStruct: TDrawItemStruct);
    procedure SetGlyph(Value: TBitmap);
    function GetGlyph: TBitmap;
    function GetNumGlyphs: TNumGlyphs;
    procedure SetNumGlyphs(Value: TNumGlyphs);
    procedure GlyphChanged(Sender: TObject);
    procedure InternalCopyImage(Image: TBitmap; ImageList: TCustomImageList; Index: Integer);
    function IsCustom: Boolean;
    function IsCustomCaption: Boolean;
    procedure SetStyle(Value: TButtonStyle);
    procedure SetKind(Value: TBitBtnEXKind);
    function GetKind: TBitBtnEXKind;
    procedure SetLayout(Value: TButtonLayout);
    procedure SetSpacing(Value: Integer);
    procedure SetMargin(Value: Integer);
    procedure CNMeasureItem(var Message: TWMMeasureItem); message CN_MEASUREITEM;
    procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM;
    procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
    procedure CMEnabledChanged(var Message: TMessage); message CM_ENABLEDCHANGED;
    procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
    procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
    procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message WM_LBUTTONDBLCLK;
    procedure SetRegion;                            // Oval Region設定追加
  protected
    procedure CreateWnd; override;                 // Create時Oval Region設定追加
    procedure SetBevelWidth(newValue: TBevelWidth); // OvalButtonのBevelWidth設定追加
    procedure SetColor(newColor: TColor);           // OvalButtonの色設定追加
    procedure ActionChange(Sender: TObject; CheckDefaults: Boolean); override;
    procedure CopyImage(ImageList: TCustomImageList; Index: Integer);
    procedure CreateHandle; override;
    procedure CreateParams(var Params: TCreateParams); override;
    function GetActionLinkClass: TControlActionLinkClass; override;
    function GetPalette: HPALETTE; override;
    procedure SetButtonStyle(ADefault: Boolean); override;
    procedure UpdateStyleElements; override;
    procedure Resize; override;                   // Resize時のOval Region再設定用に追加
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Click; override;
  published
    property BevelWidth: TBevelWidth read FBevelWidth write SetBevelWidth; // BevelWidth設定用追加
    property Color: TColor read FColor write SetColor;                     // OvalButtonの色設定用追加
    property Action;
    property Align;
    property Anchors;
    property BiDiMode;
    property Cancel stored IsCustom;
    property Caption stored IsCustomCaption;
    property Constraints;
    property Default stored IsCustom;
    property DoubleBuffered default True;
    property DragCursor;
    property DragKind;
    property DragMode;
    property Enabled;
    property Font;
    property Glyph: TBitmap read GetGlyph write SetGlyph stored IsCustom;
    property Kind: TBitBtnEXKind read GetKind write SetKind default bkCustom;
    property Layout: TButtonLayout read FLayout write SetLayout default blGlyphLeft;
    property Margin: Integer read FMargin write SetMargin default -1;
    property ModalResult stored IsCustom;
    property NumGlyphs: TNumGlyphs read GetNumGlyphs write SetNumGlyphs default 1;
    property ParentBiDiMode;
    property ParentDoubleBuffered default False;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property ShowHint;
    property Style: TButtonStyle read FStyle write SetStyle default bsStandard; //スタイルの設定変更
    property Spacing: Integer read FSpacing write SetSpacing default 4;
    property TabOrder;
    property TabStop;
    property Visible;
    property WordWrap;
    property StyleElements;
    property OnClick;
    property OnContextPopup;
    property OnDragDrop;
    property OnDragOver;
    property OnEndDock;
    property OnEndDrag;
    property OnEnter;
    property OnExit;
    property OnKeyDown;
    property OnKeyPress;
    property OnKeyUp;
    property OnMouseActivate;
    property OnMouseDown;
    property OnMouseEnter;
    property OnMouseLeave;
    property OnMouseMove;
    property OnMouseUp;
    property OnStartDock;
    property OnStartDrag;
//    property OnResize:TNotifyEvent read FOnResize write FOnResize;
  end;


  TBitBtnEXStyleHook = class(TButtonStyleHook)
  strict protected
    procedure DrawButton(ACanvas: TCanvas; AMouseInControl: Boolean); override;
  end;

procedure Register;

implementation

uses
  Vcl.Consts, Vcl.ActnList, Winapi.UxTheme, Winapi.DwmApi,
  System.Math;

// コンポーネントの登録
procedure Register;
begin
  RegisterComponents('MyVCL', [TBitBtnEX]);
end;

//{$R BitbtnEx.res} dpkファイルに移行

{ TBitBtnEX data }
var
  BitBtnExResNames: array[TBitBtnEXKind] of PChar = (
    nil, 'BBOK', 'BBCANCEL', 'BBHELP', 'BBYES', 'BBNO', 'BBCLOSE',
    'BBABORT', 'BBRETRY', 'BBIGNORE', 'BBALL');
  BitBtnExCaptions: array[TBitBtnEXKind] of PChar = (                  // pointer から PCharに変更
    nil, 'OK', 'キャンセル', 'ヘルプ(&H)', 'はい(&Y)', 'いいえ(&N)',
    '閉じる(&C)', '中止', '再試行(&R)', '無視(&I)',
    'すべて(&A)');
  BitBtnExModalResults: array[TBitBtnEXKind] of TModalResult = (
    0, mrOk, mrCancel, 0, mrYes, mrNo, 0, mrAbort, mrRetry, mrIgnore,
    mrAll);

  BitBtnExGlyphs: array[TBitBtnEXKind] of TBitmap;

const
  FHLContrast = 5; // Bevel明部輝度係数 追加
  FShContrast = 4; // Bevel影部輝度係数 追加

function GeTBitBtnEXGlyph(Kind: TBitBtnEXKind): TBitmap;
begin
  if BitBtnExGlyphs[Kind] = nil then
  begin
    BitBtnExGlyphs[Kind] := TBitmap.Create;
    BitBtnExGlyphs[Kind].LoadFromResourceName(HInstance, BitBtnExResNames[Kind]);
  end;
  Result := BitBtnExGlyphs[Kind];
end;

type
  TGlyphList = class(TImageList)
  private
    Used: TBits;
    FCount: Integer;
    function AllocateIndex: Integer;
  public
    constructor CreateSize(AWidth, AHeight: Integer);
    destructor Destroy; override;
    function AddMasked(Image: TBitmap; MaskColor: TColor): Integer;
    procedure Delete(Index: Integer);
    property Count: Integer read FCount;
  end;

  TGlyphCache = class
  private
    GlyphLists: TList;
  public
    constructor Create;
    destructor Destroy; override;
    function GetList(AWidth, AHeight: Integer): TGlyphList;
    procedure ReturnList(List: TGlyphList);
    function Empty: Boolean;
  end;

  TButtonGlyph = class
  private
    FOriginal: TBitmap;
    FGlyphList: TGlyphList;
    FIndexs: array[TButtonState] of Integer;
    FTransparentColor: TColor;
    FNumGlyphs: TNumGlyphs;
    FPaintOnGlass: Boolean;
    FThemeDetails: TThemedElementDetails;
    FThemesEnabled: Boolean;
    FOnChange: TNotifyEvent;
    FThemeTextColor: Boolean;
    procedure GlyphChanged(Sender: TObject);
    procedure SetGlyph(Value: TBitmap);
    procedure SetNumGlyphs(Value: TNumGlyphs);
    procedure Invalidate;
    function CreateButtonGlyph(State: TButtonState): Integer;
    procedure DrawButtonGlyph(Canvas: TCanvas; const GlyphPos: TPoint;
      State: TButtonState; Transparent: Boolean);
    procedure DrawButtonText(Canvas: TCanvas; const Caption: string;
      TextBounds: TRect; State: TButtonState; Flags: Longint);
    procedure CalcButtonLayout(Canvas: TCanvas; const Client: TRect;
      const Offset: TPoint; const Caption: string; Layout: TButtonLayout;
      Margin, Spacing: Integer; var GlyphPos: TPoint; var TextBounds: TRect;
      BiDiFlags: Longint);
  public
    constructor Create;
    destructor Destroy; override;
    { return the text rectangle }
    function Draw(Canvas: TCanvas; const Client: TRect; const Offset: TPoint;
      const Caption: string; Layout: TButtonLayout; Margin, Spacing: Integer;
      State: TButtonState; Transparent: Boolean; BiDiFlags: Longint): TRect;
    property Glyph: TBitmap read FOriginal write SetGlyph;
    property NumGlyphs: TNumGlyphs read FNumGlyphs write SetNumGlyphs;
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
  end;

{ TGlyphList }

constructor TGlyphList.CreateSize(AWidth, AHeight: Integer);
begin
  inherited CreateSize(AWidth, AHeight);
  Used := TBits.Create;
end;

destructor TGlyphList.Destroy;
begin
  Used.Free;
  inherited Destroy;
end;

function TGlyphList.AllocateIndex: Integer;
begin
  Result := Used.OpenBit;
  if Result >= Used.Size then
  begin
    Result := inherited Add(nil, nil);
    Used.Size := Result + 1;
  end;
  Used[Result] := True;
end;

function TGlyphList.AddMasked(Image: TBitmap; MaskColor: TColor): Integer;
begin
  Result := AllocateIndex;
  ReplaceMasked(Result, Image, MaskColor);
  Inc(FCount);
end;

procedure TGlyphList.Delete(Index: Integer);
begin
  if Used[Index] then
  begin
    Dec(FCount);
    Used[Index] := False;
  end;
end;

{ TGlyphCache }

constructor TGlyphCache.Create;
begin
  inherited Create;
  GlyphLists := TList.Create;
end;

destructor TGlyphCache.Destroy;
begin
  GlyphLists.Free;
  inherited Destroy;
end;

function TGlyphCache.GetList(AWidth, AHeight: Integer): TGlyphList;
var
  I: Integer;
begin
  for I := GlyphLists.Count - 1 downto 0 do
  begin
    Result := TGlyphList(GlyphLists[I]);
    with Result do
      if (AWidth = Width) and (AHeight = Height) then Exit;
  end;
  Result := TGlyphList.CreateSize(AWidth, AHeight);
  GlyphLists.Add(Result);
end;

procedure TGlyphCache.ReturnList(List: TGlyphList);
begin
  if List = nil then Exit;
  if List.Count = 0 then
  begin
    GlyphLists.Remove(List);
    List.Free;
  end;
end;

function TGlyphCache.Empty: Boolean;
begin
  Result := GlyphLists.Count = 0;
end;

var
  GlyphCache: TGlyphCache = nil;
  ButtonCount: Integer = 0;

{ TButtonGlyph }

constructor TButtonGlyph.Create;
var
  I: TButtonState;
begin
  inherited Create;
  FOriginal := TBitmap.Create;
  FOriginal.OnChange := GlyphChanged;
  FTransparentColor := clOlive;
  FNumGlyphs := 1;
  FPaintOnGlass := False;
  FThemesEnabled := False;
  FThemeTextColor := True;
  for I := Low(I) to High(I) do
    FIndexs[I] := -1;
  if GlyphCache = nil then GlyphCache := TGlyphCache.Create;
end;

destructor TButtonGlyph.Destroy;
begin
  FOriginal.Free;
  Invalidate;
  if Assigned(GlyphCache) and GlyphCache.Empty then
  begin
    GlyphCache.Free;
    GlyphCache := nil;
  end;
  inherited Destroy;
end;

procedure TButtonGlyph.Invalidate;
var
  I: TButtonState;
begin
  for I := Low(I) to High(I) do
  begin
    if FIndexs[I] <> -1 then FGlyphList.Delete(FIndexs[I]);
    FIndexs[I] := -1;
  end;
  GlyphCache.ReturnList(FGlyphList);
  FGlyphList := nil;
end;

procedure TButtonGlyph.GlyphChanged(Sender: TObject);
begin
  if Sender = FOriginal then
  begin
    FTransparentColor := FOriginal.TransparentColor;
    Invalidate;
    if Assigned(FOnChange) then FOnChange(Self);
  end;
end;

procedure TButtonGlyph.SetGlyph(Value: TBitmap);
var
  Glyphs: Integer;
begin
  Invalidate;
  FOriginal.Assign(Value);
  if (Value <> nil) and (Value.Height > 0) then
  begin
    FTransparentColor := Value.TransparentColor;
    if Value.Width mod Value.Height = 0 then
    begin
      Glyphs := Value.Width div Value.Height;
      if Glyphs > 4 then Glyphs := 1;
      SetNumGlyphs(Glyphs);
    end;
  end;
end;

procedure TButtonGlyph.SetNumGlyphs(Value: TNumGlyphs);
begin
  if (Value <> FNumGlyphs) and (Value > 0) then
  begin
    Invalidate;
    FNumGlyphs := Value;
    GlyphChanged(Glyph);
  end;
end;

const
  ROP_DSPDxax = $00E20746;

function TButtonGlyph.CreateButtonGlyph(State: TButtonState): Integer;
var
  TmpImage, DDB, MonoBmp: TBitmap;
  IWidth, IHeight: Integer;
  IRect, ORect: TRect;
  I: TButtonState;
  DestDC: HDC;
begin
  if (State = bsDown) and (NumGlyphs < 3) then State := bsUp;
  Result := FIndexs[State];
  if Result <> -1 then Exit;
  if (FOriginal.Width or FOriginal.Height) = 0 then Exit;
  IWidth := FOriginal.Width div FNumGlyphs;
  IHeight := FOriginal.Height;
  if FGlyphList = nil then
  begin
    if GlyphCache = nil then GlyphCache := TGlyphCache.Create;
    FGlyphList := GlyphCache.GetList(IWidth, IHeight);
  end;
  TmpImage := TBitmap.Create;
  try
    TmpImage.Width := IWidth;
    TmpImage.Height := IHeight;
    IRect := Rect(0, 0, IWidth, IHeight);
    TmpImage.Canvas.Brush.Color := clBtnFace;
    TmpImage.Palette := CopyPalette(FOriginal.Palette);
    I := State;
    if Ord(I) >= NumGlyphs then I := bsUp;
    ORect := Rect(Ord(I) * IWidth, 0, (Ord(I) + 1) * IWidth, IHeight);
    case State of
      bsUp, bsDown,
      bsExclusive:
        begin
          TmpImage.Canvas.CopyRect(IRect, FOriginal.Canvas, ORect);
          if FOriginal.TransparentMode = tmFixed then
            FIndexs[State] := FGlyphList.AddMasked(TmpImage, FTransparentColor)
          else
            FIndexs[State] := FGlyphList.AddMasked(TmpImage, clDefault);
        end;
      bsDisabled:
        begin
          MonoBmp := nil;
          DDB := nil;
          try
            MonoBmp := TBitmap.Create;
            DDB := TBitmap.Create;
            DDB.Assign(FOriginal);
            DDB.HandleType := bmDDB;
            if NumGlyphs > 1 then
            with TmpImage.Canvas do
            begin { Change white & gray to clBtnHighlight and clBtnShadow }
              CopyRect(IRect, DDB.Canvas, ORect);
              MonoBmp.Monochrome := True;
              MonoBmp.Width := IWidth;
              MonoBmp.Height := IHeight;

              { Convert white to clBtnHighlight }
              DDB.Canvas.Brush.Color := clWhite;
              MonoBmp.Canvas.CopyRect(IRect, DDB.Canvas, ORect);
              Brush.Color := clBtnHighlight;
              DestDC := Handle;
              SetTextColor(DestDC, clBlack);
              SetBkColor(DestDC, clWhite);
              BitBlt(DestDC, 0, 0, IWidth, IHeight,
                MonoBmp.Canvas.Handle, 0, 0, ROP_DSPDxax);

              { Convert gray to clBtnShadow }
              DDB.Canvas.Brush.Color := clGray;
              MonoBmp.Canvas.CopyRect(IRect, DDB.Canvas, ORect);
              Brush.Color := clBtnShadow;
              DestDC := Handle;
              SetTextColor(DestDC, clBlack);
              SetBkColor(DestDC, clWhite);
              BitBlt(DestDC, 0, 0, IWidth, IHeight,
              MonoBmp.Canvas.Handle, 0, 0, ROP_DSPDxax);

              { Convert transparent color to clBtnFace }
              DDB.Canvas.Brush.Color := ColorToRGB(FTransparentColor);
              MonoBmp.Canvas.CopyRect(IRect, DDB.Canvas, ORect);
              Brush.Color := clBtnFace;
              DestDC := Handle;
              SetTextColor(DestDC, clBlack);
              SetBkColor(DestDC, clWhite);
              BitBlt(DestDC, 0, 0, IWidth, IHeight,
              MonoBmp.Canvas.Handle, 0, 0, ROP_DSPDxax);
            end
            else
            begin
              { Create a disabled version }
              with MonoBmp do
              begin
                Assign(FOriginal);
                HandleType := bmDDB;
                Canvas.Brush.Color := clBlack;
                Width := IWidth;
                if Monochrome then
                begin
                  Canvas.Font.Color := clWhite;
                  Monochrome := False;
                  Canvas.Brush.Color := clWhite;
                end;
                Monochrome := True;
              end;
              with TmpImage.Canvas do
              begin
                Brush.Color := clBtnFace;
                FillRect(IRect);
                Brush.Color := clBtnHighlight;
                SetTextColor(Handle, clBlack);
                SetBkColor(Handle, clWhite);
                BitBlt(Handle, 1, 1, IWidth, IHeight,
                MonoBmp.Canvas.Handle, 0, 0, ROP_DSPDxax);
                Brush.Color := clBtnShadow;
                SetTextColor(Handle, clBlack);
                SetBkColor(Handle, clWhite);
                BitBlt(Handle, 0, 0, IWidth, IHeight,
                  MonoBmp.Canvas.Handle, 0, 0, ROP_DSPDxax);
              end;
            end;
          finally
            DDB.Free;
            MonoBmp.Free;
          end;
          FIndexs[State] := FGlyphList.AddMasked(TmpImage, clDefault);
        end;
    end;
  finally
    TmpImage.Free;
  end;
    Result := FIndexs[State];
    FOriginal.Dormant;
end;

procedure TButtonGlyph.DrawButtonGlyph(Canvas: TCanvas; const GlyphPos: TPoint;
  State: TButtonState; Transparent: Boolean);
var
  Index: Integer;
  Details: TThemedElementDetails;
  R: TRect;
  Button: TThemedButton;
  MemDC: HDC;
  PaintBuffer: HPAINTBUFFER;
begin
  if FOriginal = nil then Exit;
  if (FOriginal.Width = 0) or (FOriginal.Height = 0) then Exit;
  Index := CreateButtonGlyph(State);
  with GlyphPos do
  begin
    if FThemesEnabled then
    begin
      R.Left := GlyphPos.X;
      R.Top := GlyphPos.Y;
      R.Right := R.Left + FOriginal.Width div FNumGlyphs;
      R.Bottom := R.Top + FOriginal.Height;
      case State of
        bsDisabled:
          Button := tbPushButtonDisabled;
        bsDown,
        bsExclusive:
          Button := tbPushButtonPressed;
      else
        // bsUp
        Button := tbPushButtonNormal;
      end;
      Details := StyleServices.GetElementDetails(Button);

      if FPaintOnGlass then
      begin
        PaintBuffer := BeginBufferedPaint(Canvas.Handle, R, BPBF_TOPDOWNDIB, nil, MemDC);
        try
          StyleServices.DrawIcon(MemDC, Details, R, FGlyphList.Handle, Index);
          BufferedPaintMakeOpaque(PaintBuffer, R);
        finally
          EndBufferedPaint(PaintBuffer, True);
        end;
      end
      else
        StyleServices.DrawIcon(Canvas.Handle, Details, R, FGlyphList.Handle, Index);
    end
    else
      if Transparent or (State = bsExclusive) then
      begin
        ImageList_DrawEx(FGlyphList.Handle, Index, Canvas.Handle, X, Y, 0, 0,
          clNone, clNone, ILD_Transparent)
      end
      else
        ImageList_DrawEx(FGlyphList.Handle, Index, Canvas.Handle, X, Y, 0, 0,
          ColorToRGB(clBtnFace), clNone, ILD_Normal);
  end;
end;

procedure TButtonGlyph.DrawButtonText(Canvas: TCanvas; const Caption: string;
  TextBounds: TRect; State: TButtonState; Flags: LongInt);

  procedure DoDrawText(DC: HDC; const Text: UnicodeString;
    var TextRect: TRect; TextFlags: Cardinal);
  var
    LColor: TColor;
    LFormats: TTextFormat;
  begin
    if FThemesEnabled then
    begin
      if (State = bsDisabled) or (not StyleServices.IsSystemStyle and FThemeTextColor) then
      begin
        if not StyleServices.GetElementColor(FThemeDetails, ecTextColor, LColor) or (LColor = clNone) then
          LColor := Canvas.Font.Color;
      end
      else
        LColor := Canvas.Font.Color;

      LFormats := TTextFormatFlags(TextFlags);
      if FPaintOnGlass then
        Include(LFormats, tfComposited);
      StyleServices.DrawText(DC, FThemeDetails, Text, TextRect, LFormats, LColor);
    end
    else
      Winapi.Windows.DrawText(DC, Text, Length(Text), TextRect, TextFlags);
  end;

begin
  with Canvas do
  begin
    Brush.Style := bsClear;
    if (State = bsDisabled) and not FThemesEnabled then
    begin
      OffsetRect(TextBounds, 1, 1);
      Font.Color := clBtnHighlight;
      DoDrawText(Handle, Caption, TextBounds, DT_NOCLIP or DT_CENTER or DT_VCENTER or Flags);
      OffsetRect(TextBounds, -1, -1);
      Font.Color := clBtnShadow;
      DoDrawText(Handle, Caption, TextBounds, DT_NOCLIP or DT_CENTER or DT_VCENTER or Flags);
    end
    else
      DoDrawText(Handle, Caption, TextBounds, DT_NOCLIP or DT_CENTER or DT_VCENTER or Flags);
  end;
end;

procedure TButtonGlyph.CalcButtonLayout(Canvas: TCanvas; const Client: TRect;
  const Offset: TPoint; const Caption: string; Layout: TButtonLayout; Margin,
  Spacing: Integer; var GlyphPos: TPoint; var TextBounds: TRect;
  BiDiFlags: LongInt);
var
  TextPos: TPoint;
  ClientSize, GlyphSize, TextSize: TPoint;
  TotalSize: TPoint;
begin
  if (BiDiFlags and DT_RIGHT) = DT_RIGHT then
    if Layout = blGlyphLeft then Layout := blGlyphRight
  else
    if Layout = blGlyphRight then Layout := blGlyphLeft;
  { calculate the item sizes }
  ClientSize := Point(Client.Right - Client.Left, Client.Bottom -
    Client.Top);

  if FOriginal <> nil then
    GlyphSize := Point(FOriginal.Width div FNumGlyphs, FOriginal.Height) else
  GlyphSize := Point(0, 0);

  if Length(Caption) > 0 then
  begin
    TextBounds := Rect(0, 0, Client.Right - Client.Left, 0);
    DrawText(Canvas.Handle, Caption, Length(Caption), TextBounds,
    DT_CALCRECT or BiDiFlags);
    TextSize := Point(TextBounds.Right - TextBounds.Left, TextBounds.Bottom -
    TextBounds.Top);
  end
  else
  begin
    TextBounds := Rect(0, 0, 0, 0);
    TextSize := Point(0,0);
  end;

  { If the layout has the glyph on the right or the left, then both the
    text and the glyph are centered vertically. If the glyph is on the top
    or the bottom, then both the text and the glyph are centered horizontally.}
  if Layout in [blGlyphLeft, blGlyphRight] then
  begin
    GlyphPos.Y := (ClientSize.Y - GlyphSize.Y + 1) div 2;
    TextPos.Y := (ClientSize.Y - TextSize.Y + 1) div 2;
  end
  else
  begin
    GlyphPos.X := (ClientSize.X - GlyphSize.X + 1) div 2;
    TextPos.X := (ClientSize.X - TextSize.X + 1) div 2;
  end;

  { if there is no text or no bitmap, then Spacing is irrelevant }
  if (TextSize.X = 0) or (GlyphSize.X = 0) then
    Spacing := 0;

  { adjust Margin and Spacing }
  if Margin = -1 then
  begin
    if Spacing < 0 then
    begin
      TotalSize := Point(GlyphSize.X + TextSize.X, GlyphSize.Y + TextSize.Y);
      if Layout in [blGlyphLeft, blGlyphRight] then
        Margin := (ClientSize.X - TotalSize.X) div 3
      else
        Margin := (ClientSize.Y - TotalSize.Y) div 3;
      Spacing := Margin;
    end
    else
    begin
      TotalSize := Point(GlyphSize.X + Spacing + TextSize.X, GlyphSize.Y +
      Spacing + TextSize.Y);
      if Layout in [blGlyphLeft, blGlyphRight] then
        Margin := (ClientSize.X - TotalSize.X + 1) div 2
      else
        Margin := (ClientSize.Y - TotalSize.Y + 1) div 2;
    end;
  end
  else
  begin
    if Spacing < 0 then
    begin
      TotalSize := Point(ClientSize.X - (Margin + GlyphSize.X), ClientSize.Y -
        (Margin + GlyphSize.Y));
      if Layout in [blGlyphLeft, blGlyphRight] then
        Spacing := (TotalSize.X - TextSize.X) div 2
      else
        Spacing := (TotalSize.Y - TextSize.Y) div 2;
    end;
  end;

  case Layout of
    blGlyphLeft:
      begin
        GlyphPos.X := Margin;
        TextPos.X := GlyphPos.X + GlyphSize.X + Spacing;
      end;
    blGlyphRight:
      begin
        GlyphPos.X := ClientSize.X - Margin - GlyphSize.X;
        TextPos.X := GlyphPos.X - Spacing - TextSize.X;
      end;
    blGlyphTop:
      begin
        GlyphPos.Y := Margin;
        TextPos.Y := GlyphPos.Y + GlyphSize.Y + Spacing;
      end;
    blGlyphBottom:
      begin
        GlyphPos.Y := ClientSize.Y - Margin - GlyphSize.Y;
        TextPos.Y := GlyphPos.Y - Spacing - TextSize.Y;
      end;
  end;

  { fixup the result variables }
  Inc(GlyphPos.X, Client.Left + Offset.X);
  Inc(GlyphPos.Y, Client.Top + Offset.Y);

  OffsetRect(TextBounds, TextPos.X + Client.Left + Offset.X, TextPos.Y + Client.Top + Offset.Y);
end;

function TButtonGlyph.Draw(Canvas: TCanvas; const Client: TRect;
  const Offset: TPoint; const Caption: string; Layout: TButtonLayout;
  Margin, Spacing: Integer; State: TButtonState; Transparent: Boolean;
  BiDiFlags: LongInt): TRect;
var
  GlyphPos: TPoint;
begin
  CalcButtonLayout(Canvas, Client, Offset, Caption, Layout, Margin, Spacing,
  GlyphPos, Result, BiDiFlags);
  DrawButtonGlyph(Canvas, GlyphPos, State, Transparent);
  DrawButtonText(Canvas, Caption, Result, State, BiDiFlags);
end;


{ TBitBtnEX }

class constructor TBitBtnEX.Create;
begin
  TCustomStyleEngine.RegisterStyleHook(TBitBtnEX, TBitBtnEXStyleHook);
end;

class destructor TBitBtnEX.Destroy;
begin
  TCustomStyleEngine.UnRegisterStyleHook(TBitBtnEX, TBitBtnEXStyleHook);
end;


constructor TBitBtnEX.Create(AOwner: TComponent);
begin
  FGlyph := TButtonGlyph.Create;
  TButtonGlyph(FGlyph).OnChange := GlyphChanged;
  inherited Create(AOwner);
  FCanvas := TCanvas.Create;
  FStyle := bsStandard; // FStyleの値変更
  FColor := clBtnFace;  // Buttonの色初期値追加
  FBevelWidth := 1;     // BevelWidthの初期値追加
  FKind := bkCustom;
  FLayout := blGlyphLeft;
  FSpacing := 4;
  FMargin := -1;
  ControlStyle := ControlStyle + [csReflector, csPaintBlackOpaqueOnGlass];
  DoubleBuffered := True;
end;

destructor TBitBtnEX.Destroy;
begin
  inherited Destroy;
  TButtonGlyph(FGlyph).Free;
  FCanvas.Free;
end;

procedure TBitBtnEX.CreateHandle;
var
  State: TButtonState;
begin
  if Enabled then
    State := bsUp
  else
    State := bsDisabled;
  inherited CreateHandle;
  TButtonGlyph(FGlyph).CreateButtonGlyph(State);
end;

procedure TBitBtnEX.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  with Params do Style := Style or BS_OWNERDRAW;
end;

procedure TBitBtnEX.SetButtonStyle(ADefault: Boolean);
begin
  if ADefault <> IsFocused then
  begin
    IsFocused := ADefault;
    Refresh;
  end;
end;

procedure TBitBtnEX.CreateWnd; // Create時SetRegionの追加
begin
  inherited CreateWnd;
  SetRegion;
end;

procedure TBitBtnEX.Resize;    // ResizeのSetRegionの追加
begin
  inherited Resize;
  SetRegion;
//  if assigned(FOnResize) then // BitBtnにOnResizeなし
//  FOnResize(Self);
end;

procedure TBitBtnEX.SetRegion; // 楕円形 又は 四角形のRegionの設定追加
  var region: HRGN;
begin
  if FStyle = BsOvalDRaw then begin                           // 楕円形の場合 Regionの設定変更
    Region := CreateEllipticRgn(0, 0 , width + 1, height + 1);  // 楕円形の指定;
//  else // 四角形は設定されているので不要
//    Region := CreateRoundRectRgn (0, 0, Width + 1, Height + 1, 2, 2); // 四角形の指定
    SetWindowRgn(Handle, Region, True);
    DeleteObject(Region);
  end;
end;

procedure TBitBtnEX.UpdateStyleElements;
begin
  Invalidate;
end;

procedure TBitBtnEX.Click;
var
  Form: TCustomForm;
  Control: TWinControl;
begin
  case FKind of
    bkClose:
      begin
        Form := GetParentForm(Self);
        if Form <> nil then Form.Close
                       else inherited Click;
      end;
    bkHelp:
      begin
        Control := Self;
        while (Control <> nil) and
              (((Control.HelpType = htContext) and (Control.HelpContext = 0)) or
              ((Control.HelpType = htKeyword) and (Control.HelpKeyword = ''))) do
          Control := Control.Parent;
        if Control <> nil then
        begin
          if Control.HelpType = htContext then
            Application.HelpContext(Control.HelpContext)
          else
            Application.HelpKeyword(Control.HelpKeyword);
        end
        else
          inherited Click;
      end;
    else
      inherited Click;
  end;
end;

procedure TBitBtnEX.CNMeasureItem(var Message: TWMMeasureItem);
var
  Temp: PMeasureItemStruct;
begin
  Temp := Message.MeasureItemStruct;
  with Temp^ do
  begin
    itemWidth := Width;
    itemHeight := Height;
  end;
end;

procedure TBitBtnEX.CNDrawItem(var Message: TWMDrawItem);
begin
  DrawItem(Message.DrawItemStruct^);
end;

// **************************************************
// 影部RGB設定 追加
function TBitBtnEX.HLS2RGB(const HLS: TColorVector): TColorVector;
const
  Hue: array[0..5, 0..2] of Integer = (
  ( 1, -1, -1),   // red
  ( 1, 1, -1),    // yellow
  (-1, 1, -1),    // green
  (-1, 1, 1),     // cyan
  (-1, -1, 1),    // blue
  ( 1, -1, 1));   // magenta
  Components: array[0..2] of string = (
  'Hue',
  'Luminance',
  'Saturation');
var
  i, j, k: Integer;
  x: Double;
begin
  for i := Low(HLS.Coord) to High(HLS.Coord) do
    if (HLS.Coord[i] < 0) or (HLS.Coord[i] > 1) then
      raise EColorConv.CreateFmt('HLS2RGB: 0 <= %s value <= 1 required', [Components[i]]);

  j := Trunc(HLS.H * 6) mod 6;
  k := (j+1) mod 6;
  x := Frac(HLS.H * 6);
  for i := 0 to 2 do
    Result.Coord[i] := Hue[j, i] + x * (Hue[k, i] - Hue[j, i]);
  for i := 0 to 2 do Result.Coord[i] := Result.Coord[i] * HLS.S;
  if HLS.L <= 0.5 then
    for i := 0 to 2 do Result.Coord[i] := HLS.L * (Result.Coord[i] + 1)
  else
    for i := 0 to 2 do Result.Coord[i] := HLS.L + Result.Coord[i] * (1 - HLS.L);

  for i := 0 to 2 do
    if Result.Coord[i] < 0 then Result.Coord[i] := 0 else
      if Result.Coord[i] > 1 then Result.Coord[i] := 1;
end;

// 影部係数設定 追加
function TBitBtnEX.RGB2HLS(const RGB: TColorVector): TColorVector;
const
  Epsilon = 1E-8;
  Components: array[0..2] of string = (
    'Red',
    'Green',
    'Blue');
var
  i, k: Integer;
  x: Double;
  V: TColorVector;
  W: TColorVector;
//  W: TColorVector absolute Result;
  Hue: Double;
  Sat: Double;
  Lum: Double;

  function GetHue: Double;
  begin
    case k of
      0: if W.G > W.B then Result := 2 + (W.B + 1) / 2
         else Result := 4 - (W.G + 1) / 2;
      1: if W.B > W.R then Result := 4 + (W.R + 1) / 2
         else Result := 6 - (W.B + 1) / 2;
      2: if W.R > W.G then Result := (W.G + 1) / 2
         else Result := 2 - (W.R + 1) / 2;
      else Result := 0;
    end;
    Result := Result / 6;
  end;

begin
  for i := Low(RGB.Coord) to High(RGB.Coord) do
    if (RGB.Coord[i] < 0) or (RGB.Coord[i] > 1) then
      raise EColorConv.CreateFmt('RGB2HLS: 0 <= %s value <= 1 required', [Components[i]]);

  x := 0;
  for i := 0 to 2 do
  begin
    V.Coord[i] := 2 * RGB.Coord[i] - 1;   // [0, 1] -> [-1, 1]
    if Abs(V.Coord[i]) > x then
    begin
      x := Abs(V.Coord[i]);
      k := i;                             // index of RGB coordinate most different from 0.5
    end;
  end;
  if x < Epsilon then                    // middle grey
  begin
    W.H := 0;
    W.L := 0.5;                           // could be RGB.G or RGB.B as well
    W.S := 0;
    Result := W;
    Exit;
  end
  else x := 1 / x;
  for i := 0 to 2 do W.Coord[i] := V.Coord[i] * x;
  x := 0;
  if V.Coord[k] <= 0 then
  begin
    for i := 0 to 2 do if (W.Coord[i] + 1) > x then x := W.Coord[i] + 1;
    if x < Epsilon then                  // R = G = B: location on grey axis
    begin
      W.H := 0;
      W.L := RGB.R;                       // could be RGB.G or RGB.B as well
      W.S := 0;
      Result := W;
      Exit;
    end
    else x := 2 / x;
    for i := 0 to 2 do W.Coord[i] := x * (W.Coord[i] + 1) - 1;
    Hue := GetHue;

    if Abs(V.G - V.R) > Epsilon then
      Sat := (V.G - V.R) / (W.Coord[1] * (V.R + 1) - W.Coord[0] * (V.G + 1))
    else if Abs(V.B - V.G) > Epsilon then
      Sat := (V.B - V.G) / (W.Coord[2] * (V.G + 1) - W.Coord[1] * (V.B + 1))
    else if Abs(V.B - V.R) > Epsilon then
         Sat := (V.B - V.R) / (W.Coord[2] * (V.R + 1) - W.Coord[0] * (V.B + 1))
    else Sat := 0;

    if Abs(W.Coord[1] - W.Coord[0]) > Epsilon then
      Lum := (W.Coord[1] * (V.R + 1) - W.Coord[0] * (V.G + 1)) / (W.Coord[1] - W.Coord[0])
    else if Abs(W.Coord[2] - W.Coord[1]) > Epsilon then
      Lum := (W.Coord[2] * (V.G + 1) - W.Coord[1] * (V.B + 1)) / (W.Coord[2] - W.Coord[1])
    else if Abs(W.Coord[2] - W.Coord[0]) > Epsilon then
      Lum := (W.Coord[2] * (V.R + 1) - W.Coord[0] * (V.B + 1)) / (W.Coord[2] - W.Coord[0])
    else Lum := V.R + 1;
    Lum := Lum * 0.5;
  end else
  begin
    for i := 0 to 2 do if (1 - W.Coord[i]) > x then x := 1 - W.Coord[i];
    if x < Epsilon then  // R = G = B: location on grey axis
    begin
      W.H := 0;
      W.L := RGB.R;      // could be RGB.G or RGB.B as well
      W.S := 0;
      Result := W;
      Exit;
    end
    else x := 2 / x;
    for i := 0 to 2 do W.Coord[i] := x * (W.Coord[i] - 1) + 1;
    x := 1;
    for i := 0 to 2 do
      if W.Coord[i] < x then
      begin
        x := W.Coord[i];
        k := i;
      end;
    Hue := GetHue;

    if Abs(V.G - V.R) > Epsilon then
      Sat := (V.G - V.R) / (W.Coord[0] * (V.G - 1) - W.Coord[1] * (V.R - 1))
    else if Abs(V.B - V.G) > Epsilon then
      Sat := (V.B - V.G) / (W.Coord[1] * (V.B - 1) - W.Coord[2] * (V.G - 1))
    else if Abs(V.B - V.R) > Epsilon then
      Sat := (V.B - V.R) / (W.Coord[0] * (V.B - 1) - W.Coord[2] * (V.R - 1))
    else Sat := 0;

    if Abs(W.Coord[1] - W.Coord[0]) > Epsilon then
      Lum := (W.Coord[1] * (V.R - 1) - W.Coord[0] * (V.G - 1)) / (W.Coord[1] - W.Coord[0])
    else if Abs(W.Coord[2] - W.Coord[1]) > Epsilon then
      Lum := (W.Coord[2] * (V.G - 1) - W.Coord[1] * (V.B - 1)) / (W.Coord[2] - W.Coord[1])
    else if Abs(W.Coord[2] - W.Coord[0]) > Epsilon then
      Lum := (W.Coord[2] * (V.R - 1) - W.Coord[0] * (V.B - 1)) / (W.Coord[2] - W.Coord[0])
    else Lum := V.R - 1;
    Lum := 1 + Lum * 0.5;
  end;
  W.H := Hue;
  W.L := Lum;
  W.S := Sat;

  for i := 0 to 2 do
    if W.Coord[i] < 0 then W.Coord[i] := 0 else
    if W.Coord[i] > 1 then W.Coord[i] := 1;
  Result := W;
end;

// BevelColorの明暗色設定 追加
procedure TBitBtnEX.Get3DColors(FaceColor: TColor; var HighLightColor, ShadowColor: TColor; HLFactor, ShFactor: single);
var
  V,HLS : TColorVector;
  R,G,B : Byte;
begin
  FaceColor := ColorToRGB(FaceColor);
  R := GetRValue(FaceColor);
  G := GetGValue(FaceColor);
  B := GetBValue(FaceColor);
  HighLightColor := RGB(
    255 - round((256 - R) * HLFactor),
    255 - round((256 - G) * HLFactor),
    255 - round((256 - B) * HLFactor));
  V.R := R / 255;
  V.G := G / 255;
  V.B := B / 255;
  HLS := RGB2HLS(V);
  HLS.L := HLS.L * ShFactor; // Luminance := Luminance * Shadowfactor
  V := HLS2RGB(HLS);
    ShadowColor := RGB(
    Round(V.R * 255),
    Round(V.G * 255),
    Round(V.B * 255));
end;

// Bevel部の描画 追加
procedure TBitBtnEX.PaintBorder(FState: Integer);
begin
  Get3DColors(FColor, FColorHighlight, FColorShadow, (10 - FHLContrast) / 10, (10 - FShContrast) / 10); // BevelColorの明暗色設定 追加
  with FCanvas do begin
    Pen.Style := psSolid;
    Brush.Style := bsSolid;
    if FState = -1 then begin // down
      Pen.Color := FColorShadow;
      Brush.Color := FColorShadow;
    end;
    if FState = 1 then begin // up
      Pen.Color := FColorHighlight;
      Brush.Color := FColorHighlight;
    end;
    if FState = 0 then Pen.Color := FColor; // Flat
    Pie(1, 1, Width - 1, Height- 1,
    Width div 5 * 4, Height div 5,
    Width div 5, Height div 5 * 4);
    if FState = -1 then begin // down
      Pen.Color := FColorHighlight;
      Brush.Color := FColorHighlight;
    end;
    if FState = 1 then begin // up
      Pen.Color := FColorShadow;
      Brush.Color := FColorShadow;
    end;
    Pie(1, 1, Width - 1, Height - 1,
      Width div 5, Height div 5 * 4,
      Width div 5 * 4, Height div 5);
  end;
end;

procedure TBitBtnEX.DrawItem(const DrawItemStruct: TDrawItemStruct);
const
  WordBreakFlag: array[Boolean] of Integer = (0, DT_WORDBREAK);
var
  LogBrush :TLogBrush;
  IsDown, IsDefault: Boolean;
  State: TButtonState;
  R: TRect;
  Flags: Longint;
  Details: TThemedElementDetails;
  Button: TThemedButton;
  Offset: TPoint;
  LStyle: TCustomStyleServices;
  FontColor : Tcolor;
begin
  R := ClientRect;

  with DrawItemStruct do
  begin
    FCanvas.Handle := hDC;
    FCanvas.Font := Self.Font;
    IsDown := itemState and ODS_SELECTED <> 0;
    IsDefault := itemState and ODS_FOCUS <> 0;

    if not Enabled then State := bsDisabled
    else if IsDown then State := bsDown
    else State := bsUp;
  end;

// *******************************************************
  if FStyle = BsOvalDRaw then begin // OvalButton指定時の描画部追加
    FCanvas.Pen.Style := psSolid;

    FCanvas.Pen.Color := clWindowFrame;
    FCanvas.Pen.Width := 1;
    FCanvas.Brush.Style := bsSolid;
    InflateRect(R, -FBevelWidth, -FBevelWidth); // べベル幅分縦横を小さくします  

    if IsDown then
    begin
      PaintBorder(-1); // ボタンダウン時Bevel描画
      FCanvas.Pen.Color := FColorShadow;
      FCanvas.Brush.Color := FColor;
      FCanvas.Ellipse(R.Left, R.Top, R.Right, R.Bottom); // Bevelの内側塗りつぶし
    end
    else
    begin
      PaintBorder(1); // ボタン通常時時Bevel描画
      FCanvas.Brush.Style := bsSolid;
      FCanvas.Brush.Color := FColor;
      FCanvas.Ellipse(R.Left, R.Top, R.Right, R.Bottom); // Bevelの内側塗りつぶし
    end;

    if not IsFocused and not FMouseInControl then PaintBorder(0); // Focuseがなくマウスカーソルも上にいなかったら全体塗りつぶし

    FCanvas.Font := Self.Font; // Fontの取得
    R := ClientRect; // 描画枠取得
    if IsDown then begin
      OffsetRect(R, 1, 1); // 描画枠1ピクセル右下に移動
    end;
    FontColor := FCanvas.Font.Color; // フォントカラーのバックアップ
    if not Enabled then begin // フォントデスェブル色設定
      if FColor = ClGray then FCanvas.Font.Color := clLtGray
      else FCanvas.Font.Color := clGray;
    end;
    TButtonGlyph(FGlyph).FThemesEnabled := ThemeControl(Self); // テーマフラグの取得
    TButtonGlyph(FGlyph).Draw(FCanvas, R, Point(0,0), Caption, FLayout, FMargin, // 画像とCaptionの描画
      FSpacing, State, True, DrawTextBiDiModeFlags(0) or WordBreakFlag[WordWrap]); // TransparentをTrueに設定
    FCanvas.Font.Color := FontColor; // フォント色元へ戻し

    if IsFocused and IsDefault then // フォーカス枠の描画
    begin
      LogBrush.lbStyle := BS_SOLID;
      LogBrush.lbColor := ClBlack;
      LogBrush.lbHatch := 0;
      InflateRect(R, - FBevelWidth - 3, - FBevelWidth - 3);
      with FCanvas do begin
        Pen.Handle :=ExtCreatePen(PS_COSMETIC // 単純な幅が 1 のコスメティックペン
//                                  or PS_SOLID
//                                  or PS_ENDCAP_SQUARE
                                  or PS_ALTERNATE, // 1 ピクセルおきに描画を行うペン,
                                  1,         // ペンの幅1
                                 LogBrush,   // 黒色指定
                                 0,nil);     // nil PS_USERSTYLEの指定なし

        Pen.Mode := pmNotXor; // ペンモードセット

        Ellipse(R.Left, R.Top, R.Right, R.Bottom); // フォーカス枠作図
      end;
    end;

    FCanvas.Pen.Mode := PmCopy; // ペンモードセット
    R := ClientRect;
    FCanvas.Pen.Style := PsSolid;
    FCanvas.Brush.Style := bsClear;
    if not Enabled then begin
      if FColor = clSilver then FCanvas.Pen.Color := clBtnface
      else FCanvas.Pen.Color := clLtGray;
    end
    else begin
      if FColor = clWindowFrame then FCanvas.Pen.Color := clBlack
      else FCanvas.Pen.Color := clWindowFrame;
    end;
    FCanvas.Ellipse(R.Left + 1, R.Top + 1, R.Right - 1, R.Bottom - 1); // 一番外側のフレーム枠描画
  end
// **************************************************************************
  else begin // Bitbtn 描画部 BsColorDRaw 追加
    if ThemeControl(Self) then
    begin
      LStyle := StyleServices;
      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;

      Details := LStyle.GetElementDetails(Button);
      // Parent background.
      if not (csGlassPaint in ControlState) then
        LStyle.DrawParentBackground(Handle, DrawItemStruct.hDC, Details, True)
      else
        FillRect(DrawItemStruct.hDC, R, GetStockObject(BLACK_BRUSH));
      // Button shape.
      LStyle.DrawElement(DrawItemStruct.hDC, Details, DrawItemStruct.rcItem);
// **************************************
      if FStyle = BsColorDRaw then begin // BsColorDRaw追加 buttonの中を指定色で再描画 追加
        R := ClientRect;
        if Fcolor = clAqua then FCanvas.Pen.Width := 2
        else FCanvas.Pen.Width := 1;
        FCanvas.Pen.Color := clAqua;
        FCanvas.Brush.Color := Fcolor;
        if FMouseInControl then begin
          FCanvas.Pen.Style := PsSolid;
          FCanvas.RoundRect(R.Left + 5, R.Top + 5, R.Right - 4, R.Bottom - 4, 4, 4);
        end
        else begin
          FCanvas.Pen.Style := PsClear;
          FCanvas.RoundRect(R.Left + 5, R.Top + 5, R.Right - 4, R.Bottom - 4, 2, 2);
        end;
      end;
// **************************************
      LStyle.GetElementContentRect(FCanvas.Handle, Details, DrawItemStruct.rcItem, R);

      Offset := Point(0, 0);
      TButtonGlyph(FGlyph).FPaintOnGlass := csGlassPaint in ControlState;
      TButtonGlyph(FGlyph).FThemeDetails := Details;
      TButtonGlyph(FGlyph).FThemesEnabled := ThemeControl(Self);
      TButtonGlyph(FGlyph).FThemeTextColor := seFont in StyleElements;
      TButtonGlyph(FGlyph).Draw(FCanvas, R, Offset, Caption, FLayout, FMargin, FSpacing, State, False,
      DrawTextBiDiModeFlags(0) or WordBreakFlag[WordWrap]);

      if IsFocused and IsDefault and LStyle.IsSystemStyle then
      begin
        FCanvas.Pen.Color := clWindowFrame;
        FCanvas.Brush.Color := clBtnFace;
        DrawFocusRect(FCanvas.Handle, R);
      end;
    end
    else
    begin
      Flags := DFCS_BUTTONPUSH or DFCS_ADJUSTRECT;
      if IsDown then Flags := Flags or DFCS_PUSHED;
      if DrawItemStruct.itemState and ODS_DISABLED <> 0 then
        Flags := Flags or DFCS_INACTIVE;

      { DrawFrameControl doesn't allow for drawing a button as the
        default button, so it must be done here. }
      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);

        { DrawFrameControl must draw within this border }
        InflateRect(R, -1, -1);
      end;
// ****************************
      { DrawFrameControl does not draw a pressed button correctly }
      if IsDown then
      begin
        FCanvas.Pen.Color := clBtnShadow;
        FCanvas.Pen.Width := 1;
        if FStyle = BsColorDRaw then // BsColorDRaw追加 buttonの中を指定色で再描画 追加
          FCanvas.Brush.Color := FColor
        else
          FCanvas.Brush.Color := clBtnFace;
        FCanvas.Rectangle(R.Left, R.Top, R.Right, R.Bottom);
        InflateRect(R, -1, -1);
      end
      else begin
        DrawFrameControl(DrawItemStruct.hDC, R, DFC_BUTTON, Flags);
        if FStyle = BsColorDRaw then begin // BsColorDRaw追加 buttonの中を指定色で再描画 追加
          FCanvas.Pen.Style := PsClear;
          FCanvas.Brush.Color := FColor;
          FCanvas.Rectangle(R.Left, R.Top, R.Right, R.Bottom);
        end;
      end;
      FCanvas.Pen.Style := PsSolid;
// ****************************

      if IsFocused then
      begin
        R := ClientRect;
        InflateRect(R, -1, -1);
      end;

      FCanvas.Font := Self.Font;
      if IsDown then
        OffsetRect(R, 1, 1);

      TButtonGlyph(FGlyph).FThemesEnabled := ThemeControl(Self);
      TButtonGlyph(FGlyph).Draw(FCanvas, R, Point(0,0), Caption, FLayout, FMargin,
        FSpacing, State, True, DrawTextBiDiModeFlags(0) or WordBreakFlag[WordWrap]); // TransparentをTrueに設定

      if IsFocused and IsDefault then
      begin
        R := ClientRect;
        InflateRect(R, -4, -4);
        FCanvas.Pen.Color := clWindowFrame;
        FCanvas.Brush.Color := clBtnFace;
        DrawFocusRect(FCanvas.Handle, R);
      end;
    end;
  end;
  FCanvas.Handle := 0;
end;

procedure TBitBtnEX.CMFontChanged(var Message: TMessage);
begin
  inherited;
  Invalidate;
end;

procedure TBitBtnEX.CMEnabledChanged(var Message: TMessage);
begin
  inherited;
  Invalidate;
end;

procedure TBitBtnEX.WMLButtonDblClk(var Message: TWMLButtonDblClk);
begin
  Perform(WM_LBUTTONDOWN, Message.Keys, LPARAM(Word(Message.XPos) or (Word(Message.YPos) shr 16)));
end;

function TBitBtnEX.GetPalette: HPALETTE;
begin
  Result := Glyph.Palette;
end;

procedure TBitBtnEX.SetGlyph(Value: TBitmap);
begin
  TButtonGlyph(FGlyph).Glyph := Value as TBitmap;
  FModifiedGlyph := True;
  Invalidate;
end;

function TBitBtnEX.GetGlyph: TBitmap;
begin
  Result := TButtonGlyph(FGlyph).Glyph;
end;

procedure TBitBtnEX.GlyphChanged(Sender: TObject);
begin
  Invalidate;
end;

procedure TBitBtnEX.InternalCopyImage(Image: TBitmap;
  ImageList: TCustomImageList; Index: Integer);
begin
  with Image do
  begin
    Width := ImageList.Width;
    Height := ImageList.Height;
    Canvas.Brush.Color := clFuchsia; //! for lack of a better color
    Canvas.FillRect(Rect(0,0, Width, Height));
    ImageList.Draw(Canvas, 0, 0, Index);
  end;
end;

function TBitBtnEX.IsCustom: Boolean;
var
  Link: TBitBtnEXActionLink;
begin
  Link := TBitBtnEXActionLink(ActionLink);
  Result := (Kind = bkCustom) and
    not ((Link <> nil) and Link.IsImageIndexLinked and Link.IsGlyphLinked(Link.FImageIndex));
end;

procedure TBitBtnEX.SetStyle(Value: TButtonStyle);
begin
  if Value <> FStyle then
  begin
    FStyle := Value;
    Invalidate;
  end;
end;

procedure TBitBtnEX.SetKind(Value: TBitBtnEXKind);
begin
  if Value <> FKind then
  begin
    if Value <> bkCustom then
    begin
      Default := Value in [bkOK, bkYes];
      Cancel := Value in [bkCancel, bkNo];

      if ((csLoading in ComponentState) and (Caption = '')) or
          (not (csLoading in ComponentState)) then
      begin
        if BitBtnExCaptions[Value] <> nil then
          aption := BitBtnExCaptions[Value];
//      Caption := LoadResString(BitBtnExCaptions[Value]);
      end;

      ModalResult := BitBtnExModalResults[Value];
      TButtonGlyph(FGlyph).Glyph := GeTBitBtnEXGlyph(Value);
      NumGlyphs := 2;
      FModifiedGlyph := False;
    end;
    FKind := Value;
    Invalidate;
  end;
end;

function TBitBtnEX.IsCustomCaption: Boolean;
begin
  Result := AnsiCompareStr(Caption, BitBtnExCaptions[FKind]) <> 0;
//  Result := AnsiCompareStr(Caption, LoadResString(BitBtnExCaptions[FKind])) <> 0;
end;

function TBitBtnEX.GetKind: TBitBtnEXKind;
begin
  if FKind <> bkCustom then
  if ((FKind in [bkOK, bkYes]) xor Default) or
    ((FKind in [bkCancel, bkNo]) xor Cancel) or
    (ModalResult <> BitBtnExModalResults[FKind]) or
    FModifiedGlyph then
    FKind := bkCustom;
  Result := FKind;
end;

procedure TBitBtnEX.SetLayout(Value: TButtonLayout);
begin
  if FLayout <> Value then
  begin
    FLayout := Value;
    Invalidate;
  end;
end;

function TBitBtnEX.GetNumGlyphs: TNumGlyphs;
begin
  Result := TButtonGlyph(FGlyph).NumGlyphs;
end;

procedure TBitBtnEX.SetNumGlyphs(Value: TNumGlyphs);
begin
  if Value < 0 then Value := 1
  else if Value > 4 then Value := 4;
  if Value <> TButtonGlyph(FGlyph).NumGlyphs then
  begin
    TButtonGlyph(FGlyph).NumGlyphs := Value;
    Invalidate;
  end;
end;

// BevelWidthの設定 追加
procedure TBitBtnEX.SetBevelWidth(newValue: TBevelWidth);
begin
  if FBevelWidth <> newValue then begin
    FBevelWidth := newValue;
    Invalidate;
  end;
end;

// Ovalボタンの色設定追加
procedure TBitBtnEX.SetColor(newColor: TColor);
begin
  if FColor <> newColor then begin
    FColor := newColor;
    Invalidate;
  end;
end;

procedure TBitBtnEX.SetSpacing(Value: Integer);
begin
  if FSpacing <> Value then
  begin
    FSpacing := Value;
    Invalidate;
  end;
end;

procedure TBitBtnEX.SetMargin(Value: Integer);
begin
  if (Value <> FMargin) and (Value >= - 1) then
  begin
    FMargin := Value;
    Invalidate;
  end;
end;

procedure TBitBtnEX.CopyImage(ImageList: TCustomImageList; Index: Integer);
begin
  InternalCopyImage(Glyph, ImageList, Index);
end;

procedure TBitBtnEX.ActionChange(Sender: TObject; CheckDefaults: Boolean);
var
  Link: TBitBtnEXActionLink;
begin
  inherited ActionChange(Sender, CheckDefaults);
  if Sender is TCustomAction then
    with TCustomAction(Sender) do
    begin
      Link := TBitBtnEXActionLink(ActionLink);
      if CheckDefaults and not Link.IsGlyphLinked(Link.FImageIndex) and (not Glyph.Empty) then
        Exit;
      if CheckDefaults or (Link.FImageIndex <> -1) or (Link.FImageIndex <> ImageIndex) then
      begin
        Link.FImageIndex := ImageIndex;
        if ImageIndex <> -1 then
          Glyph := nil;
      end;
      { Copy image from action's imagelist }
      if (Glyph.Empty) and (ActionList <> nil) and (ActionList.Images <> nil) and
        (ImageIndex >= 0) and (ImageIndex < ActionList.Images.Count) then
        CopyImage(ActionList.Images, ImageIndex);
    end;
end;

procedure DestroyLocals; far;
var
  I: TBitBtnEXKind;
begin
  for I := Low(TBitBtnEXKind) to High(TBitBtnEXKind) do
    BitBtnExGlyphs[I].Free;
end;

procedure TBitBtnEX.CMMouseEnter(var Message: TMessage);
begin
  inherited;
  if StyleServices.Enabled and not FMouseInControl and not (csDesigning in ComponentState) then
  begin
    FMouseInControl := True;
    Repaint;
  end;
end;

procedure TBitBtnEX.CMMouseLeave(var Message: TMessage);
begin
  inherited;
  if StyleServices.Enabled and FMouseInControl then
  begin
    FMouseInControl := False;
    Repaint;
  end;
end;

function TBitBtnEX.GetActionLinkClass: TControlActionLinkClass;
begin
  Result := TBitBtnEXActionLink;
end;

{ TBinBtnStyleHook }

procedure TBitBtnEXStyleHook.DrawButton(ACanvas: TCanvas; AMouseInControl: Boolean);
const
  WordBreakFlag: array[Boolean] of Integer = (0, DT_WORDBREAK);
var
  Details: TThemedElementDetails;
  DrawRect: TRect;
  Offset: TPoint;
  State: TButtonState;
begin
  if not (Control is TBitBtnEX) then
  begin
    inherited;
    Exit;
  end;
  DrawRect := Control.ClientRect;
  if FPressed then
    Details := StyleServices.GetElementDetails(tbPushButtonPressed)
  else if AMouseInControl then
    Details := StyleServices.GetElementDetails(tbPushButtonHot)
  else if Focused then
    Details := StyleServices.GetElementDetails(tbPushButtonDefaulted)
  else if Control.Enabled then
    Details := StyleServices.GetElementDetails(tbPushButtonNormal)
  else
    Details := StyleServices.GetElementDetails(tbPushButtonDisabled);
  DrawRect := Control.ClientRect;
  StyleServices.DrawElement(ACanvas.Handle, Details, DrawRect);

  Offset := Point(0, 0);
  with TBitBtnEX(Control) do
  begin
    if not Enabled then State := bsDisabled
    else if FPressed then State := bsDown
    else State := bsUp;
    ACanvas.Font := Font;
    TButtonGlyph(FGlyph).FPaintOnGlass := False;
    TButtonGlyph(FGlyph).FThemeDetails := Details;
    TButtonGlyph(FGlyph).FThemesEnabled := True;
    TButtonGlyph(FGlyph).FThemeTextColor := seFont in StyleElements;
    TButtonGlyph(FGlyph).Draw(ACanvas, DrawRect, Offset, Caption, FLayout, FMargin, FSpacing, State, False,
    DrawTextBiDiModeFlags(0) or WordBreakFlag[WordWrap]);
  end;
end;

{ TBitBtnEXActionLink }

procedure TBitBtnEXActionLink.AssignClient(AClient: TObject);
begin
  inherited AssignClient(AClient);
  FClient := AClient as TBitBtnEX;
end;

constructor TBitBtnEXActionLink.Create(AClient: TObject);
begin
  inherited;
  FImageIndex := -1;
end;

function TBitBtnEXActionLink.IsImageIndexLinked: Boolean;
begin
  Result := inherited IsImageIndexLinked and
    (FImageIndex = TCustomAction(Action).ImageIndex);
end;

function TBitBtnEXActionLink.IsGlyphLinked(Index: TImageIndex): Boolean;
var
  LBitmap: TBitmap;
  Images: TCustomImageList;
begin
  Images := TCustomAction(Action).ActionList.Images;
  Result := (Images <> nil) and (FClient.Glyph <> nil) and
    (FClient.Glyph.Width = Images.Width) and (FClient.Glyph.Height = Images.Height);
  if Result then
  begin
    LBitmap := TBitmap.Create;
    try
      FClient.InternalCopyImage(LBitmap, Images, Index);
      Result := LBitmap.Equals(FClient.Glyph);
    finally
      LBitmap.Free;
    end;
  end;
end;

procedure TBitBtnEXActionLink.SetImageIndex(Value: Integer);
begin
  if IsImageIndexLinked or FClient.Glyph.Empty then
  begin
    if Action is TCustomAction then
      with TCustomAction(Action) do
        { Copy image from action's imagelist }
        if (ActionList <> nil) and (ActionList.Images <> nil) then
          if (Value >= 0) and (Value < ActionList.Images.Count) then
          begin
            if IsGlyphLinked(FImageIndex) or FClient.Glyph.Empty then
            FClient.CopyImage(ActionList.Images, Value);
          end
          else
            FClient.Glyph := nil;
    FImageIndex := Value;
    FClient.GlyphChanged(nil);
  end;
end;


initialization
  FillChar(BitBtnExGlyphs, SizeOf(BitBtnExGlyphs), 0);
finalization
  DestroyLocals;
end.


    download BitbtnEx.zip


     コンポーネント一覧へ戻る