SHOEISHA iD

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

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

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

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

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

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


CLuxSubMenuクラス

 CLuxSubMenuクラスはサブメニューを管理するためのクラスです。ほとんどはCLuxMenuクラスと同等の処理を行いますが、親メニューのようにメニューバーを描画するわけではないので、専用のメソッドをオーバーライドしていきたいと思います。

 CLuxSubMenuCLuxMenuは異なるクラスのため、QueryInterfaceメソッドをオーバーライドして専用のGUIDを識別させる必要があります。判別用のGUIDを変更するだけなので、コードの詳細は省略します。

GetItemSizeメソッド

 CLuxMenuGetItemSizeメソッドと異なる点は、文字列描画に必要な幅に加え、アイコン用の幅と文字列の右側に位置するある程度のブランクを考慮し、50ピクセルだけ多めにサイズ指定している点です。他はCLuxMenu.GetItemSizeと同じような処理になります。

GetItemSizeメソッドのコード
'-------------------------------------------------------
' メニューアイテムの表示領域を取得
'-------------------------------------------------------
Virtual Sub GetItemSize(item_index As Long, ByRef size As SIZE)
    If ppobj_MenuItemData[item_index]->str Then
        Dim hdc As HDC
        hdc=GetDC(GetDesktopWindow())
        Dim hOldFont As HFONT
        hOldFont=SelectObject(hdc,m_hMenuFont)

        GetTextExtentPoint32(hdc,
            ppobj_MenuItemData[item_index]->str,
            lstrlen(ppobj_MenuItemData[item_index]->str),
            size)

        SelectObject(hdc,hOldFont)
        DeleteDC(hdc)

        size.cx+=50
        If size.cy<22 Then size.cy=22
    Else
        'セパレータのとき
        size.cx=50
        size.cy=10
    End If
End Sub

OwnerDrawメソッド

 このメソッドは、メニューバー描画とポップアップメニューの見た目が違うのと同じくらい、CLuxMenuOwnerDrawメソッドとは大きく異なります。

 まずCLuxCtrlBase.CreateGradationBitmapメソッドを利用して左端に表示するグラデーションビットマップを生成し、描画を行います。セパレータの場合は境界線を引いて完了です。

 bSelectを使って選択状態を識別し、必要に応じてホットイメージで背景を描画します。この際に、メニューアイテムの状態が無効になっていないか(MFS_DISABLEDが指定されていないか)も考慮しておきましょう。

 メニューアイテムがチェック状態の場合は、アイコンを描画せずにチェックマークを描画しています。チェック状態でない場合は、アイコンが指定されていればアイコンを描画します。チェック状態でなく、アイコンも指定されていない場合は左側のグラデーションバーには何も表示する必要はありません。また、アイコンの描画にはDrawIconEx関数を使用し、透過処理などもその関数に任せます。

 最後に、文字列を描画して完了ですが、ショートカット文字列の存在を忘れずに行います。szMainにメニュー文字列を、szShortcutにショートカット文字列を分別して格納し、szShortcutは右端に描画します。

