メインコンテンツへスキップ
  1. 記事一覧/

mikutterで使う機能をRuby-GNOMEにコミットした話

·829 文字·2 分· loading · loading ·
mikutter ruby

としぁ氏、この度はご解凍、誠におめでとうございます。思えば苦節4年、硬く閉ざされた世界であっても、Twitter氏との仲を温め、この度のご解凍に至られたことは、誠に感慨深いことでございます。今後もウワァアアアアファボッタアアアアアアされることを切に願っております。

今日は以下のプルリクについて紹介させて頂きます。想定読者はRubyでGTK3を使いたい方になります。

gi: add support for implementing virtual functions in Ruby #1386

gi: fix a bug that virtual functions of grandparent class can’t be implemented #1433

PRのモチベーションとしては、GTK3で導入されたカスタムウィジット・カスタムコンテナをRubyでも使いたいというものになります。カスタムウィジットを使うと、例えばmikutterの MiraclePainter のような独自の描画機構を持ったウィジットが作れます。実際にPRで導入された機能は MiraclePainter で使われています。

MiraclePainterの実装

以下のRubyコードはカスタムウィジットの実装例になります。

require 'gtk3'

Gtk.init

class MyWidget < Gtk::Widget
  type_register

  def initialize
    super

    self.has_window = true
    self.redraw_on_allocate = true
    style_context.add_class 'mywidget'
  end

  def virtual_do_get_request_mode
    Gtk::SizeRequestMode::HEIGHT_FOR_WIDTH
  end

  def virtual_do_get_preferred_width
    [100, 300]
  end

  def virtual_do_get_preferred_height
    get_preferred_height_for_width(preferred_width[0])
  end

  def virtual_do_get_preferred_height_for_width(width)
    min = 100
    h = width.zero? ? min : 9000 / width
    [min, h]
  end

  def signal_do_size_allocate(allocation)
    self.allocation = allocation

    x, y, w, h = allocation.x, allocation.y, allocation.width, allocation.height
    realized? and window.move_resize x, y, w, h
  end

  def signal_do_realize
    x, y, w, h = allocation.x, allocation.y, allocation.width, allocation.height
    attr = Gdk::WindowAttr.new w, h, :input_output, :child
    attr.x = x
    attr.y = y
    attr.visual = visual
    attr.event_mask = events | Gdk::EventMask::EXPOSURE_MASK
    attr.wclass = :input_output

    wat = Gdk::WindowAttributesType
    mask = wat::X | wat::Y | wat::VISUAL

    self.window = Gdk::Window.new parent_window, attr, mask
    register_window window

    self.realized = true
  end

  def signal_do_unrealize
    unregister_window window
    window.destroy
    self.realized = false
  end

  def singla_do_map
    super

    window.show
  end

  def signal_do_unmap
    super

    window.hide
  end

  def signal_do_draw(cr)
    x, y, w, h = allocation.x, allocation.y, allocation.width, allocation.height

    Gtk.render_frame style_context, cr, x, y, w, h
    Gtk.render_background style_context, cr, x, y, w, h
  end
end

provider = Gtk::CssProvider.new
provider.load_from_data <<EOF
.mywidget { border: 1px solid red; }
EOF
screen = Gdk::Screen.default
Gtk::StyleContext.add_provider_for_screen screen, provider, :application

mywidget = MyWidget.new

box = Gtk::Box.new :vertical
box.add mywidget
box.show_all

window = Gtk::Window.new
window.add box
window.set_size_request 300, 300
window.present

Gtk.main

カスタムウィジットの実装では、ウィジェットのサイズを返す関数が必要です。 GtkWidget にサイズを計算するためのいくつかの仮想関数が定義されているため、カスタムウィジットのクラス MyWidget でそれらをオーバーライドします。 virtual_do_XXX というのがオーバーライドの定義になります。

オーバーライドすべき関数の詳細は以下にまとまっています。

Custom Widgets

また、GTK3でのウィジットのサイズ計算方式height-for-widthについても知っておいた方が良いでしょう。

Gtk.Widget

以上になります。