New animation API. Start rewriting the frame slidein slideout logic.

(Use the keys for now, mouse is not working)
This commit is contained in:
Marco Pesenti Gritti 2007-03-12 12:39:29 +01:00
parent 158d933a10
commit 0984938f95
4 changed files with 151 additions and 120 deletions

View File

@ -53,7 +53,6 @@ class Shell(gobject.GObject):
self._popup_context = PopupContext() self._popup_context = PopupContext()
self._frame = Frame(self) self._frame = Frame(self)
self._frame.show_and_hide(3)
#self.start_activity('org.laptop.JournalActivity') #self.start_activity('org.laptop.JournalActivity')

View File

@ -27,33 +27,43 @@ from view.frame.PanelWindow import PanelWindow
from view.frame.clipboardpanelwindow import ClipboardPanelWindow from view.frame.clipboardpanelwindow import ClipboardPanelWindow
from view.frame.framepopupcontext import FramePopupContext from view.frame.framepopupcontext import FramePopupContext
from model.ShellModel import ShellModel from model.ShellModel import ShellModel
from sugar.graphics.timeline import Timeline from sugar.graphics import animator
from sugar.graphics import units from sugar.graphics import units
_ANIMATION = True class _Animation(animator.Animation):
def __init__(self, frame, end):
start = frame.get_current_position()
animator.Animation.__init__(self, start, end)
self._frame = frame
class Frame: def next_frame(self, current):
INACTIVE = 0 self._frame.move(current)
TEMPORARY = 1
STICKY = 2
HIDE_ON_LEAVE = 3
AUTOMATIC = 4
class _KeyListener(object):
def __init__(self, frame):
self._frame = frame
self._frame_active = False
def key_press(self):
if self._frame_active:
self._frame.hide()
self._frame_active = False
else:
self._frame.show()
self._frame_active = True
def key_release(self):
pass
class Frame(object):
def __init__(self, shell): def __init__(self, shell):
self._left_panel = None self._left_panel = None
self._right_panel = None self._right_panel = None
self._top_panel = None self._top_panel = None
self._bottom_panel = None self._bottom_panel = None
self._hover_frame = False
self._shell = shell self._shell = shell
self._mode = Frame.INACTIVE self._current_position = 0.0
self._current_position = 0
self._timeline = Timeline(self)
self._timeline.add_tag('slide_in', 18, 24)
self._timeline.add_tag('before_slide_out', 48, 48)
self._timeline.add_tag('slide_out', 49, 54)
self._event_frame = EventFrame() self._event_frame = EventFrame()
self._event_frame.connect('enter-edge', self._enter_edge_cb) self._event_frame.connect('enter-edge', self._enter_edge_cb)
@ -78,6 +88,21 @@ class Frame:
screen = gtk.gdk.screen_get_default() screen = gtk.gdk.screen_get_default()
screen.connect('size-changed', self._size_changed_cb) screen.connect('size-changed', self._size_changed_cb)
self._key_listener = _KeyListener(self)
def is_visible(self):
return self._top_panel.props.visible
def get_popup_context(self):
return self._popup_context
def get_current_position(self):
return self._current_position
def move(self, pos):
self._current_position = pos
self._update_position()
def _create_top_panel(self): def _create_top_panel(self):
panel = self._create_panel(hippo.ORIENTATION_HORIZONTAL) panel = self._create_panel(hippo.ORIENTATION_HORIZONTAL)
root = panel.get_root() root = panel.get_root()
@ -141,75 +166,6 @@ class Frame:
panel.connect('enter-notify-event', self._enter_notify_cb) panel.connect('enter-notify-event', self._enter_notify_cb)
panel.connect('leave-notify-event', self._leave_notify_cb) panel.connect('leave-notify-event', self._leave_notify_cb)
def _popup_context_activated_cb(self, popup_context):
self._timeline.goto('slide_in', True)
def _popup_context_deactivated_cb(self, popup_context):
if self._mode != Frame.STICKY and not self._hover_frame:
self._timeline.play('before_slide_out', 'slide_out')
def _enter_notify_cb(self, window, event):
self._enter_notify()
def _drag_motion_cb(self, window, context, x, y, time):
self._enter_notify()
return True
def _drag_leave_cb(self, window, drag_context, timestamp):
self._leave_notify(window)
def _leave_notify_cb(self, window, event):
# FIXME for some reason every click cause also a leave-notify
if event.state == gtk.gdk.BUTTON1_MASK:
return
self._leave_notify(window)
def _enter_notify(self):
self._hover_frame = True
if not self._timeline.on_tag('slide_in'):
self._timeline.goto('slide_in', True)
def _leave_notify(self, panel):
self._hover_frame = False
if not self._popup_context.is_active() and \
(self._mode == Frame.HIDE_ON_LEAVE or \
self._mode == Frame.AUTOMATIC):
self._timeline.play('before_slide_out', 'slide_out')
def _enter_edge_cb(self, event_frame):
self._mode = Frame.HIDE_ON_LEAVE
self._timeline.play(None, 'slide_in')
def _enter_corner_cb(self, event_frame):
self._mode = Frame.HIDE_ON_LEAVE
self._timeline.play('slide_in', 'slide_in')
def _event_frame_leave_cb(self, event_frame):
if self._mode != Frame.STICKY:
self._timeline.goto('slide_out', True)
def show_and_hide(self, seconds):
self._mode = Frame.AUTOMATIC
self._timeline.play()
def notify_key_press(self):
if self._timeline.on_tag('slide_in'):
self._timeline.play('before_slide_out', 'slide_out')
elif self._timeline.on_tag('before_slide_out'):
self._mode = Frame.TEMPORARY
else:
self._mode = Frame.STICKY
self._timeline.play('slide_in', 'slide_in')
def notify_key_release(self):
if self._mode == Frame.TEMPORARY:
self._timeline.play('before_slide_out', 'slide_out')
def _move(self, pos):
self._current_position = pos
self._update_position()
def _update_position(self): def _update_position(self):
screen_h = gtk.gdk.screen_height() screen_h = gtk.gdk.screen_height()
screen_w = gtk.gdk.screen_width() screen_w = gtk.gdk.screen_width()
@ -230,43 +186,48 @@ class Frame:
screen_w, 0, screen_w, 0,
screen_w - units.grid_to_pixels(1), 0) screen_w - units.grid_to_pixels(1), 0)
def do_slide_in(self, current=0, n_frames=0): def hide(self):
if _ANIMATION: anim = animator.Animator(0.5, 30, animator.EASE_OUT_EXPO)
if current + 1 == n_frames: anim.add(_Animation(self, 0.0))
# hardcode last frame to avoid precision errors in division anim.start()
self._move(1)
else:
# each frame, move half the remaining distance
pos = 0.0
for i in range(0, current + 1):
pos += float((1.0 - float(pos)) / 2.0)
self._move(pos)
elif current == 0:
self._move(1)
if self._event_frame.is_visible():
self._event_frame.hide()
def do_slide_out(self, current=0, n_frames=0): def show(self):
if _ANIMATION: anim = animator.Animator(0.5, 30, animator.EASE_OUT_EXPO)
if current + 1 == n_frames: anim.add(_Animation(self, 1.0))
# hardcode last frame to avoid precision errors in division anim.start()
self._move(0)
else:
# each frame, move half the remaining distance
pos = 0.0
for i in range(0, current + 1):
pos += float((1.0 - float(pos)) / 2.0)
self._move(1.0 - float(pos))
elif current == 0:
self._move(0)
if not self._event_frame.is_visible():
self._event_frame.show()
def _size_changed_cb(self, screen): def _size_changed_cb(self, screen):
self._update_position() self._update_position()
def is_visible(self): def _popup_context_activated_cb(self, popup_context):
return self._top_panel.props.visible pass
def get_popup_context(self): def _popup_context_deactivated_cb(self, popup_context):
return self._popup_context pass
def _enter_notify_cb(self, window, event):
pass
def _leave_notify_cb(self, window, event):
pass
def _drag_motion_cb(self, window, context, x, y, time):
pass
def _drag_leave_cb(self, window, drag_context, timestamp):
pass
def _enter_edge_cb(self, event_frame):
pass
def _enter_corner_cb(self, event_frame):
pass
def _event_frame_leave_cb(self, event_frame):
pass
def notify_key_press(self):
self._key_listener.key_press()
def notify_key_release(self):
self._key_listener.key_release()

