Ruby/Gtk2 でカスタムウィジェットを作る

長いあいだ Ruby/Gtk2 でカスタムウィジェットの作りかたがよくわからなかったけど、今日 rbbr を読んでいてやりかたがわかった。クラス定義の中で GLib::Object.type_register を呼び出して GObject システムに型を登録してから、GLib::MetaInterface.signal_new でシグナルを登録するらしい。

ためしに、2つあるトグルボタンを両方とも押し込まないと状態が ON にならない DualButton ウィジェットクラスを作ってみた。

# -*- coding: utf-8 -*-
require 'gtk2'

include Gtk

class DualButton < HButtonBox
  type_register

  signal_new("toggled", GLib::Signal::ACTION, nil, GLib::Type["void"])

  def initialize 
    super
    self.spacing = 10

    @active = false

    @first_button = ToggleButton.new("")
    @second_button = ToggleButton.new("")

    pack_start(@first_button)
    pack_start(@second_button)

    [@first_button, @second_button].each do |button|
      button.signal_connect('toggled') do
        recalc_state
      end
    end
  end

  def recalc_state
    state = @first_button.active? && @second_button.active?
    if state != @active
      @active = state
      signal_emit('toggled')
    end
  end

  def active?
    @active
  end
end

見ため的にはボタンを左右に 2 つならべただけのウィジェットなので HButtonBox を継承する。両方のトグルボタンの状態を監視して、条件が揃ったら自身が toggled シグナルを送出する。on/off の状態は active? メソッドで公開している。

def main
  window = Window.new
  window.border_width = 20

  dual_button = DualButton.new
  dual_button.signal_connect('toggled') do
    puts "dual button #{dual_button.active? ? 'on' : 'off'}"
  end

  window.add dual_button

  window.signal_connect('destroy') { Gtk.main_quit }

  window.show_all
  Gtk.main

  0
end

exit main

クライアント部分は、DualButton の状態が変化したら端末に on/off を出力するというものだ。

左を押し下げて、右も下げると on になる。

ここで右を上げると off になって、さらに左を上げても off のままなのでシグナルは送出されない。

これで UI クラスの間での通信は GTK っぽくシグナルを使うことができるようになった。