2013年12月14日

DXRuby Advent Calender 15日目 【実践】DXRubyWSでウィジェットを作ろう

DXRuby Advent Calender 15日目です

14日目は音の理屈と実践でした。

あおいたくさんの13日目の記事とあわせてSoundEffectについての理解がふかまった感じです。


DXRubyにはDXRubyWindowSystem(以下WS)というGUIを作る為のコード群があります。
作者であるmirichiさんのGitHubのページで公開されています。

https://github.com/mirichi/DXRubyWS


このWSはDXRubyの描画機能を使っているのでDXRuby上でしか動作しませんが、
実際の動作部分はRubyで記述されているので、機能や見た目を自分でデザインしてコントロールを作ることができます。

この記事では簡単なコントロールを作りながらWSの魅力にせまろうと思います。
WSについてすべてを解説することは難しいので必要な部分のみ解説をいれますので、詳しいことは公式ページのリファレンスに目を通していただくことになります。


今回はせっかくなので公式ページにないものを作りましょう。
数値入力ボックスがGUIプログラムでは利用頻度が高いと思われるのでこれを作っていきましょう。

まずは、数値入力ボックスに必要な機能を考えます。

・数値を入力するテキスト領域
・押すと数値が変動するスピンボタン

この二つはそれぞれコントロールとしてWSに存在します。
一から作るよりこれを利用するのがよさそうです。


では、実際に作っていくわけですが、まず何をすればいいのか…
WSにはWSControlとWSContainerという二つのクラスが定義されています。
コントロール処理の主要な部分はここに記述されているので、これらを継承だけでコントロールの下地ができてしまいます。

ところで親クラスが二つありますがどちらを使うべきなのでしょう?
WSControlは単一のコントロールですが、WSContainerは自身もコントロールであり、かつ配下に他のコントロールを持つことができるという違いがあります。

数値入力ボックスはテキストボックスとボタンの組み合わせで構成されているので、今回はそれらを格納できるWSContainerを継承して使いましょう。


それでは、実際に外枠だけざっくりと作ってしまいましょう。
numberinput.rbなどの適当な名前にして、standardGUIフォルダにrbファイルを作りましょう。


WSのボタンとテキストボックスを使いたいので、

require_relative 'button'
require_relative 'textbox'

まずはこの二つをrequireします。

次にWScontainerを継承しWSNumberInputクラスを作ります。

module WS
class WSNumberInput < WSContainer

end
end

では、この中に必要な処理をどんどん書いて行きましょう。

# 初期化
def initialize(tx, ty, width, height)
super(tx, ty, [width, 48].max, [height, 20].max)
@data = 0
create_controls
set_text
end

まずは初期化処理。
座標と、幅を受け取ってWSContainarに渡します。
数値入力ボックスなので数値を保持しておくインスタンス@dataを作って初期化しておきます。

配下のコントロールを作るcreate_controlsと、
数値をテキスト化してテキストボックスに渡すset_textを呼び出します。

# テキストボックスにテキストを設定
def set_text
@c_numtext.text = @data.to_s
end


# コントロールの作成
def create_controls
# テキストボックスの作成
@c_numtext = WSTextBox.new(0, 0, width - 24, height)
# スピンボタンの作成
font = Font.new(10)
@c_button_add = WSButton.new(width - 24, 0, 24, height / 2, "▲")
@c_button_add.font = font
@c_button_add.add_handler(:click, self.method(:click_add_button))
@c_button_sub = WSButton.new(width - 24, height / 2, 24, height / 2, "▼")
@c_button_sub.font = font
@c_button_sub.add_handler(:click, self.method(:click_sub_button))
# コントロールの登録
add_control(@c_numtext)
add_control(@c_button_add)
add_control(@c_button_sub)
end

子コントロールとしてテキストボックスとスピンボタンを作って、数値入力ボックスの配下のコントロールとして登録します。

WSContainer#add_control(ctl, name=nil)

ctlに登録したいコントロールを渡してこのメソッドを呼べば子コントロールとして登録することができます。
nameにSymbolを渡すとそのコントロールを呼び出す特異メソッドを追加してくれます。
自分は好みでコントロールをインスタンスにいれてますが、こういった方法も用意されています。

作成したボタンはそのままでは動作しないのでハンドラを利用して処理を追加します。

WSControl#add_handler(signal, obj=nil, &block)

signalはコントロールに対するアクションをSymbolで指定します。
objにはmethodオブジェクトを渡します。

