はじめに
ActiveBasicで実装するグラフィカルコントロールということで解説するのも3回目になります。今までの流れをまとめると、下記のとおりです。
今回は、LuxMenuを提供するために必要な各種モジュールの解説を行い、前回のおさらいとしてLuxMenuをCOMコンポーネント化するための具体的な公開方法を紹介します。
毎度ながら、ActiveBasic 5.0(以下、AB)を用いての解説になります。下記サイトからダウンロードし、インストールしておきましょう。
LuxMenuのおさらい
LuxMenuは既存の地味なウィンドウコントロールをグラフィカルなものにし、クールな雰囲気を簡単に演出するためのコントロールです。
LuxMenuを使うと、左端のグラデーションバーにアイコンを配置することが可能です。セレクト状態がクッキリと見やすいのも大きな特徴のひとつです。
LuxMenuを設計するにあたって
LuxMenuは、既存のメニューコントロールのオーナー描画機能を利用して実現するため、一からコントロールを作るわけではありません。内部の処理はWindowsに任せて、デザインのみをカスタマイズするという流れになります。
必要なオブジェクト
アプリ開発を経験されている方はご存知と思いますが、メニューアイテムには下記の2種類の層が存在します。
- 親メニュー層
- サブメニュー層
ウィンドウ上部のメニューバーに常に表示されているのが「親メニュー」です。その親メニューをクリックするとすぐ下に表示されるのが「サブメニュー」です。双方共にメニューコントロールですので構造上の大きな違いはありませんが、オーナー描画を行う上で多少の違いが生じます。また、新規にメニューハンドルを生成する場合に、CreateMenu
/CreatePopupMenu
という具合にWin32API関数が分かれています。
そこで、メニューを管理するクラスとしてCLuxMenu
、CLuxSubMenu
を作成していきたいと思います。また、これら2つのクラスに加えて、便利なメソッドを提供するCLuxCtrlBase
クラス、個々のメニューアイテムを管理するためのCMenuItemData
クラスを定義していきます。
CLuxCtrlBaseクラス
「LuxMenu.sbp」を開き、CLuxCtrlBase
クラスの宣言部分を見てみましょう。
このCLuxCtrlBase
クラスは、LuxMenuコントロールを実装する上で、あると便利な機能を定義し、スーパークラスとして提供する役割を果たします。各メソッドはProtected
アクセス修飾子が指定され、派生クラスからのみの呼び出しが可能となっています。
HitTestメソッド
HitTest
はpos
で指定した座標がrc
の四角形内に位置するかどうかを判断するためのメソッドです。ヒットした場合は1を、そうでない場合は0を返します。
'------------------------------------------- ' 座標領域の認識 '------------------------------------------- Function HitTest(ByRef rc As RECT, ByRef pos As POINTAPI) As BOOL If rc.left<=pos.x and pos.x<rc.right and _ rc.top<=pos.y and pos.y<rc.bottom Then HitTest=1 Else HitTest=0 End If End Function
LightRGBメソッド
LightRGB
メソッドは、第1パラメータのRGB値の各要素にratio
を掛け合わせ、結果を返します。RGB値の明るさを倍増させたいとき(または減少させたいとき)に利用します。
'------------------------------------------- ' RGBの明るさを調整 '------------------------------------------- Function LightRGB(rgb As COLORREF, ratio As Double) As COLORREF LightRGB=RGB(LOBYTE(LOWORD(rgb))+((255-LOBYTE(LOWORD(rgb)))*ratio), HIBYTE(LOWORD(rgb))+((255-HIBYTE(LOWORD(rgb)))*ratio), LOBYTE(HIWORD(rgb))+((255-LOBYTE(HIWORD(rgb)))*ratio)) End Function
CreateGradationBitmapメソッド
CreateGradationBitmap
はグラデーションビットマップを生成するためのメソッドです。size
パラメータで幅・高さを指定し、color1
~color2
に変化するようなグラデーションビットマップを生成します。
CreateDIBSection
関数でビットマップを生成し、取得したビットマップ配列(pByte
配列)に直接的にグラデーションを表現するためのRGB値を書き込んでいきます。pByte
配列の内容は直接的にビットマップに反映されます。また、CreateDIBSection
関数によって生成されたビットマップハンドルがこのメソッドの戻り値となります。fDirection
パラメータにDIRECTION_HORZ
を指定すると水平方向、DIRECTION_VERT
を指定すると垂直方向にグラデーションが描かれるのも注目したい点です。
戻り値のビットマップハンドルが不要になったら、DeleteObject
関数で破棄する必要があります。
'------------------------------------------- ' グラデーションビットマップを生成 '------------------------------------------- Function CreateGradationBitmap(ByRef size As SIZE,color1 As COLORREF, color2 As COLORREF, fDirection As Long) As HBITMAP Dim BitmapInfo As BITMAPINFO FillMemory(VarPtr(BitmapInfo.bmiHeader),SizeOf(BITMAPINFOHEADER),0) BitmapInfo.bmiHeader.biSize=SizeOf(BITMAPINFOHEADER) BitmapInfo.bmiHeader.biWidth=size.cx BitmapInfo.bmiHeader.biHeight=size.cy BitmapInfo.bmiHeader.biPlanes=1 BitmapInfo.bmiHeader.biBitCount=24 Dim hdc As HDC hdc=GetDC(GetDesktopWindow()) Dim hBitmap As HBITMAP Dim pByte As *Byte hBitmap=CreateDIBSection(hdc,BitmapInfo, DIB_RGB_COLORS,VarPtr(pByte),0,0) Dim i As Long, i2 As Long, x As Long, y As Long Dim rgb As COLORREF i=BitmapInfo.bmiHeader.biWidth*3 If (i mod SizeOf(Long))<>0 Then i+=SizeOf(Long)-(i mod SizeOf(Long)) Dim ratio As Double If fDirection=DIRECTION_HORZ Then '水平方向 For x=0 To ELM(BitmapInfo.bmiHeader.biWidth) ratio=x/BitmapInfo.bmiHeader.biWidth rgb=RGB(LOBYTE(LOWORD(color1))+(LOBYTE(LOWORD(color2)) As Long _ - LOBYTE(LOWORD(color1)) As Long)*(ratio), '赤要素 HIBYTE(LOWORD(color1))+(HIBYTE(LOWORD(color2)) As Long _ - HIBYTE(LOWORD(color1)) As Long)*(ratio), '緑要素 LOBYTE(HIWORD(color1))+(LOBYTE(HIWORD(color2)) As Long _ - LOBYTE(HIWORD(color1)) As Long)*(ratio)) '青要素 For y=0 To ELM(BitmapInfo.bmiHeader.biHeight) i2=y*i+x*3 pByte[i2+2]=LOBYTE(LOWORD(rgb)) pByte[i2+1]=HIBYTE(LOWORD(rgb)) pByte[i2]=LOBYTE(HIWORD(rgb)) Next Next ElseIf fDirection=DIRECTION_VERT Then '垂直方向 For y=0 To ELM(BitmapInfo.bmiHeader.biHeight) ratio=y/BitmapInfo.bmiHeader.biHeight rgb=RGB(LOBYTE(LOWORD(color1))+(LOBYTE(LOWORD(color2)) As Long _ - LOBYTE(LOWORD(color1)) As Long)*(ratio), '赤要素 HIBYTE(LOWORD(color1))+(HIBYTE(LOWORD(color2)) As Long _ - HIBYTE(LOWORD(color1)) As Long)*(ratio), '緑要素 LOBYTE(HIWORD(color1))+(LOBYTE(HIWORD(color2)) As Long _ - LOBYTE(HIWORD(color1)) As Long)*(ratio)) '青要素 For x=0 To ELM(BitmapInfo.bmiHeader.biWidth) i2=y*i+x*3 pByte[i2+2]=LOBYTE(LOWORD(rgb)) pByte[i2+1]=HIBYTE(LOWORD(rgb)) pByte[i2]=LOBYTE(HIWORD(rgb)) Next Next End If DeleteDC(hdc) CreateGradationBitmap= hBitmap End Function
CreateGrayIconメソッド
CreateGrayIcon
は淡色化されたアイコンを生成するためのメソッドです。hBaseIcon
に、もととなるアイコンを指定します。今回は24ビットフルカラーのみを対象としているので、このメソッドにアイコンハンドルを引き渡す際は注意しましょう。
まず、hBaseIcon
をもとにICONINFO
構造体を取得します。ICONINFO
構造体は以下のような定義になっており、hbmColor
でアイコンが持つビットマップハンドルを参照できます。
Type ICONINFO fIcon As BOOL xHotspot As DWord yHotspot As DWord hbmMask As HBITMAP hbmColor As HBITMAP End Type
IconInfo.hbmColor
をもとに、GetDIBits
関数を利用してビットマップハンドルのビットデータをpByte
が示すメモリに取得します。pByte
が示す領域はあらかじめmalloc
関数で確保しておきます。For
~Next
ループ内でpByte
が示すメモリの内容を変更し、淡色化を行います。
最後にSetDIBits
関数でpByte
の内容をIconInfo.hbmColor
に反映し、IconInfo
構造体をもとにCreateIconIndirect
関数でアイコンハンドルを生成して完了です。計算過程で利用した不要なビットマップはDeleteObject
関数で削除しておきましょう。
戻り値のアイコンハンドルが不要になったら、DestroyIcon
で破棄する必要があります。
'------------------------------------------- ' 淡色アイコンを生成 '------------------------------------------- Function CreateGrayIcon(hBaseIcon As HICON) As HICON Dim IconInfo As ICONINFO GetIconInfo(hBaseIcon,IconInfo) '--------------------- ' ビットマップを加工 '--------------------- Dim Bitmap As BITMAP GetObject(IconInfo.hbmColor,SizeOf(BITMAP),Bitmap) Dim BitmapInfo As BITMAPINFO FillMemory(VarPtr(BitmapInfo.bmiHeader),SizeOf(BITMAPINFOHEADER),0) BitmapInfo.bmiHeader.biSize=SizeOf(BITMAPINFOHEADER) BitmapInfo.bmiHeader.biWidth=Bitmap.bmWidth BitmapInfo.bmiHeader.biHeight=Bitmap.bmHeight BitmapInfo.bmiHeader.biPlanes=1 BitmapInfo.bmiHeader.biBitCount=24 BitmapInfo.bmiHeader.biCompression=BI_RGB Dim hdc As HDC hdc=GetDC(GetDesktopWindow()) Dim pByte As *Byte pByte=malloc(Bitmap.bmWidth*Bitmap.bmHeight*SizeOf(COLORREF)) GetDIBits(hdc, IconInfo.hbmColor, 0, Bitmap.bmHeight, pByte, BitmapInfo, DIB_RGB_COLORS) Dim i As Long, i2 As Long, x As Long, y As Long i=BitmapInfo.bmiHeader.biWidth*3 If (i mod SizeOf(Long))<>0 Then i+=SizeOf(Long)-(i mod SizeOf(Long)) For x=0 To ELM(BitmapInfo.bmiHeader.biWidth) For y=0 To ELM(BitmapInfo.bmiHeader.biHeight) i2=y*i+x*3 If pByte[i2+2]=0 and pByte[i2+1]=0 and pByte[i2]=0 Then '透明色 '何もしない Else Dim ratio=0.5 As Double '明るさ pByte[i2+2]+=((255-pByte[i2+2]) As Double*ratio) As Byte pByte[i2+1]+=((255-pByte[i2+1]) As Double*ratio) As Byte pByte[i2]+=((255-pByte[i2]) As Double*ratio) As Byte End If Next Next SetDIBits(hdc, IconInfo.hbmColor, 0, Bitmap.bmHeight, pByte, BitmapInfo, DIB_RGB_COLORS) free(pByte) DeleteDC(hdc) CreateGrayIcon=CreateIconIndirect(IconInfo) '不要なビットマップを破棄 DeleteObject(IconInfo.hbmMask) DeleteObject(IconInfo.hbmColor) End Function