OwnerDrawメソッドのコード
'-------------------------------------------------------
' サブメニューのオーナー描画
'-------------------------------------------------------
Virtual Sub OwnerDraw(hdc As HDC, ByRef ref_rc As RECT,
    bSelect As BOOL, item_index As Long)
    Dim rc As RECT
    Dim BackColor As COLORREF, TextColor As COLORREF
    Dim i As Long

    Dim hPen As HPEN,hOldPen As HPEN
    Dim hBrush As HBRUSH, hOldBrush As HBRUSH

    '-----------------------------
    ' 左側のグラデーションを描画
    '-----------------------------

    Dim size As SIZE
    size.cx=22
    size.cy=ref_rc.bottom-ref_rc.top
    Dim hBmp As HBITMAP
    hBmp=CreateGradationBitmap(size,RGB(254,254,251), _
        RGB(196,196,173),DIRECTION_HORZ)

    Dim memdc As HDC
    memdc=CreateCompatibleDC(hdc)
    SelectObject(memdc,hBmp)
    BitBlt(hdc,ref_rc.left,ref_rc.top,size.cx,size.cy,memdc,0,0,SRCCOPY)
    DeleteDC(memdc)
    DeleteObject(hBmp)

    If ppobj_MenuItemData[item_index]->str=0 Then
        'セパレータのとき
        hPen=CreatePen(PS_SOLID,0,MenuColor_Default_Line)
        hOldPen=SelectObject(hdc,hPen)

        MoveToEx(hdc,ref_rc.left+24,
            ref_rc.top+(ref_rc.bottom-ref_rc.top)\2,ByVal NULL)
        LineTo(hdc,ref_rc.right,ref_rc.top+(ref_rc.bottom-ref_rc.top)\2)

        SelectObject(hdc,hOldPen)
        DeleteObject(hPen)
        Exit Sub
    End If

    'アイテムの状態を取得
    Dim mii As MENUITEMINFO
    mii.cbSize=SizeOf(MENUITEMINFO)
    mii.fMask=MIIM_STATE
    GetMenuItemInfo(m_hMenu,item_index,1,mii)

    If bSelect<>0 and (mii.fState and MFS_DISABLED)=0 Then
        'ホットイメージで表示
        BackColor=MenuColor_Hot_Back

        hPen=CreatePen(PS_SOLID,0,MenuColor_Hot_Line)
        hOldPen=SelectObject(hdc,hPen)

        hBrush=CreateSolidBrush(BackColor)
        hOldBrush=SelectObject(hdc,hBrush)

        Rectangle(hdc,ref_rc.left,ref_rc.top,ref_rc.right,ref_rc.bottom)

        SelectObject(hdc,hOldPen)
        SelectObject(hdc,hOldBrush)

        DeleteObject(hPen)
        DeleteObject(hBrush)
    Else
        BackColor=GetSysColor(COLOR_MENU)

        rc=ref_rc
        rc.left=ref_rc.left+22
        rc.right=ref_rc.right

        '背景を塗りつぶす
        hBrush=CreateSolidBrush(BackColor)
        FillRect(hdc,rc,hBrush)
        DeleteObject(hBrush)
    End If

    If mii.fState and MFS_CHECKED Then
        'チェックマークの外枠を描画
        rc=ref_rc
        rc.top++
        rc.right=rc.left+20
        rc.bottom--

        hPen=CreatePen(PS_SOLID,0,MenuColor_Hot_Line)
        hOldPen=SelectObject(hdc,hPen)

        hBrush=CreateSolidBrush(BackColor)
        hOldBrush=SelectObject(hdc,hBrush)

        Rectangle(hdc,rc.left,rc.top,rc.right,rc.bottom)

        SelectObject(hdc,hOldPen)
        SelectObject(hdc,hOldBrush)

        DeleteObject(hPen)
        DeleteObject(hBrush)

        'チェックマークを描画
        Dim BaseX As Long : BaseX=rc.left
        Dim BaseY As Long : BaseY=rc.top
        For i=0 To ELM(3)
            SetPixel(hdc,BaseX+6+i,BaseY+9+i,MenuColor_Text)
            SetPixel(hdc,BaseX+6+i,BaseY+10+i,MenuColor_Text)
        Next
        For i=0 To ELM(4)
            SetPixel(hdc,BaseX+9+i,BaseY+10-i,MenuColor_Text)
            SetPixel(hdc,BaseX+9+i,BaseY+11-i,MenuColor_Text)
        Next
    Else
        If ppobj_MenuItemData[item_index]->hIcon Then
            'アイコンを描画
            Dim hIcon As HICON
            If mii.fState and MFS_DISABLED Then
                hIcon=ppobj_MenuItemData[item_index]->hGrayIcon
            Else
                hIcon=ppobj_MenuItemData[item_index]->hIcon
            End If

            DrawIconEx(hdc,ref_rc.left+1,
                ref_rc.top+3,hIcon,16,16,0,NULL,DI_NORMAL)
        End If
    End If

    'メイン文字列とショートカットキー文字列を分別
    Dim szMain[255] As Byte,szShortcut[255] As Byte
    i=0
    Do
        If ppobj_MenuItemData[item_index]->str[i]=Asc(Ex"\t") Then
            szMain[i]=0

            lstrcpy(szShortcut,ppobj_MenuItemData[item_index]->str+i+1)
            Exit Do
        End If
        szMain[i]=ppobj_MenuItemData[item_index]->str[i]
        If ppobj_MenuItemData[item_index]->str[i]=0 Then
            szShortcut[0]=0
            Exit Do
        End If
        i++
    Loop

    rc=ref_rc
    rc.left=ref_rc.left+26
    rc.right=ref_rc.right

    Dim hOldFont As HFONT
    hOldFont=SelectObject(hdc,m_hMenuFont)

    If mii.fState and MFS_DISABLED Then
        '淡色カラー
        TextColor=MenuColor_Gray_Text
    Else
        '通常カラー
        TextColor=MenuColor_Text
    End If

    SetBkMode(hdc, TRANSPARENT)
    SetTextColor(hdc, TextColor)

    DrawText(hdc,szMain,-1,rc,DT_VCENTER or DT_LEFT or DT_SINGLELINE)

    If szShortcut[0] Then
        rc.left=rc.right-45
        DrawText(hdc,szShortcut,-1,rc,
            DT_VCENTER or DT_LEFT or DT_SINGLELINE)
    End If

    SelectObject(hdc,hOldFont)
End Sub

次のページ
CMenuItemDataクラス

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

デザイン性に優れた拡張メニューコントロール連載記事一覧

もっと読む

この記事の著者

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

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

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

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

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/514 2008/08/26 13:40

イベント

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

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

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

メールバックナンバー