(75) ボタンを押してみた

せっかく画像の表示方法に整理がついたので、ボタンを表示して押せるプログラムを試しに作ってみることにしました。
ただの四角形をボタンに見立てるのもつまらないので、私なりに気合を入れてボタンっぽい画像を用意してみたり。

 

色の淡い方を押していない時の、濃い方を押した時のつもりで。

これらの画像をインターネット経由で取得できるようにサーバに置いたら準備完了です。


まずは、この画像を取り込んで表示させるだけのコードを書きます。

GraphicsWindow.Width=320
GraphicsWindow.Height=200
GraphicsWindow.CanResize="False"
button["off"]=ImageList.LoadImage("http://bit.ly/dygOB7")
GraphicsWindow.DrawImage(button["off"],20,20)

image

GraphicsWindow.CanResize は、使用者がウィンドウをリサイズできないように追加しました。Small Basic では、True / False は文字列で指定します。

マウスボタンを押した時に何か動作をさせるには、その動作を記述したサブルーチンを用意します。

Sub MouseDownEvent
  GraphicsWindow.DrawImage(button["on"],20,20)
EndSub

で、マウスボタンを押した時にこのサブルーチンを実行するよう、GraphicsWindow.MouseDown イベントに MouseDownEvent サブルーチンを設定します。

GraphicsWindow.MouseDown=MouseDownEvent

押した時用の濃い色のボタン画像の読み込みも必要ですね。

button["on"]=ImageList.LoadImage("http://bit.ly/beStHm")

これらを書き足すと、ソース全体は以下のようになります。

GraphicsWindow.Width=320
GraphicsWindow.Height=200
GraphicsWindow.CanResize="False"
GraphicsWindow.MouseDown=MouseDownEvent

button["off"]=ImageList.LoadImage("http://bit.ly/dygOB7")
button["on"]=ImageList.LoadImage("http://bit.ly/beStHm")

GraphicsWindow.DrawImage(button["off"],20,20)

'--- 以下サブルーチン ---

Sub MouseDownEvent
  GraphicsWindow.DrawImage(button["on"],20,20)
EndSub

メインルーチンの終りには、何も書く必要はありません。起動時には、Sub ~ EndSub で囲まれたロジックは読み飛ばされます。
下手に「Program.End()」とか書くとハングしたりします。Small Basic はまだ v0.8 で正式版ではないようで、想定外っぽい使い方をするとばんばんハングしますのでお気をつけください。

で、実行結果。

image image

マウスボタンを押すと光るわけですが。
悲しいかな、ウィンドウ上の白い部分をクリックしてもボタンは光ります。座標判定をしていないので当然っちゃ当然の動作ではあります。

ので、クリックされた位置がボタン上かどうかの判定ロジックも追加してみます。

Sub MouseDownEvent
  If GraphicsWindow.MouseX>=20 And GraphicsWindow.MouseX<20+41 And GraphicsWindow.MouseY>=20 And GraphicsWindow.MouseY<20+41 Then
    GraphicsWindow.DrawImage(button["on"],20,20)
  EndIf
EndSub

…な、長ぇ。
Small Basic には命令文を途中で折り返す文法が用意されていませんので、If から Then までは一気に記述する必要があります。
判断条件をひとつずつに分解して If のネスト ( 入れ子 ) にする手もあるんですが、あまりネストが深くなるのもいかがなものかということで、ここでは長ーい一文にしてあります。

マウスクリックの位置による動作の違いはキャプチャではお見せできないので、画像は割愛します。

プログラム全体は、以下のようになります。

GraphicsWindow.Width=320
GraphicsWindow.Height=200
GraphicsWindow.CanResize="False"
GraphicsWindow.MouseDown=MouseDownEvent

button["off"]=ImageList.LoadImage("http://bit.ly/dygOB7")
button["on"]=ImageList.LoadImage("http://bit.ly/beStHm")

GraphicsWindow.DrawImage(button["off"],20,20)

'--- 以下サブルーチン ---

Sub MouseDownEvent
  If GraphicsWindow.MouseX>=20 And GraphicsWindow.MouseX<20+41 And GraphicsWindow.MouseY>=20 And GraphicsWindow.MouseY<20+41 Then
    GraphicsWindow.DrawImage(button["on"],20,20)
  EndIf
