JRuby+SWTでブラウザができてました

ちょっと前からOperaがネットワークに継がらなくなって、全く作業の捗らないあらっきぃです。
この記事はちょろめで書いてます。

Windowsにpikを導入して、JRubyで遊んでいたのですが、気がついたらSWTでブラウザを作っていました。
何を言ってるのかよく分かりませんが僕も分かりません。冬休みのありに余った時間を弄んでいたようです。

最初に、スクショでも、

![20131222151839.png](https://qiita-image-store.s3.amazonaws.com/0/11219/2d80a669-9a1b-94b6-81f1-58c5c1540f63.png)

SWTのブラウザウィジェットはWindowsだとIEコンポーネントなので、Flashがあれば艦これがプレイできるんですね。
これはJavaFXにない強みです。(何かが間違ってる)

```rb:browser.rb
# coding : UTF-8

#SWTの読み込み(同じフォルダにswt.jarを置いといてください)
require 'swt.jar'

#ウィンドウのタイトルとホームページのURL(お好みでどうぞ)
title = "SWT Simple Browser on JRuby"
homepage = "http://google.com/"

#SWT関連のパッケージの読み込み
swt      = Java::OrgEclipseSwt
graphics = Java::OrgEclipseSwtGraphics
layout   = Java::OrgEclipseSwtLayout
widgets  = Java::OrgEclipseSwtWidgets
browser  = Java::OrgEclipseSwtBrowser

#定数をswt::SWT::NULLとかするのが面倒なので、
SWT = swt::SWT
GridData = layout::GridData

#ロケーションバーを作る(クラスに纏めるのが面倒だったからlambdaにしちゃったけど非常にアレ)
setup_locationbar = ->(shell) do
  #ロケーションバーのウィジェット達を纏めるComposite
  composite = widgets::Composite.new shell, SWT::NULL
  composite.layout = layout::GridLayout.new 4, false
  composite.layout_data = GridData.new GridData::FILL_HORIZONTAL

  #戻るボタン
  back = widgets::Button.new composite, SWT::NULL
  back.text = "←"
  #クリックされたときの動作を登録
  back.add_selection_listener do |e|
    @browser.back if @browser.back_enabled?
  end

  #進むボタン
  forward = widgets::Button.new composite, SWT::NULL
  forward.text = "→"
  forward.add_selection_listener do |e|
    @browser.forward if @browser.forward_enabled?
  end

  #更新ボタン
  update = widgets::Button.new composite, SWT::NULL
  update.text = "↺" #環境によっては化けそうな文字だな…
  update.add_selection_listener do |e|
    #これも非常にアレ
    case update.text
    when "↺"
      @browser.set_url @location.text
    when "×"
      @browser.stop
    end
  end
  
  #URLが表示されてるテキストバー
  location = widgets::Text.new composite, SWT::SINGLE | SWT::BORDER
  location.layoutData = GridData.new GridData::FILL_BOTH

  return back, forward, update, location
end

#ブラウザを作る。(上に同じく、非常にアレ)
setup_browser = ->(shell) do
  browser = browser::Browser.new shell, SWT::NONE
  #setUrlがurl=の形式で呼べないのは、複数引数でオーバーライドされているからか、返り値があるから(どっちか分からないけど、多分どちらか)
  browser.set_url homepage
  browser.layoutData = GridData.new GridData::FILL_BOTH

  #ドキュメントのタイトルが変わったとき
  browser.add_title_listener do |e|
    shell.text = "#{e.title} - #{title}"
  end
  
  #URLが変わったときに実行される
  browser.add_location_listener do |e|
    #ツールバーを更新
    @location.text = "#{e.location}"
    @back.enabled = browser.back_enabled?
    @forward.enabled = browser.forward_enabled?
    
    #changedとchanfingという二つのメソッドを持っているLocationListenerを一つのProcで実装しているので、
    #callerで取得したメソッド名を元に分岐する
    case caller.first.match(/`(.*)'/)[1].to_sym
    when :changing
      @update.text = "×"
      shell.text = "#{e.location} - #{title}"
    when :changed
      @update.text = "↺"
      @update.enabled = true
    end
  end

  browser
end

#ここら辺はSWTプログラミングのテンプレートみたいなものだと思う
display = widgets::Display.new
at_exit { display.dispose } #忘れないうちに登録

shell = widgets::Shell.new
shell.text = title
shell.size = graphics::Point.new 800, 600
shell.layout = layout::GridLayout.new 1, false

#全体で使われるウィジェットはなぜかインスタンス変数になってる
@back, @forward,@update, @location = setup_locationbar.call shell
@browser = setup_browser.call shell

shell.open

#イベントループ(よく分かっていない)
until shell.disposed?
  unless display.read_and_dispatch
    display.sleep
  end
end
```

コメントは無駄にたくさんありますが、全体的にとてもアレなんだなー、という雰囲気が伝わってきます。一部の変数がインスタンス変数になっている意味がまるでありませんしね。

しかしまあ、ブロック渡しでインターフェースをimplementsしたときに、複数メソッドあったときの分岐処理なんかは参考になるんじゃないかと思います。というか、それがなかったらこんなわけの分からないコードさらしたくないです。

100行ちょっとでまともな機能のブラウザが作れるんだから、やっぱJRubyとかSWTはすごいなー。OSSは偉大だなー。