ボタンをクリックした時の処理を指定したいので、signalには:clickを指定します。

@c_button_add.add_handler(:click, self.method(:click_add_button))
@c_button_sub.add_handler(:click, self.method(:click_sub_button))

それぞれ加算ボタン減算ボタンを押した時の処理をボタンに追加しています。

さて肝心のボタンクリック時の処理がないので次はこれを作りましょう。

# 加算ボタンの押下処理
def click_add_button(obj, tx, ty)
@data += 1
set_text
end

# 減算ボタンの押下処理
def click_sub_button(obj, tx, ty)
@data -= 1
set_text
end

押されたときに数値を1増やす、あるいは減らしてテキストを更新するだけの簡単な処理ですね。

これで数値入力の基本的な機能は完成しました。

しかし実際に動かしてみるとテキスト入力時に問題が発生します。
テキストボックスには数字以外の文字も入力できてしまうため、これを検出し数値のみの状態を維持する処理を加えましょう。

# テキストに数字以外のものが入っていないかをチェックし、データに反映する
def check_text
num_text = @c_numtext.text
if num_text != "0" && num_text.to_i == 0 || num_text != num_text.to_i.to_s
@c_numtext.text = @data.to_s
end
@data = @c_numtext.text.to_i
end

あまりキレイではないですがとりあえず動けばいいということで――
テキストボックスに不正な値が入っている場合、変更前の@dataに置き換えます。
正常な値が入っている場合はその値を@dataに格納します。
これを毎フレーム呼び出すか、あるいはテキストボックスの内容を監視して比較して変更があった場合呼び出すなどしてテキストボックスに数値だけが入るようになります。

(恐らく)正常に動作する数値入力ボックスが完成しました。

ちなみにこれは基本なのでさらに作り込んだり、アレンジを加えるのもよいでしょう。

数値の最大値や最小値、ステップ値の設定。
入力値の限界に達した時に数値をループさせる設定。
スピンボタンを1変動させるものだけでなく10変動させるものも作ったり、正と負をワンタッチで切り替えるボタンをつけるなどなど自分なりのアレンジもできます。

駆け足でしたが、このようにWSを使えばコントロールも比較的簡単につくることができます。
WSで自分だけのウィジェットやゲーム用のコントロールを作って素敵なDXRubyライフを!

最後にここまでのコードのまとめ
DXRuby開発版1.5.7用 DXRubyWSの最新版が必要

# coding: utf-8
require_relative 'button'
require_relative 'textbox'

module WS
class WSNumberInput < WSContainer

attr_accessor :data

# 初期化
def initialize(tx, ty, width, height, style = {})
super(tx, ty, [width, 48].max, [height, 20].max)
@data = 0
create_controls
set_text
end

# コントロールの作成
def create_controls
# テキストボックスの作成
@c_numtext = WSTextBox.new(0, 0, width - 24, height)
# スピンボタンの作成
font = Font.new(10)
@c_button_add = WSButton.new(width - 24, 0, 24, height / 2, "▲")
@c_button_add.font = font
@c_button_add.add_handler(:click, self.method(:click_add_button))
@c_button_sub = WSButton.new(width - 24, height / 2, 24, height / 2, "▼")
@c_button_sub.font = font
@c_button_sub.add_handler(:click, self.method(:click_sub_button))
# コントロールの登録
add_control(@c_numtext)
add_control(@c_button_add)
add_control(@c_button_sub)
end

# 加算ボタンの押下処理
def click_add_button(obj, tx, ty)
@data += 1
set_text
end

# 減算ボタンの押下処理
def click_sub_button(obj, tx, ty)
@data -= 1
set_text
end

# 更新
def update
super
check_text
end

# テキストに数字以外のものが入っていないかをチェックし、データに反映する
def check_text
num_text = @c_numtext.text
if num_text != "0" && num_text.to_i == 0 || num_text != num_text.to_i.to_s
@c_numtext.text = @data.to_s
end
@data = @c_numtext.text.to_i
end

# テキストボックスにテキストを設定
def set_text
@c_numtext.text = @data.to_s
end
end
end

以上急ごしらえですが何かの参考になれば幸いです。
posted by 鳴海つかさ at 13:00| Comment(0) | TrackBack(0) | その他
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

この記事へのトラックバックURL
http://blog.sakura.ne.jp/tb/82574977

この記事へのトラックバック