EndSub

Web ページで見たい場合はhttp://smallbasic.com/program/?DRR192 へジャンプ、インポートする場合は DRR192 を指定してください。


これだけでは、一度押したら、あと何もできなくなります。
ので、今度は「マウスボタンを離したらボタンを元に戻す」ロジックも追加します。

Sub MouseUpEvent
  GraphicsWindow.DrawImage(button["off"],20,20)
EndSub

MouseUp イベント用サブルーチンでは、座標判定を行いません。
座標判定を行うと、マウスボタンを押し下げ → ボタン領域外へドラッグ → ボタンを離すという操作をされた場合にボタンが光ったままになってしまうからです。
押し下げた瞬間の状況を基準にまとめると、処理の取りこぼしが起きにくくなります。

MouseUp イベントに上記サブルーチンを設定したロジックも加えたプログラム全体は、以下のようになります。

( ZVJ871 )

GraphicsWindow.Width=320
GraphicsWindow.Height=200
GraphicsWindow.CanResize="False"
GraphicsWindow.MouseDown=MouseDownEvent
GraphicsWindow.MouseUp=MouseUpEvent
button["off"]=ImageList.LoadImage("http://bit.ly/dygOB7")
button["on"]=ImageList.LoadImage("http://bit.ly/beStHm")

GraphicsWindow.DrawImage(button["off"],20,20)

'--- 以下サブルーチン ---
Sub MouseDownEvent
  If GraphicsWindow.MouseX>=20 And GraphicsWindow.MouseX<20+41 And GraphicsWindow.MouseY>=20 And GraphicsWindow.MouseY<20+41 Then
    GraphicsWindow.DrawImage(button["on"],20,20)
  EndIf
EndSub

Sub MouseUpEvent
  GraphicsWindow.DrawImage(button["off"],20,20)
EndSub

ここまで動作が確認できたら、1 回仕切り直して、今度はボタンを 3 × 3 のマス目状に並べてみることにします。

まず、メインルーチンを以下のように書き直します。

'初期処理
Init()

'イベントルーチンの関連付け
GraphicsWindow.MouseDown=MouseDownEvent
GraphicsWindow.MouseUp=MouseUpEvent

画面の初期処理とか各種パラメータの初期化が長ったらしくなりそうなので、初期処理をまとめてサブルーチンに切り出した形です。

初期処理用のサブルーチン Init() では、最初にグラフィックウィンドウの大きさを固定にします。

'GraphicsWindowの初期化
GraphicsWindow.Width=320          'ウィンドウの横幅
GraphicsWindow.Height=200         'ウィンドウの高さ
GraphicsWindow.CanResize="False"  'ウィンドウのリサイズ不可

今後機能を増やしていくと、この初期処理にどんどん時間がかかっていくことになりそうですので、「初期設定中…」という文章をいったん表示させることにします。

'初期処理中メッセージ
GraphicsWindow.FontSize=24
GraphicsWindow.FontName="MS ゴシック"
GraphicsWindow.DrawText(50,70,"初期設定中…")

画像をインターネットの向こう側から取得します。

'画像の取り込み
button["off"]=ImageList.LoadImage("http://bit.ly/dygOB7")
button["on"]=ImageList.LoadImage("http://bit.ly/beStHm")

ボタン画像は、41 × 41 ドットの大きさを持ちますが、上記のコードのように直接数字で記述してしまうと何の値なのかわからなくなるので、意味のわかりやすい名前をつけた変数にいったん格納することにします。
今回はボタン間の隙間、ボタンパネル全体の左上座標も指定しますので、同じように変数に格納します。

'座標パラメータの設定
button["size"]["normal"]=41
margin["grid"]=3                  'ボタン間のマージン
margin["left"]=95                 'パネル左上の横位置
margin["top"]=30                  'パネル左上の縦位置

ここまでできたら、あとはボタンを描画するだけですので、ウィンドウ全体の背景色を指定してからウィンドウをクリアします。これでさきほど表示した「初期設定中…」の表示が消えます。

'初期画像を背景に描画
GraphicsWindow.BackgroundColor=GraphicsWindow.GetColorFromRGB(192,192,192)
GraphicsWindow.Clear()

