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 { struct _SugarKeyGrabberClass {
GObjectClass base_class; GObjectClass base_class;
void (* key_pressed) (SugarKeyGrabber *grabber, void (* key_pressed) (SugarKeyGrabber *grabber,
const char *key); const char *key);
void (* key_released) (SugarKeyGrabber *grabber,
const char *key);
}; };
enum { enum {
KEY_PRESSED, KEY_PRESSED,
KEY_RELEASED,
N_SIGNALS N_SIGNALS
}; };
@ -97,6 +100,34 @@ sugar_key_grabber_class_init(SugarKeyGrabberClass *grabber_class)
g_cclosure_marshal_VOID__STRING, g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1, G_TYPE_NONE, 1,
G_TYPE_STRING); 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 static GdkFilterReturn
@ -105,22 +136,33 @@ filter_events(GdkXEvent *xevent, GdkEvent *event, gpointer data)
SugarKeyGrabber *grabber = (SugarKeyGrabber *)data; SugarKeyGrabber *grabber = (SugarKeyGrabber *)data;
XEvent *xev = (XEvent *)xevent; XEvent *xev = (XEvent *)xevent;
if (xev->type == KeyPress) { if (xev->type == KeyRelease) {
GList *l; char *key;
guint keycode, state;
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_REMOVE;
} }
}
} }
return GDK_FILTER_CONTINUE; return GDK_FILTER_CONTINUE;

View File

@ -31,7 +31,10 @@ class Shell(gobject.GObject):
self._grid = Grid() self._grid = Grid()
self._key_grabber = KeyGrabber() 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('F1')
self._key_grabber.grab('F2') self._key_grabber.grab('F2')
self._key_grabber.grab('F3') self._key_grabber.grab('F3')
@ -61,10 +64,14 @@ class Shell(gobject.GObject):
elif key == 'F4': elif key == 'F4':
self.set_zoom_level(sugar.ZOOM_MESH) self.set_zoom_level(sugar.ZOOM_MESH)
elif key == 'F5': elif key == 'F5':
self._frame.toggle_visibility() self._frame.notify_key_press()
elif key == 'F6': elif key == 'F6':
self.start_activity('org.sugar.Terminal') 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): def __window_opened_cb(self, screen, window):
if window.get_window_type() == wnck.WINDOW_NORMAL: if window.get_window_type() == wnck.WINDOW_NORMAL:
activity_host = ActivityHost(self, window) 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.TopPanel import TopPanel
from view.frame.PanelWindow import PanelWindow from view.frame.PanelWindow import PanelWindow
from sugar.canvas.Grid import Grid from sugar.canvas.Grid import Grid
from sugar.canvas.Timeline import Timeline
from sugar.canvas.MenuShell import MenuShell from sugar.canvas.MenuShell import MenuShell
class EventFrame(gobject.GObject): class EventFrame(gobject.GObject):
@ -69,7 +70,13 @@ class Frame:
def __init__(self, shell): def __init__(self, shell):
self._windows = [] self._windows = []
self._shell = shell 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() model = goocanvas.CanvasModelSimple()
root = model.get_root_item() root = model.get_root_item()
@ -116,53 +123,44 @@ class Frame:
self._windows.append(panel_window) self._windows.append(panel_window)
def _menu_shell_activated_cb(self, menu_shell): def _menu_shell_activated_cb(self, menu_shell):
self._cancel_hide() pass
def _menu_shell_deactivated_cb(self, menu_shell): def _menu_shell_deactivated_cb(self, menu_shell):
self._hide_after(500) pass
def _enter_notify_cb(self, window, event): def _enter_notify_cb(self, window, event):
self._cancel_hide() pass
def _leave_notify_cb(self, window, event): def _leave_notify_cb(self, window, event):
# FIXME for some reason every click cause also a leave-notify pass
if event.state == gtk.gdk.BUTTON1_MASK:
return
if not self._menu_shell.is_active():
self._hide_after(500)
def _event_frame_hover_cb(self, event_frame): def _event_frame_hover_cb(self, event_frame):
self.show() pass
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)
def show_and_hide(self, seconds): def show_and_hide(self, seconds):
self.show() self._timeline.play()
self._hide_after(seconds * 1000)
def show(self): def notify_key_press(self):
for panel in self._windows: if self._timeline.on_tag('slide_in'):
panel.show() self._timeline.play('before_slide_out', 'slide_out')
self._event_frame.hide() elif self._timeline.on_tag('before_slide_out'):
self._sticky = True
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()
else: 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)