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._frame = Frame(self)
self._frame.show_and_hide(3)
#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.framepopupcontext import FramePopupContext
from model.ShellModel import ShellModel
from sugar.graphics.timeline import Timeline
from sugar.graphics import animator
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:
INACTIVE = 0
TEMPORARY = 1
STICKY = 2
HIDE_ON_LEAVE = 3
AUTOMATIC = 4
def next_frame(self, current):
self._frame.move(current)
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):
self._left_panel = None
self._right_panel = None
self._top_panel = None
self._bottom_panel = None
self._hover_frame = False
self._shell = shell
self._mode = Frame.INACTIVE
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._current_position = 0.0
self._event_frame = EventFrame()
self._event_frame.connect('enter-edge', self._enter_edge_cb)
@ -78,6 +88,21 @@ class Frame:
screen = gtk.gdk.screen_get_default()
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):
panel = self._create_panel(hippo.ORIENTATION_HORIZONTAL)
root = panel.get_root()
@ -141,75 +166,6 @@ class Frame:
panel.connect('enter-notify-event', self._enter_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):
screen_h = gtk.gdk.screen_height()
screen_w = gtk.gdk.screen_width()
@ -230,43 +186,48 @@ class Frame:
screen_w, 0,
screen_w - units.grid_to_pixels(1), 0)
def do_slide_in(self, current=0, n_frames=0):
if _ANIMATION:
if current + 1 == n_frames:
# hardcode last frame to avoid precision errors in division
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 hide(self):
anim = animator.Animator(0.5, 30, animator.EASE_OUT_EXPO)
anim.add(_Animation(self, 0.0))
anim.start()
def do_slide_out(self, current=0, n_frames=0):
if _ANIMATION:
if current + 1 == n_frames:
# hardcode last frame to avoid precision errors in division
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 show(self):
anim = animator.Animator(0.5, 30, animator.EASE_OUT_EXPO)
anim.add(_Animation(self, 1.0))
anim.start()
def _size_changed_cb(self, screen):
self._update_position()
def is_visible(self):
return self._top_panel.props.visible
def _popup_context_activated_cb(self, popup_context):
pass
def get_popup_context(self):
return self._popup_context
def _popup_context_deactivated_cb(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
sugar_PYTHON = \
__init__.py \
animator.py \
bubble.py \
button.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