電卓の形だけ。計算はできない。

#!/usr/bin/env ruby

require 'sdl'
require 'cairo'

class Sprite
  attr_accessor :x, :y, :alive, :enabled, :idx, :info
  attr_writer :draw, :matrix

  def initialize
    @x, @y = 0, 0
    @alive = true
    @enabled = true
    @idx = 0
    @info = {}
    @matrix = Cairo::Matrix.identity
  end

  def renew(cr)
    return if !@enabled
    cr.matrix = @matrix * Cairo::Matrix.translate(@x, @y)
    @draw[cr]
  end
end

class Phase
  attr_accessor :handles, :task

  def initialize(w, h, info=nil)
    @image = Cairo::ImageSurface.new(w, h)
    @info = info
    @sprites = []
    @handles = {}
  end

  def addSprite
    @sprites << Sprite.new
    yield(@sprites.last)
    @sprites.last
  end

  def renew(drawFlag=true)
    @task[] if @task
    if drawFlag
      cr = Cairo::Context.new(@image)
      @sprites.reject!{|obj| !obj.alive}
      @sprites.each {|obj| obj.renew(cr)}
      @image
    else
      nil
    end
  end

  def layerSort
    @sprites.sort!{|a, b| a.idx <=> b.idx}
  end
end

SDL.init(SDL::INIT_VIDEO)
SDL::WM.set_caption("Calc","")
w, h = 300,280
screen = SDL::Screen.open(w, h, 32, 0)
phase = Phase.new(w, h)
display_text = ""
phase.addSprite do |obj|
  obj.draw = proc do |cr|
    cr.set_source_color(Cairo::Color::GRAY)
    cr.rectangle(0, 0, w, h)
    cr.fill
  end
end
phase.addSprite do |obj|
  obj.x, obj.y = 10, 10
  obj.draw = proc do |cr|
    cr.set_source_color(Cairo::Color::WHITE)
    cr.rectangle(0, 0, 280, 30)
    cr.fill
  end
end
font_size = 25
phase.addSprite do |obj|
  obj.x, obj.y = 50, 10
  obj.draw = proc do |cr|
    cr.set_source_color(Cairo::Color::BLACK)
    cr.font_size = font_size
    cr.move_to(0, font_size)
    cr.show_text(display_text.rjust(12))
  end
end
key_layout = %w(7 8 9 / 4 5 6 * 1 2 3 - C 0 = +)
keys = []
key_color = [Cairo::Color::WHITE, Cairo::Color::YELLOW, Cairo::Color::ORANGE]
kw, kh = 50, 30
4.times do |i|
  4.times do |j|
    x = j * 70 + 15
    y = i * 50 + 80
    text = key_layout[4 * i + j]
    keys << phase.addSprite do |obj|
      obj.x, obj.y = x, y
      obj.info[:rx] = x..x+kw
      obj.info[:ry] = y..y+kh
      obj.info[:key_idx] = 0
      obj.info[:text] = text
      obj.draw = proc do |cr|
        cr.set_source_color(key_color[obj.info[:key_idx]])
        cr.rectangle(0, 0, kw, kh)
        cr.fill
        cr.set_source_color(Cairo::Color::BLACK)
        cr.font_size = font_size
        cr.move_to(15, font_size)
        cr.show_text(text)
      end
    end
  end
end
phase.handles[SDL::Event::MouseMotion] = proc do |e|
  keys.each do |obj|
    if obj.info[:rx].include?(e.x) && obj.info[:ry].include?(e.y)
      obj.info[:key_idx] = 1
    else
      obj.info[:key_idx] = 0
    end
  end
end
phase.handles[SDL::Event::MouseButtonUp] = proc do |e|
  keys.each do |obj|
    if obj.info[:rx].include?(e.x) && obj.info[:ry].include?(e.y)
      obj.info[:key_idx] = 1
    end
  end
end
phase.handles[SDL::Event::MouseButtonDown] = proc do |e|
  keys.each do |obj|
    if obj.info[:rx].include?(e.x) && obj.info[:ry].include?(e.y)
      obj.info[:key_idx] = 2
      if obj.info[:text] == "C"
        display_text = ""
      else
        display_text << obj.info[:text]
      end
    end
  end
end
phase.handles[SDL::Event::Quit] = proc{exit}
phase.handles[SDL::Event::KeyDown] = proc{|e| exit if e.sym == SDL::Key::ESCAPE}
loop do
  while e = SDL::Event.poll
    if handle = phase.handles[e.class]
      handle[e]
    end
  end
  image = phase.renew
  surface = SDL::Surface.new_from(image.data, w, h, 32, image.stride, 0, 0, 0, 0)
  screen.put(surface,0,0)
  screen.flip
  SDL.delay(20)
end