WxRuby

I wrote the entire UI for my “Simple Skype” front end in wxRuby. wxRuby’s benefit comes from it’s use of widgets native to the window manager (i.e., Aqua on Mac OS X, etc.). And, while I only had a few problems with it, most of my problems were PEBKACs.

Sadly, the memory leak in my GUI, caused by the use of Bitmaps, was not among my PEBKACs. This is currently tracked as an open bug against Wx. It is unfortunate that this issue has apparently not received much attention since it was noted three years ago…

And so, on to another windowing toolkit… Take 2 will probably be written in Ruby/TK or FXRuby as I understand that both are fairly mature bindings.

In the meantime, below is the code, using rb-skypemac, that I was putting together.

require 'rubygems'
require 'wx'
require 'rb-skypemac'
require 'monitor'
include Wx
include SkypeMac

$BTN_WIDTH = 150
$BTN_HEIGHT = 150
$FONT_SIZE = 14
$IMAGE_DIR = "./images/"
$GREEN_IMG = $IMAGE_DIR + "skype_green2.gif"
$YELLOW_IMG = $IMAGE_DIR + "skype_yellow2.gif"
$RED_IMG = $IMAGE_DIR + "skype_red2.gif"

module SimpleSkypeUtil
  def create_bitmap_text_button(labels, parent, status)
    bitmap = case status
      when "ONLINE": Bitmap.new($GREEN_IMG, BITMAP_TYPE_GIF)
      when "DND": Bitmap.new($RED_IMG, BITMAP_TYPE_GIF)
      else Bitmap.new($YELLOW_IMG, BITMAP_TYPE_GIF)  
    end
    bitmap.draw do |dc|
      dc.set_font Font.new($FONT_SIZE, FONTFAMILY_DEFAULT, FONTSTYLE_NORMAL, FONTWEIGHT_NORMAL)
      dc.set_background_mode TRANSPARENT
      dc.set_text_foreground BLACK
      label_no = 0
      extent = dc.get_text_extent(labels[0])
      extent_y = extent[1] * labels.size
      labels.each do |label|
        extent = dc.get_text_extent(label)
        text_x = ($BTN_WIDTH - extent[0]) / 2
        text_y = ($BTN_HEIGHT - extent_y) / 2 + extent[1] * label_no
        dc.draw_text label, text_x, text_y
        label_no += 1 
      end
    end
    btn = BitmapButton.new(parent, -1, bitmap)
  end
end

module SimpleSkype
  class HangupDialog < Wx::Dialog 
    include SimpleSkypeUtil

    # TODO: replace destroy_children call below to prevent this dialog from being nuked's unduly
    def initialize(parent, id, title, call)
      super(parent, 
            id, 
            title, 
            Point.new(parent.get_position.x + parent.get_size.get_width, 
                      parent.get_position.y),
                    Size.new(150, 150),
                    DIALOG_EX_METAL|STAY_ON_TOP)
      @call = call

      set_size Size.new($BTN_WIDTH, $BTN_HEIGHT)
      set_sizer(sizer = StdDialogButtonSizer.new)
      btn = create_bitmap_text_button("Hang up call".to_a, self, :red)
      btn.set_min_size Size.new($BTN_WIDTH, $BTN_HEIGHT)
      sizer.add btn

      evt_button(btn.get_id) { |e| @call.hangup; self.hide }
    end
  end

  class SkypeApp < App
    include SimpleSkypeUtil
    include MonitorMixin

    attr :frame

    def handle_user_click(event, user)
        call = Skype.call user
      HangupDialog.new(@frame, -1, "Hang up call?", call).show_modal
    end

    def create_button_per_friend
      @friends.map do |f|
        labels = []
        labels << "#{(f.fullname != "") ? f.fullname : f.handle}"
        #labels << "(#{f.onlinestatus})"
        btn = create_bitmap_text_button labels, @frame, f.onlinestatus
        btn.set_min_size Size.new(150,150)
        evt_button(btn.get_id) { |e| handle_user_click(e, f) }
        btn
      end
    end

    def redraw_friends_buttons
      @frame.destroy_children

      @sizer = BoxSizer.new(VERTICAL) if @sizer.nil?

      @friends = Skype.online_friends.sort

      @frame.set_client_size($BTN_WIDTH, $BTN_HEIGHT * @friends.size)   
      @friend_btns = create_button_per_friend
      @friend_btns.each { |b| @sizer.add(b, 1, GROW|ALL, 2) }
      @sizer.layout     

      @frame.set_sizer(@sizer) if @frame.get_sizer.nil?
      @frame.show
    end

    def on_init
      @friends = Skype.online_friends
      @frame = Frame.new(nil, 
                         1, 
                         'Simple Skype', 
                         Point.new(0, 80), 
                         Size.new($BTN_WIDTH,$BTN_HEIGHT * @friends.size), 
                         FRAME_EX_METAL)
      redraw_friends_buttons

      timer = Timer.new @frame, -1
      evt_timer(-1) { redraw_friends_buttons }
      timer.start 5000
    end
  end
end

app = SimpleSkype::SkypeApp.new

app.main_loop

Posted by evan on Sunday, April 29, 2007

blog comments powered by Disqus