SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

デザイン性に優れた拡張メニューコントロール

LuxMenuの内部構造の詳細を理解する

デザイン性に優れた拡張メニューコントロール 第3回


  • このエントリーをはてなブックマークに追加

連載3回目となる今回は、デザイン性に優れた拡張メニューコントロール「LuxMenu」をより深く知るために、その内部構造について解説していきます。どのような仕組みでLuxMenuが表示されるのか、また通常メニューからどのように移行するのかなどを紹介します。

  • このエントリーをはてなブックマークに追加

はじめに

 ActiveBasicで実装するグラフィカルコントロールということで解説するのも3回目になります。今までの流れをまとめると、下記のとおりです。

  • 第1回 … LuxMenuの使い方(呼び出し方法)
  • 第2回 … COMコンポーネントの公開方法
  • 第3回(今回) … LuxMenuの内部構造の詳細を理解する

 今回は、LuxMenuを提供するために必要な各種モジュールの解説を行い、前回のおさらいとしてLuxMenuをCOMコンポーネント化するための具体的な公開方法を紹介します。

 毎度ながら、ActiveBasic 5.0(以下、AB)を用いての解説になります。下記サイトからダウンロードし、インストールしておきましょう。

LuxMenuのおさらい

 LuxMenuは既存の地味なウィンドウコントロールをグラフィカルなものにし、クールな雰囲気を簡単に演出するためのコントロールです。

通常メニューとLuxMenuの違い
通常メニューとLuxMenuの違い

 LuxMenuを使うと、左端のグラデーションバーにアイコンを配置することが可能です。セレクト状態がクッキリと見やすいのも大きな特徴のひとつです。

LuxMenuを設計するにあたって

 LuxMenuは、既存のメニューコントロールのオーナー描画機能を利用して実現するため、一からコントロールを作るわけではありません。内部の処理はWindowsに任せて、デザインのみをカスタマイズするという流れになります。

必要なオブジェクト

 アプリ開発を経験されている方はご存知と思いますが、メニューアイテムには下記の2種類の層が存在します。

  • 親メニュー層
  • サブメニュー層

 ウィンドウ上部のメニューバーに常に表示されているのが「親メニュー」です。その親メニューをクリックするとすぐ下に表示されるのが「サブメニュー」です。双方共にメニューコントロールですので構造上の大きな違いはありませんが、オーナー描画を行う上で多少の違いが生じます。また、新規にメニューハンドルを生成する場合に、CreateMenu/CreatePopupMenuという具合にWin32API関数が分かれています。

 そこで、メニューを管理するクラスとしてCLuxMenuCLuxSubMenuを作成していきたいと思います。また、これら2つのクラスに加えて、便利なメソッドを提供するCLuxCtrlBaseクラス、個々のメニューアイテムを管理するためのCMenuItemDataクラスを定義していきます。

LuxMenuに関するクラスの構成図
LuxMenuに関するクラスの構成図

CLuxCtrlBaseクラス

 「LuxMenu.sbp」を開き、CLuxCtrlBaseクラスの宣言部分を見てみましょう。

 このCLuxCtrlBaseクラスは、LuxMenuコントロールを実装する上で、あると便利な機能を定義し、スーパークラスとして提供する役割を果たします。各メソッドはProtectedアクセス修飾子が指定され、派生クラスからのみの呼び出しが可能となっています。

HitTestメソッド

 HitTestposで指定した座標がrcの四角形内に位置するかどうかを判断するためのメソッドです。ヒットした場合は1を、そうでない場合は0を返します。

HitTestメソッドのコード
'-------------------------------------------
' 座標領域の認識
'-------------------------------------------
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値の明るさを倍増させたいとき(または減少させたいとき)に利用します。

LightRGBメソッドのコード
'-------------------------------------------
' 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パラメータで幅・高さを指定し、color1color2に変化するようなグラデーションビットマップを生成します。

 CreateDIBSection関数でビットマップを生成し、取得したビットマップ配列(pByte配列)に直接的にグラデーションを表現するためのRGB値を書き込んでいきます。pByte配列の内容は直接的にビットマップに反映されます。また、CreateDIBSection関数によって生成されたビットマップハンドルがこのメソッドの戻り値となります。fDirectionパラメータにDIRECTION_HORZを指定すると水平方向、DIRECTION_VERTを指定すると垂直方向にグラデーションが描かれるのも注目したい点です。

 戻り値のビットマップハンドルが不要になったら、DeleteObject関数で破棄する必要があります。

CreateGradationBitmapメソッドのコード
'-------------------------------------------
' グラデーションビットマップを生成
'-------------------------------------------
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でアイコンが持つビットマップハンドルを参照できます。

ICONINFO構造体の定義文
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関数で確保しておきます。ForNextループ内でpByteが示すメモリの内容を変更し、淡色化を行います。

 最後にSetDIBits関数でpByteの内容をIconInfo.hbmColorに反映し、IconInfo構造体をもとにCreateIconIndirect関数でアイコンハンドルを生成して完了です。計算過程で利用した不要なビットマップはDeleteObject関数で削除しておきましょう。

 戻り値のアイコンハンドルが不要になったら、DestroyIconで破棄する必要があります。

CreateGrayIconメソッドのコード
'-------------------------------------------
' 淡色アイコンを生成
'-------------------------------------------
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

会員登録無料すると、続きをお読みいただけます

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

次のページ
CLuxMenuクラス

この記事は参考になりましたか?

  • このエントリーをはてなブックマークに追加
デザイン性に優れた拡張メニューコントロール連載記事一覧

もっと読む

この記事の著者

山本 大祐(ヤマモト ダイスケ)

普段はActiveBasicと周辺ツールの開発を行っています。最近は諸先輩方を見習いながら勉強中の身。AB開発日記

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/514 2008/08/26 13:40

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング