マウスでクリックするタイプのボタンのサンプル

Windows のコントロールを使わず、自分で描画する「マウスでクリックするタイプのボタン」のサンプルです。

これ、 Windows コントロールのボタンと同じような挙動をさせようと思ったら案外ややこしくなるんです。ボタンをクリックし始めたかと思うと、そのままドラッグするような動作をした場合...とか。

このスクリプトの特徴は以下の通り。

以前似たような機能をランチャーテンプレで作ったのですが、それにはちょっとした問題がありました。

まず、ボタンにホバーしたままマウスをウィンドウからはみ出させるとホバー状態が保たれたままになってしまう問題です。

ランチャーのボタンでの不具合

これはマウスが動いたことを知らせるメッセージ WM_MOUSEMOVE が、マウスがウィンドウ内でないと送られてこないからでした。しかし、ウィンドウからはみ出たことをメッセージで取得する方法が 2ch に書き込まれていてそこで知りました。

371 :名前は開発中のものです。:2007/08/01(水) 23:40:12 ID:6YU1zlws

>>370

TrackMouseEvent WM_MOUSELEAVE 辺りをググればおk

#uselib "user32.dll" 
#func TrackMouseEvent "TrackMouseEvent" int 

dim tme,4 
tme(0) = 16 
tme(1) = 2 
tme(2) = hwnd 
tme(3) = 0 

oncmd gosub *SUB_WM_MOUSEMOVE,0x200 
oncmd gosub *SUB_WM_MOUSELEAVE,0x02A3 
stop 

*SUB_WM_MOUSEMOVE 
TrackMouseEvent varptr(tme) 
redraw 0:color 255,255,255:boxf:color:pos 320,240:line mousex,mousey:redraw 1 
return 

*SUB_WM_MOUSELEAVE 
redraw 0:color 255,255,255:boxf:color:pos 320,240:mes "外に出たよ":redraw 1 
return

ゲーム製作技術@2ch掲示板 HSP - Hot Soup Processor [15]より引用。マークアップは作者( fujidig )による。

もう一つの不具合は、カーソルを変更しても実際に変更されるのは「次にマウスが動いたとき」と一テンポ遅れることです。これは原因はよく分かりませんでしたが、カーソルを変更直後に mouse 命令を使えば解決できます。HSP3のスクリプトを垂れ流すブログ: ドラッグできる矩形の表示 に掲載されたスクリプトでそのことを知りました。

解決法が分かったので、モジュール変数を使ったスクリプトを書くのを兼ねて、サンプルを作ってみました。

というわけで、前置きが長くなりましたが以下がそのスクリプトです。

// マウスでクリックするタイプのボタンのサンプル

#include "user32.as"

// 定数
#const global TRUE  0 == 0
#const global FALSE 0 != 0
#const NOT_EXIST_BUTTON_INDEX -1

#module mdlButton rect, text

#modinit int _rectLeft, int _rectTop, int _rectRight, int _rectBottom, str _text
    rect = _rectLeft, _rectTop, _rectRight, _rectBottom
    text = _text
    return

// ボタンの描画
#modfunc drawButton int isHover, int isActive
    if isHover == FALSE {
        // 通常
        color 228, 234, 237
    } else : if isActive {
        color 201, 213, 219
    } else {
        color 255, 229, 107
    }
    boxf rect( 0 ), rect( 1 ), rect( 2 ), rect( 3 )
    color
    DrawText hDC, varptr( text ), -1, varptr( rect ), 0x0025  // DT_VCENTER | DT_SINGLELINE | DT_CENTER
    return

// 座標がボタン内にあるか
#modfunc isPtInButton int x, int y
    PtInRect varptr( rect ), x, y
    return ( stat != 0 )

#global