View File

@ -1,6 +1,7 @@
sugardir = $(pythondir)/sugar/graphics sugardir = $(pythondir)/sugar/graphics
sugar_PYTHON = \ sugar_PYTHON = \
__init__.py \ __init__.py \
animator.py \
bubble.py \ bubble.py \
button.py \ button.py \
iconbutton.py \ iconbutton.py \

View File

@ -0,0 +1,70 @@
# Copyright (C) 2007, Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
import time
import gobject
EASE_OUT_EXPO = 1
class Animator(object):
def __init__(self, time, fps, easing=EASE_OUT_EXPO):
self._animations = []
self._time = time
self._interval = 1.0 / fps
self._easing = easing
self._timeout_sid = 0
def add(self, animation):
self._animations.append(animation)
def start(self):
if self._timeout_sid:
self.stop()
self._start_time = time.time()
self._timeout_sid = gobject.timeout_add(
int(self._interval * 1000), self._next_frame_cb)
def stop(self):
if self._timeout_sid:
gobject.source_remove(self._timeout_sid)
self._timeout_sid = 0
def _next_frame_cb(self):
current_time = min (self._time, time.time() - self._start_time)
for animation in self._animations:
animation.do_frame(current_time, self._time, self._easing)
return (current_time != self._time)
class Animation(object):
def __init__(self, start, end):
self.start = start
self.end = end
def do_frame(self, time, duration, easing):
start = self.start
change = self.end - self.start
if easing == EASE_OUT_EXPO:
frame = change * pow(2, 10 * (time / duration - 1)) + start;
self.next_frame(frame)
def next_frame(self, frame):
pass