diff --git a/bindings/globalkeys/sugar-key-grabber.c b/bindings/globalkeys/sugar-key-grabber.c index 7b006858..70f9b471 100644 --- a/bindings/globalkeys/sugar-key-grabber.c +++ b/bindings/globalkeys/sugar-key-grabber.c @@ -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; diff --git a/shell/view/Shell.py b/shell/view/Shell.py index 1e0ae302..f95cf313 100644 --- a/shell/view/Shell.py +++ b/shell/view/Shell.py @@ -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) diff --git a/shell/view/frame/Frame.py b/shell/view/frame/Frame.py index 8ecf37f8..1213d3d6 100644 --- a/shell/view/frame/Frame.py +++ b/shell/view/frame/Frame.py @@ -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() diff --git a/sugar/canvas/Timeline.py b/sugar/canvas/Timeline.py new file mode 100644 index 00000000..333484eb --- /dev/null +++ b/sugar/canvas/Timeline.py @@ -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)