Simple timeline API.

Use it to implement the frame key logic.
This commit is contained in:
Marco Pesenti Gritti 2006-09-21 14:08:10 +02:00
parent 4977b44037
commit 446a58d52c
4 changed files with 189 additions and 55 deletions

View File

@ -43,12 +43,15 @@ struct _SugarKeyGrabber {
struct _SugarKeyGrabberClass {
GObjectClass base_class;
void (* key_pressed) (SugarKeyGrabber *grabber,
const char *key);
void (* key_pressed) (SugarKeyGrabber *grabber,
const char *key);
void (* key_released) (SugarKeyGrabber *grabber,
const char *key);
};
enum {
KEY_PRESSED,
KEY_RELEASED,
N_SIGNALS
};
@ -97,6 +100,34 @@ sugar_key_grabber_class_init(SugarKeyGrabberClass *grabber_class)
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1,
G_TYPE_STRING);
signals[KEY_RELEASED] = g_signal_new ("key-released",
G_TYPE_FROM_CLASS (grabber_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (SugarKeyGrabberClass, key_released),
NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1,
G_TYPE_STRING);
}
static char *
get_key_from_event(SugarKeyGrabber *grabber, XEvent *xev)
{
GList *l;
guint keycode, state;
keycode = xev->xkey.keycode;
state = xev->xkey.state;
for (l = grabber->keys; l != NULL; l = l->next) {
Key *keyinfo = (Key *)l->data;
if (keyinfo->keycode == keycode &&
(state & USED_MODS) == keyinfo->state) {
return g_strdup(keyinfo->key);
}
}
return NULL;
}
static GdkFilterReturn
@ -105,22 +136,33 @@ filter_events(GdkXEvent *xevent, GdkEvent *event, gpointer data)
SugarKeyGrabber *grabber = (SugarKeyGrabber *)data;
XEvent *xev = (XEvent *)xevent;
if (xev->type == KeyPress) {
GList *l;
guint keycode, state;
if (xev->type == KeyRelease) {
char *key;
key = get_key_from_event(grabber, xevent);
if (key) {
g_signal_emit (grabber, signals[KEY_RELEASED], 0, key);
g_free(key);
return GDK_FILTER_REMOVE;
}
}
if (xev->type == KeyPress) {
char *key;
key = get_key_from_event(grabber, xevent);
if (key) {
g_signal_emit (grabber, signals[KEY_PRESSED], 0, key);
g_free(key);
XGrabKeyboard (GDK_WINDOW_XDISPLAY (grabber->root),
GDK_WINDOW_XID (grabber->root),
0, GrabModeAsync, GrabModeAsync, 0L);
keycode = xev->xkey.keycode;
state = xev->xkey.state;
for (l = grabber->keys; l != NULL; l = l->next) {
Key *keyinfo = (Key *)l->data;
if (keyinfo->keycode == keycode &&
(state & USED_MODS) == keyinfo->state) {
g_signal_emit (grabber, signals[KEY_PRESSED],
0, keyinfo->key);
return GDK_FILTER_REMOVE;
}
}
}
return GDK_FILTER_CONTINUE;

View File

@ -31,7 +31,10 @@ class Shell(gobject.GObject):
self._grid = Grid()
self._key_grabber = KeyGrabber()
self._key_grabber.connect('key-pressed', self.__global_key_pressed_cb)
self._key_grabber.connect('key-pressed',
self.__global_key_pressed_cb)
self._key_grabber.connect('key-released',
self.__global_key_released_cb)
self._key_grabber.grab('F1')
self._key_grabber.grab('F2')
self._key_grabber.grab('F3')
@ -61,10 +64,14 @@ class Shell(gobject.GObject):
elif key == 'F4':
self.set_zoom_level(sugar.ZOOM_MESH)
elif key == 'F5':
self._frame.toggle_visibility()
self._frame.notify_key_press()
elif key == 'F6':
self.start_activity('org.sugar.Terminal')
def __global_key_released_cb(self, grabber, key):
if key == 'F5':
self._frame.notify_key_release()
def __window_opened_cb(self, screen, window):
if window.get_window_type() == wnck.WINDOW_NORMAL:
activity_host = ActivityHost(self, window)

View File

@ -8,6 +8,7 @@ from view.frame.RightPanel import RightPanel
from view.frame.TopPanel import TopPanel
from view.frame.PanelWindow import PanelWindow
from sugar.canvas.Grid import Grid
from sugar.canvas.Timeline import Timeline
from sugar.canvas.MenuShell import MenuShell
class EventFrame(gobject.GObject):
@ -69,7 +70,13 @@ class Frame:
def __init__(self, shell):
self._windows = []
self._shell = shell
self._hide_timeout = 0
self._sticky = False
self._timeline = Timeline(self)
self._timeline.add_tag('start', 0, 0)
self._timeline.add_tag('slide_in', 6, 12)
self._timeline.add_tag('before_slide_out', 36, 36)
self._timeline.add_tag('slide_out', 37, 42)
model = goocanvas.CanvasModelSimple()
root = model.get_root_item()
@ -116,53 +123,44 @@ class Frame:
self._windows.append(panel_window)
def _menu_shell_activated_cb(self, menu_shell):
self._cancel_hide()
pass
def _menu_shell_deactivated_cb(self, menu_shell):
self._hide_after(500)
pass
def _enter_notify_cb(self, window, event):
self._cancel_hide()
pass
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
if not self._menu_shell.is_active():
self._hide_after(500)
pass
def _event_frame_hover_cb(self, event_frame):
self.show()
def _hide_timeout_cb(self):
self.hide()
return False
def _cancel_hide(self):
if self._hide_timeout > 0:
gobject.source_remove(self._hide_timeout)
def _hide_after(self, ms):
self._cancel_hide()
self._hide_timeout = gobject.timeout_add(ms, self._hide_timeout_cb)
pass
def show_and_hide(self, seconds):
self.show()
self._hide_after(seconds * 1000)
self._timeline.play()
def show(self):
for panel in self._windows:
panel.show()
self._event_frame.hide()
def hide(self):
for panel in self._windows:
panel.hide()
self._event_frame.show()
def toggle_visibility(self):
if self._windows[0].props.visible:
self.hide()
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._sticky = True
else:
self.show()
self._sticky = False
self._timeline.play('slide_in', 'slide_in')
def notify_key_release(self):
if self._sticky:
self._timeline.play('before_slide_out', 'slide_out')
def do_slide_in(self, current, n_frames):
if current == 0:
for panel in self._windows:
panel.show()
self._event_frame.hide()
def do_slide_out(self, current, n_frames):
if current == 0:
for panel in self._windows:
panel.hide()
self._event_frame.show()

87
sugar/canvas/Timeline.py Normal file
View File

@ -0,0 +1,87 @@
import gobject
class _Tag:
def __init__(self, name, start_frame, end_frame):
self.name = name
self.start_frame = start_frame
self.end_frame = end_frame
class TimelineObserver:
def __init__(self, observer):
self._observer = observer
def next_frame(self, tag, current_frame, n_frames):
try:
method = getattr(self._observer, 'do_' + tag)
method(current_frame, n_frames)
except:
pass
class Timeline:
def __init__(self, observer):
self._fps = 12
self._tags = []
self._name_to_tag = {}
self._current_frame = 0
self._timeout_sid = 0
self._observer = TimelineObserver(observer)
def add_tag(self, name, start_frame, end_frame):
tag = _Tag(name, start_frame, end_frame)
self._tags.append(tag)
self._name_to_tag[name] = tag
def remove_tag(self, name):
tag = self._tags[name]
self._tags.remove(tag)
del self._tags[name]
def _next_frame(self, tag, frame):
n_frames = tag.start_frame - tag.end_frame
self._observer.next_frame(tag.name, frame, n_frames)
def on_tag(self, name):
tag = self._name_to_tag[name]
return (tag.start_frame <= self._current_frame and \
tag.end_frame >= self._current_frame)
def _get_tags_for_frame(self, frame):
result = []
for tag in self._tags:
if tag.start_frame <= frame and tag.end_frame >= frame:
result.append(tag)
return result
def _timeout_cb(self, end_frame):
for tag in self._get_tags_for_frame(self._current_frame):
cur_frame = self._current_frame - tag.start_frame
self._next_frame(tag, cur_frame)
if self._current_frame < end_frame:
self._current_frame += 1
return True
else:
return False
def play(self, start_tag=None, stop_tag=None):
self.stop()
if start_tag == None:
start = self._tags[0].start_frame
else:
start = self._name_to_tag[start_tag].start_frame
if stop_tag == None:
end = self._tags[len(self._tags) - 1].end_frame
else:
end = self._name_to_tag[stop_tag].end_frame
self._current_frame = start
interval = 1000 / self._fps
self._timeout_sid = gobject.timeout_add(
interval, self._timeout_cb, end)
def stop(self):
if self._timeout_sid > 0:
gobject.source_remove(self._timeout_sid)