def __init__(self, go_window):
        gtk.Window.__init__(self)
        self.go_window = go_window
        self.results = []
        self.cursor_position = 0
        self.offset = 0
        self.last_cursor_position = None
        self.set_app_paintable(True)
        self.set_keep_above(True)
        self.set_skip_taskbar_hint(True)
        self.set_skip_pager_hint(True)
        self.set_decorated(False)
        self.set_transient_for(self.go_window)
        self.set_default_size(420, 0)

        self._last_frame_time = None
        self.__drawing_queued = False
        self.tweener = Tweener(0.5, Easing.Cubic.ease_in_out)
        self.connect('expose-event', self.draw_window)
        self.do_screen_changed()
class GoResultsWindow(gtk.Window):
    max_results = 4
    def __init__(self, go_window):
        gtk.Window.__init__(self)
        self.go_window = go_window
        self.results = []
        self.cursor_position = 0
        self.offset = 0
        self.last_cursor_position = None
        self.set_app_paintable(True)
        self.set_keep_above(True)
        self.set_skip_taskbar_hint(True)
        self.set_skip_pager_hint(True)
        self.set_decorated(False)
        self.set_transient_for(self.go_window)
        self.set_default_size(420, 0)

        self._last_frame_time = None
        self.__drawing_queued = False
        self.tweener = Tweener(0.5, Easing.Cubic.ease_in_out)
        self.connect('expose-event', self.draw_window)
        self.do_screen_changed()

    @property
    def height(self):
        return self.get_allocation().height

    @property
    def width(self):
        return self.get_allocation().width

    def move_cursor(self, direction_or_newpos='down'):
        try:
            last = self.results[self.cursor_position]
        except IndexError:
            last = None
        reset = False
        if direction_or_newpos == 'down':
            newpos = self.cursor_position + 1
        elif direction_or_newpos == 'up':
            newpos = self.cursor_position - 1
        else:
            newpos = direction_or_newpos
            reset = True
        self.cursor_position = min(newpos, len(self.results) - 1)
        self.cursor_position = max(self.cursor_position, 0)
        try:
            selected = self.results[self.cursor_position]
        except IndexError:
            pass
        else:
            if selected is not last or reset == True:
                #    print 'Moving down!'
                #    for result in self.results:
                #        # how far between selected.real_y and where it needs to be?
                #        move_to = selected.real_y - self.height
                #        print 'Moving to %s - %s = %s' % (selected.real_y, self.height, move_to)
                #        self.tweener.add_tween(result, y=result.y + move_to, duration=0.25)
                #elif selected.real_y < self.get_allocation().y:
                #    print 'Moving up!'
                #    for result in self.results:
                #        self.tweener.add_tween(result, y=0 - (selected.real_y), duration=0.25)
                new_offset = None
                if selected.y + GoResult.height > self.height:
                    new_offset = max(0, self.cursor_position - 3) * (GoResult.height + GoResult.spacing)
                elif selected.y < self.get_allocation().y:
                    new_offset = self.cursor_position * (GoResult.height + GoResult.spacing)
                if new_offset is not None:
                    try:
                        pass
                    except AttributeError:
                        pass
                    self._move_tween = self.tweener.add_tween(self, offset=new_offset, duration=0.25, easing=Easing.Back.ease_out)
                normal_border = GoResult('').border_colour
                normal_bg = GoResult('').background_colour
                for other in filter(lambda r: r is not selected, self.results):
                    other.border_colour = normal_border
                    self.tweener.add_tween(other, background_colour=normal_bg, duration=0.25)

                new_bg = selected.background_colour[:]
                new_bg[3] = 0.95
                selected.border_colour = [1, 1, 1, 1]
                if not reset:
                    self.tweener.add_tween(selected, background_colour=new_bg, duration=0.25)
                else:
                    selected.background_colour = new_bg
        self.redraw()


    def redraw(self):
        """Queue redraw. The redraw will be performed not more often than
           the `framerate` allows"""
        if self.__drawing_queued == False: #if we are moving, then there is a timeout somewhere already
            self.__drawing_queued = True
            self._last_frame_time = dt.datetime.now()
            gobject.timeout_add(1000 / 60, self.__interpolate)

    # animation bits
    def __interpolate(self):
        if self.tweener:
            self.tweener.update((dt.datetime.now() - self._last_frame_time).microseconds / 1000000.0)

        self.__drawing_queued = self.tweener.has_tweens()
        self._last_frame_time = dt.datetime.now()

        self.queue_draw() # this will trigger do_expose_event when the current events have been flushed
        return self.__drawing_queued

    def draw_window(self, widget, event, type="key"):
        c = widget.window.cairo_create()
        #Make the window transparent

        c.set_source_rgba(0.0, 0.0, 0.0, 0.0)
        c.set_operator(cairo.OPERATOR_SOURCE)
        c.paint()

        new_height = min(len(self.results), self.max_results) * (GoResult.height + GoResult.spacing)
        if self.height != new_height:
            self.resize(GoResult.width, new_height)

        c.set_operator(cairo.OPERATOR_OVER)
        c.rectangle(self.get_allocation().x, self.get_allocation().y, self.width, new_height)
        c.clip()
        x = 0

        for number, result in enumerate(self.results):
            y = (number * (GoResult.height + GoResult.spacing)) - self.offset
            result.x = x
            result.y = y
            #if self.cursor_position - 5 < number < self.cursor_position + 5:
            c.save()
            c.rectangle(x, y, GoResult.width, GoResult.height)
            c.clip()
            if y <= self.height + GoResult.height and y + GoResult.height + GoResult.spacing >= 0:
                result.draw(c)
            else:
                print result.title
                print 'Y', y
                print 'offset', self.offset
                print 'height', self.height
                print
            c.restore()

    def update_results(self, new_results):
        new_results = [GoResult(r['title'], r.get('caption'), r.get('thumbnail'), r.get('callback')) for r in new_results[:]]
        #combined_results_len = len(self.results)
        #for result in new_results:
        #    if result not in self.results:
        #        combined_results_len += 1
        self.resize(GoResult.width, self.max_results * (GoResult.height + GoResult.spacing))
        x = gtk.gdk.screen_get_default().get_width() / 2 - GoResult.width / 2
        y = self.go_window.get_position()[1] + self.go_window.get_size()[1] + 20
        self.move(x, y)

        # TODO: add animation and scrolling
        self.results = new_results
        self.move_cursor(0)

    def do_screen_changed(self, old_screen=None):
        screen = self.get_screen()
        colormap = screen.get_rgba_colormap()
        if colormap == None:
            print 'Your screen does not support alpha channels!'
            colormap = screen.get_rgb_colormap()
            self.supports_alpha = False
        else:
            print 'Your screen supports alpha channels!'
            self.supports_alpha = True

        self.set_colormap(colormap)

        return True