ボタンの描画そのものは、上記で設定した座標パタメータを使って、x 方向 ( 横方向 ) と y 方向 ( 縦方向 ) の 2 重ループで回してやれば OK です。

For x=0 To 2
  btnLeft=margin["left"]+(button["size"]["normal"]+margin["grid"])*x
  For y=0 To 2
    btnTop=margin["top"]+(button["size"]["normal"]+margin["grid"])*y
    GraphicsWindow.DrawImage(button["off"],btnLeft,btnTop)
  EndFor
EndFor

以上をまとめると、初期処理用のサブルーチン全体としては以下のようなコードになります。

'初期処理
Sub Init

  'GraphicsWindowの初期化
  GraphicsWindow.Width=320          'ウィンドウの横幅
  GraphicsWindow.Height=200         'ウィンドウの高さ
  GraphicsWindow.CanResize="False"  'ウィンドウのリサイズ不可

  '初期処理中メッセージ
  GraphicsWindow.FontSize=24
  GraphicsWindow.FontName="MS ゴシック"
  GraphicsWindow.DrawText(50,70,"初期設定中…")

  '画像の取り込み
  button["off"]=ImageList.LoadImage("http://bit.ly/dygOB7")
  button["on"]=ImageList.LoadImage("http://bit.ly/beStHm")

  '座標パラメータの設定
  button["size"]["normal"]=41
  margin["grid"]=3                  'ボタン間のマージン
  margin["left"]=95                 'パネル左上の横位置
  margin["top"]=30                  'パネル左上の縦位置

  '初期画像を背景に描画
  GraphicsWindow.BackgroundColor=GraphicsWindow.GetColorFromRGB(192,192,192)
  GraphicsWindow.Clear()
  For x=0 To 2
    btnLeft=margin["left"]+(button["size"]["normal"]+margin["grid"])*x
    For y=0 To 2
      btnTop=margin["top"]+(button["size"]["normal"]+margin["grid"])*y
      GraphicsWindow.DrawImage(button["off"],btnLeft,btnTop)
    EndFor
  EndFor

EndSub

さて、次はクリック座標からボタン画像の描画位置を算出するロジックです。
先ほどとは違い判定するボタンが 9 つありますので、ひとつずつ座標範囲を判定条件とする If 文を書くのはわずらわしいです。
ので、まずは x 方向だけを算出します。

ボタンごとの座標の増分値を

mgn=button["size"]["normal"]+margin["grid"]

と先に算出しておいてから、

'マウスクリックの x 座標から描画座標を算出
btnLeft=-1
If GraphicsWindow.MouseX>=margin["left"] And GraphicsWindow.MouseX+button["size"]["normal"] Then
  btnLeft=margin["left"]
ElseIf GraphicsWindow.MouseX>=margin["left"]+mgn And GraphicsWindow.MouseX+mgn+button["size"]["normal"] Then
  btnLeft=margin["left"]+mgn
ElseIf GraphicsWindow.MouseX>=margin["left"]+mgn*2 And GraphicsWindow.MouseX+mgn*2+button["size"]["normal"] Then
  btnLeft=margin["left"]+mgn*2
EndIf

まあこんな感じで。
Small Basic の予約語の文字数が長いのと、こちらで用意した変数もわかりやすくをモットーに名称をつけたのとで、妙に 1 行が長くなってしまっていますがまあ気にしない。
ボタンとボタンの隙間や、ボタンのない領域をクリックした場合には、-1 を算出するようにしています。

同様に、y 方向も算出します。

'マウスクリックの y 座標から描画座標を算出
btnTop=-1
If GraphicsWindow.MouseY>=margin["top"] And GraphicsWindow.MouseY+button["size"]["normal"] Then
  btnTop=margin["top"]
ElseIf GraphicsWindow.MouseY>=margin["top"]+mgn And GraphicsWindow.MouseY+mgn+button["size"]["normal"] Then
  btnTop=margin["top"]+mgn
ElseIf GraphicsWindow.MouseY>=margin["top"]+mgn*2 And GraphicsWindow.MouseY+mgn*2+button["size"]["normal"] Then
  btnTop=margin["top"]+mgn*2
