2013年12月19日

WSCheckBox

DXRubyWSでコントロールを作ろう第二段! 今回はチェックボックスを作ってみました。

    # クリックされた場合真偽値を入れ替える
    def on_click(tx, ty)
      @data = !@data
      super
    end 

trueとfalseを入れ替えるだけなので処理の実体はこの四行だけです。
他は全部描画関係です。
説明以上!

以下ソース


# coding: utf-8

module WS
  class WSCheckBox < WSControl
    
    # 定数
    C_CBBASE  = [255,255,255]
    C_CBLINES = [80,80,80]
    C_CBLINEM = [160,160,160]
    C_CBLINEH = [240,240,240]
    C_CHECK   = [  0,  0,  0]
    
    # 公開インスタンス
    attr_accessor :caption, :fore_color, :data

    # Mix-In
    include Focusable
    include ButtonClickable
    
    def initialize(tx, ty, width, caption = "")
      super(tx, ty, width, 16)
      @image      = {}
      @data       = false
      @caption    = caption
      @fore_color = C_BLACK
      # 画像を作成する
      set_image
    end
    
    # チェックボックスの画像を設定
    def set_image
      sx = 1
      sy = 1
      ex = 14
      ey = 14
      @image[false] = Image.new(16, 16)
                     .line(sx, sy, sx, ey, C_CBLINES)
                     .line(sx, sy+1 , ex, sy+1 , C_CBLINES)
                     .line(sx, sy , ex, sy, C_CBLINES)
                     .line(sx+1, sy ,sx+1, ey, C_CBLINES)
                     .line(sx+1,ey-1,ex-1,ey-1, C_CBLINEM)
                     .line(sx, ey, ex, ey, C_CBLINEH)
                     .line(ex-1, sy+1, ex-1, ey-1, C_CBLINEM)
                     .line(ex, sy, ex, ey, C_CBLINEH)
                     .box_fill(sx+2, sy+2, ex-2, ey-2, C_CBBASE)
                     
      @image[true] = Image.new(16, 16)
                     .line(sx, sy, sx, ey, C_CBLINES)
                     .line(sx, sy+1 , ex, sy+1 , C_CBLINES)
                     .line(sx, sy , ex, sy, C_CBLINES)
                     .line(sx+1, sy ,sx+1, ey, C_CBLINES)
                     .line(sx+1,ey-1,ex-1,ey-1, C_CBLINEM)
                     .line(sx, ey, ex, ey, C_CBLINEH)
                     .line(ex-1, sy+1, ex-1, ey-1, C_CBLINEM)
                     .line(ex, sy, ex, ey, C_CBLINEH)
                     .box_fill(sx+2, sy+2, ex-2, ey-2, C_CBBASE)
                     .line(sx+3, ey-5, sx+5, ey-3, C_CHECK)
                     .line(sx+3, ey-6, sx+5, ey-4, C_CHECK)
                     .line(sx+3, ey-7, sx+5, ey-5, C_CHECK)
                     .line(sx+3, ey-8, sx+5, ey-6, C_CHECK)
                     .line(sx+6, ey-4, ex-3, ey-8, C_CHECK)
                     .line(sx+6, ey-5, ex-3, ey-9, C_CHECK)
                     .line(sx+6, ey-6, ex-3, ey-10, C_CHECK)
                     .line(sx+6, ey-7, ex-3, ey-11, C_CHECK)
    end
    
    ### イベント ###
    # クリックされた場合真偽値を入れ替える
    def on_click(tx, ty)
      @data = !@data
      super
    end 

    ### 描画 ###    
    def draw
      # コントロールの状態を参照して画像を変更
      self.image = @image[@data]
      super
      # キャプションを描画
      if @caption.length > 0
        width = @font.get_width(@caption)
        self.target.draw_font(self.x + 20 , self.y , @caption, @font, :color=>@fore_color)
      end
    end
  end
end
 


posted by 鳴海つかさ at 20:18| Comment(0) | TrackBack(0) | DXRuby

2013年12月15日

数値入力コントロールの続き アレンジ版

昨日の続きです。

数値入力コントロールを改造してみました。

主な変更点としては

数値入力の最大値、最小値の設定を追加
ボタンを押した時のステップ値の設定を追加
スピンボタンを四つに増やし押した時の増減量に大小をつけました

新規キャンバス.jpg

後はコードを少し整理しました。

昨日書いたアレンジ案そのままですね。

こういったアレンジが簡単であるのもDXRubyWindowSystemの便利なところですね。



以下実際のコードです。


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

module WS
class WSNumberInput < WSContainer

### 公開インスタンス###
attr_accessor :min, :max, :small, :big

### スピンボタンの定義 ###
class WSSpinButton < WSButtonBase
include Focusable
include RepeatClickable # リピートクリック用モジュール
end

# 初期化
def initialize(tx, ty, width, height)
super(tx, ty, [width, 72].max, [height, 20].max)
@data = 0
@min = 0
@max = 99999
@small = 1
@big = 10
create_controls
set_text
end

# コントロールの作成
def create_controls
# テキストボックスの作成
c_numtext = WSTextBox.new(0, 0, width - 48, height)
# スピンボタン変動小の作成
font_s = Font.new(8)
c_b_add_s = WSSpinButton.new(width - 48, 0, 24, height / 2, "▲")
c_b_add_s.font = font_s
c_b_add_s.add_handler(:click, self.method(:click_add_button_s))
c_b_sub_s = WSSpinButton.new(width - 48, height / 2, 24, height / 2, "▼")
c_b_sub_s.font = font_s
c_b_sub_s.add_handler(:click, self.method(:click_sub_button_s))
# スピンボタン変動大の作成
font_b = Font.new(10)
c_b_add_b = WSSpinButton.new(width - 24, 0, 24, height / 2, "▲")
c_b_add_b.font = font_b
c_b_add_b.add_handler(:click, self.method(:click_add_button_b))
c_b_sub_b = WSSpinButton.new(width - 24, height / 2, 24, height / 2, "▼")
c_b_sub_b.font = font_b
c_b_sub_b.add_handler(:click, self.method(:click_sub_button_b))

# コントロールの登録
add_control(c_numtext, :c_numtext)
add_control(c_b_add_s)
add_control(c_b_sub_s)
add_control(c_b_add_b)
add_control(c_b_sub_b)
end

# 加算ボタン小の押下処理
def click_add_button_s(obj, tx, ty)
@data = [@data + @small, @max].min
set_text
end

# 減算ボタン小の押下処理
def click_sub_button_s(obj, tx, ty)
@data = [@data - @small, @min].max
set_text
end

# 加算ボタン大の押下処理
def click_add_button_b(obj, tx, ty)
@data = [@data + @big, @max].min
set_text
end

# 減算ボタン大の押下処理
def click_sub_button_b(obj, tx, ty)
@data = [@data - @big, @min].max
set_text
end

### スタイル ###
# 限界値の設定

def limit(min , max)
@min = min
@max = max
end

# ステップ値の設定
def step(small , big)
@small = small
@big = big
end

# 更新
def update
super
check_text
end

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

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

end
end
 


posted by 鳴海つかさ at 18:31| Comment(0) | TrackBack(0) | DXRuby

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) | その他