# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+require "evas"
require "ecore"
require "ecore_x"
require "ecore_evas"
require "embrace/imap"
PKG_NAME = "embrace"
-DATADIR = "/usr/local/share/#{PKG_NAME}/"
-
-class Evas::EvasObject
- def move_relative(obj, x, y)
- # FIXME investigate whether there's an easier way
- move(*obj.geometry[0..1].zip([x, y]).map { |(a, b)| a + b })
- end
-
- def center(obj)
- a = geometry
- b = obj.geometry
-
- move_relative(obj, (b[2] / 2) - (a[2] / 2),
- (b[3] / 2) - (a[3] / 2))
- end
-
- def alpha=(alpha)
- set_color(*(get_color[0..-2] << alpha))
- end
-end
+PREFIX = nil
+DATADIR = "#{PREFIX || "/usr/local"}/share/#{PKG_NAME}/"
module Embrace
- VERSION = "0.0.1"
+ VERSION = "0.1.0"
ICON_FILE = DATADIR + "l33t_MAI_envelope.png"
- MAX_ICONS = 11
class ZeroToOneAnimator < Ecore::Animator
def initialize(duration)
# an animator that runs for the specified number of seconds,
# and yields values between 0 and 255
class AlphaAnimator < ZeroToOneAnimator
- def initialize(duration, *objects)
+ def initialize(duration, object)
super(duration) do |v|
- objects.each { |o| o.alpha = (255 * v).to_i }
+ a = compute_alpha(v)
+ object.set_color(a, a, a, a)
end
end
+
+ def compute_alpha(v)
+ (255 * v).to_i
+ end
+ end
+
+ class InverseAlphaAnimator < AlphaAnimator
+ def compute_alpha(v)
+ super((1.0 - v).abs)
+ end
end
class MoveAnimator < ZeroToOneAnimator
end
class MailboxIcon < Evas::Smart
+ class FadeOutFinishedEvent < Ecore::Event
+ attr_reader :icon
+
+ def initialize(icon)
+ super()
+
+ @icon = icon
+ end
+ end
+
attr_accessor :slot
def initialize(evas, label)
super(evas)
+ self.name = label
+
@slot = nil
@alpha_anim = nil
@label = Evas::Text.new(evas)
@objects = [@img, @label]
+ @objects.each { |o| add_member(o) }
- @img.set_color(255, 255, 255, 0)
- @label.set_color(255, 255, 255, 0)
- @label.set_color(255, 0, 0, 0)
+ set_color(0, 0, 0, 0)
@img.set_file(ICON_FILE)
@img.set_fill(0, 0, *@img.get_size)
- @label.text = label
+ @label.text = name
@label.set_font("VeraBd", 10)
+ a = @label.geometry
+ b = *@img.get_size
+
+ @label_offset_x = (b[0] / 2) - (a[2] / 2)
+ @label_offset_y = (b[1] / 2) - (a[3] / 2)
+
resize(*@img.get_size)
end
- def label
- @label.text
+ def fade_in
+ show
+
+ @alpha_anim ||= AlphaAnimator.new(2, self)
+ @alpha_anim.on_finished { @alpha_anim = nil }
+ end
+
+ def fade_out
+ @alpha_anim ||= InverseAlphaAnimator.new(2, self)
+ @alpha_anim.on_finished do
+ @alpha_anim = nil
+ FadeOutFinishedEvent.raise(self)
+ end
end
# smart callbacks
- def on_show
+ def smart_show
@objects.each { |o| o.show }
-
- @alpha_anim ||= AlphaAnimator.new(2, @img, @label)
- @alpha_anim.on_finished { @alpha_anim = nil }
end
- def on_hide
+ def smart_hide
@objects.each { |o| o.hide }
@alpha_anim && @alpha_anim.delete
@alpha_anim = nil
end
- def on_delete
+ def smart_delete
@objects.each { |o| o.delete }
@objects.clear
@img = @label = @alpha_anim = nil
end
- def on_move(x, y)
- @objects.each { |o| o.move(x, y) }
+ def smart_move(x, y)
+ @img.move(x, y)
- @label.center(self)
+ # center the label on the image
+ @label.move(x + @label_offset_x,
+ y + @label_offset_y)
end
- def on_resize(w, h)
+ def smart_resize(w, h)
@img.resize(w, h)
end
- end
-
- class FixedSizeArray < Array
- def initialize(siz)
- super
- end
-
- def each
- super { |item| yield item unless item.nil? }
- end
- def delete_at(i)
- self[i] = nil
+ def smart_color_set(r, g, b, a)
+ @img.set_color(r, g, b, a)
+ @label.set_color(r, 0, 0, a)
end
-
- undef :push
- undef :<<
- undef :unshift
-
- undef :pop
- undef :shift
- undef :delete
end
class Container < Evas::Smart
class ContainerFullError < ContainerError; end
class ContainerLockedError < ContainerError; end
- include Enumerable
-
def initialize(evas)
super
@bg = Evas::Rectangle.new(evas)
- @bg.set_color(0, 0, 0, 255)
+ @bg.set_color(0, 0, 0, 0)
- @icons = FixedSizeArray.new(MAX_ICONS)
- @about_to_add = []
+ add_member(@bg)
+
+ @icons = []
@animators = []
- @lock_count = 0
+ @about_to_add = 0
+ @add_lock_count = 0
+
+ @handlers = [
+ Ecore::EventHandler.new(MailboxIcon::FadeOutFinishedEvent,
+ &method(:on_icon_fade_out_finished))
+ ]
end
- def each
- @icons.each { |i| yield i unless i.nil? }
+ def can_add?
+ !slots_left.zero? && @add_lock_count.zero?
+ end
+
+ def can_delete?
+ @about_to_add.zero? && @add_lock_count.zero?
end
def <<(i)
Kernel.raise(ContainerFullError) if slots_left.zero?
- Kernel.raise(ContainerLockedError) if @lock_count > 0
+ Kernel.raise(ContainerLockedError) unless @add_lock_count.zero?
+
+ geo = geometry
+
+ i.move(geo[0],
+ geo[1] + geo[3] % Main.instance.icon_height)
- i.move_relative(self, 0, 0)
i.slot = next_slot
i.clip = self
- i.show
+ i.fade_in
# check whether we need to need to move this icon
if slots_left == 1
movement = Main.instance.icon_height * (slots_left - 1)
- @about_to_add << i
+ @about_to_add += 1
move_time = 0.25 * slots_left
@animators << MoveAnimator.new(move_time, movement, i)
@animators.delete(ani)
@icons[i.slot] = i
- # FIXME check whether we can always shift the array instead
- #puts "really added #{i.label} now (slot #{i.slot})"
- @about_to_add.delete(i)
+ @about_to_add -= 1
end
end
def delete(icon)
- # icons that are placed above the one that's deleted need
- # to be moved
i = @icons.index(icon)
return (block_given? ? yield : nil) if i.nil?
end
def delete_at(i)
- @icons[i].delete
+ Kernel.raise(ContainerLockedError) unless @about_to_add.zero?
+ Kernel.raise(ContainerLockedError) unless @add_lock_count.zero?
+
+ @add_lock_count += 1
+ @icons[i].fade_out
+ end
+
+ def on_icon_fade_out_finished(ev)
+ i = @icons.index(ev.icon)
+ ev.icon.delete
@icons.delete_at(i)
- ar = @icons[i..-1].reject { |i| i.nil? }
- return if ar.nil?
+ # icons that are placed above the one that's deleted need
+ # to be moved. check whether are there any first
+ if i == @icons.length
+ @add_lock_count -= 1
+ return
+ end
- @lock_count += 1
+ ar = @icons[i..-1]
@animators << MoveAnimator.new(2, Main.instance.icon_height, *ar)
@animators.last.on_finished do |ani|
@animators.delete(ani)
- @lock_count -= 1
+ @add_lock_count -= 1
end
end
- def length
- @icons.nitems
- end
-
# smart callbacks
- def on_show
+ def smart_show
@bg.show
end
- def on_hide
+ def smart_hide
@bg.hide
end
- def on_delete
+ def smart_delete
@bg.delete
@bg = nil
end
- def on_move(x, y)
+ def smart_move(x, y)
@bg.move(x, y)
end
- def on_resize(w, h)
+ def smart_resize(w, h)
@bg.resize(w, h)
end
private
+ def max_icons
+ geometry.pop / Main.instance.icon_height
+ end
+
def slots_left
- MAX_ICONS - @icons.nitems - @about_to_add.length
+ max_icons - @icons.nitems - @about_to_add
end
def next_slot
- @icons.nitems + @about_to_add.length
+ @icons.nitems + @about_to_add
end
end
def initialize
super
+ self.has_alpha = true
self.title = "Embrace"
self.borderless = true
@container.layer = -1
@container.show
- size = [@icon_dim.first, icon_height * MAX_ICONS]
+ size = [@icon_dim.first]
+
+ arg = ARGV.shift
+ if arg.nil?
+ size << icon_height * 11
+ else
+ size << arg.to_i
+ end
+
resize(*size)
set_size_min(*size)
set_size_max(*size)
end
private
- def add_icon(name)
- @container << MailboxIcon.new(evas, name)
- end
-
def on_timer
- return unless @server.nil?
-
- @server = IMAP::Session.new(@config)
+ @server ||= IMAP::Session.new(@config)
true
end
lbl = md.captures.first
end
- found = @container.find { |i| i.label == lbl }
-
- begin
- if ev.count.zero?
- unless found.nil?
- #puts "removing icon #{lbl}"
- @container.delete(found)
- else
- #puts "count == 0, but icon not found (#{lbl})"
- end
- elsif found.nil?
- #puts "adding icon #{lbl}"
- add_icon(lbl)
- else
- #puts "count > 0, but already there (#{lbl})"
- end
- rescue Exception => e
- puts e.message
+ found = evas.find_object(lbl)
+
+ if ev.count.zero? && !found.nil? && @container.can_delete?
+ @container.delete(found)
+ elsif !ev.count.zero? && found.nil? && @container.can_add?
+ @container << MailboxIcon.new(evas, lbl)
end
false