EndIf

描画すべきボタン画像の座標位置は、変数 btnLeft と btnTop に格納されます。Small Basic では戻り値という考え方はなく、変数はすべてコード内グローバルですので、自分で「この変数の値を変えるのはこのサブルーチン」と限定しておかないと、あっというまに昔懐かしいスパゲティプログラムができあがりますので充分な注意が必要です。

このへんの注意事項を冒頭コメントに記載してできあがった描画座標算出のサブルーチン GetButtonLocation の全体は、以下のようなコードになります。

'クリック座標からボタン左上座標を算出
'       戻り値: 描画座標(btnLeft,btnTop)
Sub GetButtonLocation

  mgn=button["size"]["normal"]+margin["grid"]

  'マウスクリックの x 座標から描画座標を算出
  btnLeft=-1
  If GraphicsWindow.MouseX>=margin["left"] And GraphicsWindow.MouseX+button["size"]["normal"] Then
    btnLeft=margin["left"]
  ElseIf GraphicsWindow.MouseX>=margin["left"]+mgn And GraphicsWindow.MouseX+mgn+button["size"]["normal"] Then
    btnLeft=margin["left"]+mgn
  ElseIf GraphicsWindow.MouseX>=margin["left"]+mgn*2 And GraphicsWindow.MouseX+mgn*2+button["size"]["normal"] Then
    btnLeft=margin["left"]+mgn*2
  EndIf

  'マウスクリックの y 座標から描画座標を算出
  btnTop=-1
  If GraphicsWindow.MouseY>=margin["top"] And GraphicsWindow.MouseY+button["size"]["normal"] Then
    btnTop=margin["top"]
  ElseIf GraphicsWindow.MouseY>=margin["top"]+mgn And GraphicsWindow.MouseY+mgn+button["size"]["normal"] Then
    btnTop=margin["top"]+mgn
  ElseIf GraphicsWindow.MouseY>=margin["top"]+mgn*2 And GraphicsWindow.MouseY+mgn*2+button["size"]["normal"] Then
    btnTop=margin["top"]+mgn*2
  EndIf

EndSub

ここまでできれば、.MouseDown イベント用サブルーチン・.MouseUp イベント用サブルーチンを記述します。

.MouseDown イベントの場合は、上記でこしらえた GetButtonLocation サブルーチンを呼び出して変数 btnLeft と btnTop に算出した描画座標を格納してから、どちらも -1 (領域外) ではないことを確認してから「On」画像を表示させます。

'マウスボタンを押された時の処理
Sub MouseDownEvent

  'クリック座標から、ボタンのインデックスと描画位置を算出
  GetButtonLocation()

  '算出結果がボタン上であれば、「On」画像を表示
  If btnLeft<>-1 and btnTop<>-1 then
    GraphicsWindow.DrawImage(button["on"],btnLeft,btnTop)
  EndIf

EndSub

.MouseUp イベントの方は、GetButtonLocation サブルーチンを呼び出しません。
ということは、 変数 btnLeft と btnTop には .MouseDown イベントで算出 → 「On」画像を描画した時の座標がそのまま残っているということになります。

ので、素直に変数 btnLeft と btnTop が指し示す座標に「Off」画像を描画します。
おっと、どちらかが -1 の場合は .MouseDown イベントで「On」画像が描画されていないことになりますので、「Off」画像の再描画も行わないようにします。

'マウスボタンを離された時の処理
Sub MouseUpEvent

  'マウスボタンを押された時に「On」画像を表示していれば「Off」画像に戻す
  If btnLeft<>-1 and btnTop<>-1 then
    GraphicsWindow.DrawImage(button["off"],btnLeft,btnTop)
  EndIf

EndSub

以上ですべてのコードを記述し終わりました。
さずがに「全体はこうです」と提示するにはコードが少々長いので、ご覧になりたい方はインポートまたは Web ページで確認してみてください。

( NWF899 )

image image

1 コメント

  1. さるべーじ より:

    Web ページで実行した場合、最初の 1 回目をすばやくクリックするとボタンが光りっぱなしになりますね…。
    やっぱ Silverlight を考慮すると、Draw ~ 系は使わない方がいいかも。

コメントを投稿