if FALSE { // グローバルで命令/関数を定義
    // ホバーボタンの取得(値が変更されたかを返す)
    #deffunc setHoverButton local isHover, local _hoverButton
        _hoverButton = NOT_EXIST_BUTTON_INDEX
        foreach buttons
            // ホバーしているかの条件
            isPtInButton buttons( cnt ), mouseX, mouseY // マウス座標がボタン内にあること
            isHover = stat
            if isClicking {
                // クリック中である場合は加えて、クリック開始時のホバーボタンと同じボタンであること
                isHover &= ( clickingStartButton == cnt )
            }
            if isHover {
                _hoverButton = cnt
            }
        loop
        if hoverButton != _hoverButton {
            hoverButton = _hoverButton
            return TRUE
        }
        return FALSE
}

// TRACKMOUSEEVENT 構造体
dim tme, 4
tme( 0 ) = 16    // cbSize
tme( 1 ) = 2     // dwFlags = TME_LEAVE
tme( 2 ) = hWnd  // hwndTrack
tme( 3 ) = 0     // dwHoverTime

// ウィンドウメッセージの割り当て
oncmd gosub *onLButtonDown, 0x0201  // WM_LBUTTONDOWN
oncmd gosub *onLButtonUp,   0x0202  // WM_LBUTTONUP
oncmd gosub *onMouseMove,   0x0200  // WM_MOUSEMOVE
oncmd gosub *onMouseLeave,  0x02A3  // WM_MOUSELEAVE

hoverButton = NOT_EXIST_BUTTON_INDEX
isClicking = FALSE
clickingStartButton = NOT_EXIST_BUTTON_INDEX
cursor = 0x7F00 // IDC_ARROW

// ボタンの作成
newmod buttons, mdlButton, 100, 100, 200, 120, "ほげ"
newmod buttons, mdlButton, 100, 130, 200, 150, "ぴよ"
newmod buttons, mdlButton, 100, 160, 200, 180, "ふが"
newmod buttons, mdlButton, 300,  80, 500, 300, "でか"
newmod buttons, mdlButton, 580, 180, 680, 250, "はみ"

gosub *drawScreen

stop

// クリック開始
*onLButtonDown
    isClicking = TRUE
    clickingStartButton = hoverButton
    if hoverButton != NOT_EXIST_BUTTON_INDEX {
        gosub *drawScreen
    }
    return

// クリック終了
*onLButtonUp
    isClicking = FALSE
    if hoverButton != NOT_EXIST_BUTTON_INDEX {
        gosub *drawScreen
        if clickingStartButton == hoverButton {
            dialog str( hoverButton )+"番のボタンを押した"
        }
    }
    return

// ウィンドウ内でのマウスの移動
*onMouseMove
    TrackMouseEvent varptr( tme )    // ウィンドウ外に出たときに通知する
    getkey isClicking, 1
    setHoverButton
    if stat {    // ホバーボタンの値が変更されていたら
        gosub *changeCursor
        gosub *drawScreen
    }
    return

// マウスがウィンドウの外に出た
*onMouseLeave
    if hoverButton != NOT_EXIST_BUTTON_INDEX {
        hoverButton = NOT_EXIST_BUTTON_INDEX // 無条件でホバー中のボタンはなしに
        gosub *changeCursor
        gosub *drawScreen
    }
    return

// 画面の描画
*drawScreen
    redraw 0
    color 128, 128, 128 : boxf
    foreach buttons
        drawButton buttons( cnt ), ( hoverButton == cnt ), isClicking
    loop
    redraw 1
    return

// カーソルの変更
*changeCursor
    if hoverButton == NOT_EXIST_BUTTON_INDEX {
        // IDC_ARROW
        if cursor == 0x7F00 : return
        cursor = 0x7F00
    } else {
        // IDC_HAND
        if cursor == 0x7F89 : return
        cursor = 0x7F89
    }
    LoadCursor 0, cursor
    SetClassLong hWnd, -12, stat
    mouse  // カーソル変更を即座に反映
    return

eseobj/imgbtn - HSP開発wiki の関連リンクにこの記事が載りました。ありがとうございます。

インフォメーション

公開日時
2007年9月25日 午後2時50分8秒
最終更新日時
2007年12月20日 午後7時55分8秒
カテゴリ
HSP