Re: [sugar] [Cross Posted] High res screenshots of Sugar
On Fri, 2008-10-10 at 18:12 +0200, Marco Pesenti Gritti wrote: On Fri, Oct 10, 2008 at 6:09 PM, Bert Freudenberg [EMAIL PROTECTED] wrote: If you *really* wanted to get fancy, run Sugar inside a VNC server of say 12000x9000 pixels at 2000 dpi. Sugar is designed to be scalable to all resolutions so in theory this should work. In practice you'll find there are some things that are not really scaled. Fonts will probably be messed up in that case. They shouldn't, Sugar uses scalable fonts. Bert is correct here. It's supposed to work... how well it work in practice I don't know :) The GTK+ theme would need to be adjusted to give good results, as it uses a lot of pixel sizes. Benjamin signature.asc Description: This is a digitally signed message part ___ Sugar mailing list Sugar@lists.laptop.org http://lists.laptop.org/listinfo/sugar
Re: [sugar] rendering test
Hello, On Sun, 2008-09-28 at 12:43 +0200, Riccardo Lucchese wrote: Besides this, I think the icon caching mechanism should be reworked: right now every icon has its own surfaces-cache and its svgloader. So that, if I'm not wrong, two icons showing the same svg (with same size, colors etc..) cache two distinct but identical surfaces in their _iconbuffer and cache two times the raw svg file contents in their svgloader. The cached surface is shared between different icons (the icon buffer is not shared). _surface_cache is a class variable, so that only one LRU list for the surfaces is created. I guess best would be switching to have only one global cache (or not ? ;); perhaps a global cache clashes with using server side surfaces ? (see benzea's patches) A shared cache would be great. One way to do it would be to share a mmap'able file similar to the GTK+ icon cache file. But to create this file, one will need to know the icon colors that should be prerendered. More complicated would be to create a small service that uploads rendered icons to the X server, and hands back the pixmap ID. That way all applications could use one shared server side cache. (The pixmap based sapwood GTK+ engine, used on the Nokia 770/8x0 devices, does this to save memory.) Benjamin signature.asc Description: This is a digitally signed message part ___ Sugar mailing list Sugar@lists.laptop.org http://lists.laptop.org/listinfo/sugar
Re: [sugar] rendering test
On Sun, 2008-09-28 at 18:35 +0200, Bernie Innocenti wrote: Benjamin Berg wrote: A shared cache would be great. One way to do it would be to share a mmap'able file similar to the GTK+ icon cache file. But to create this file, one will need to know the icon colors that should be prerendered. More complicated would be to create a small service that uploads rendered icons to the X server, and hands back the pixmap ID. That way all applications could use one shared server side cache. (The pixmap based sapwood GTK+ engine, used on the Nokia 770/8x0 devices, does this to save memory.) We discussed this approach in Brno. I'm glad to hear it's feasible. Oh, why can't we just use the gtk icon cache with the rendered pixmaps? The thing is that the GTK+ icon cache are just prerendered icons (created with a standalone application). In Sugar however, we need a more complicated mechanism as the colour of the icons is changed on the fly. One would probably need to implement a custom icon cache, that prerenders the most often needed colour combinations. Benjamin signature.asc Description: This is a digitally signed message part ___ Sugar mailing list Sugar@lists.laptop.org http://lists.laptop.org/listinfo/sugar
Re: [sugar] [PATCH 2/6] Implement API to handle tabbing.
On Fri, 2008-06-20 at 11:19 +0200, Marco Pesenti Gritti wrote: On Thu, Jun 19, 2008 at 9:08 PM, Benjamin Berg [EMAIL PROTECTED] wrote: +def tabbing_activate_current(self): +home_model = self._model.get_home() +activity = home_model.get_active_activity() +if activity and activity.get_window(): +activity.get_window().activate(1) Do we get a window_changed even in the model even if the window has really been activated during the tabbing? Yes. Because of this the model needs to ignore window changes during tabbing. If the window changes were not ignored, a race condition exists: - User tabs - Timeout to activate the window fires - User tabs - Window is raised a bit later - The homemodel emits active-activity-changed And in the end the wrong button is selected in the activities tray (for 1/4 of a second). Benjamin signature.asc Description: This is a digitally signed message part ___ Sugar mailing list Sugar@lists.laptop.org http://lists.laptop.org/listinfo/sugar
[sugar] [PATCH] Tabbing again
Hello, here is another go at the tabbing patches, after Eben gave his feedback. There are no palettes anymore, instead it tries to get close to switching the activities instantly. However, the actual activity switching is delayed by 1/4 of a second, to help against long expose times. There are again two patch series. One for sugar-toolkit (2 Patches) and another one for sugar (6 patches). Benjamin signature.asc Description: This is a digitally signed message part ___ Sugar mailing list Sugar@lists.laptop.org http://lists.laptop.org/listinfo/sugar
[sugar] [PATCH 1/2] Add is_modifier and is_special_modifier functions to SugarKeyGrabber.
--- src/sugar/_sugarext.defs | 19 + src/sugar/sugar-key-grabber.c | 59 + src/sugar/sugar-key-grabber.h |6 3 files changed, 84 insertions(+), 0 deletions(-) diff --git a/src/sugar/_sugarext.defs b/src/sugar/_sugarext.defs index 02b673c..6bc068f 100644 --- a/src/sugar/_sugarext.defs +++ b/src/sugar/_sugarext.defs @@ -121,6 +121,25 @@ ) ) +(define-method is_modifier + (of-object SugarKeyGrabber) + (c-name sugar_key_grabber_is_modifier) + (return-type gboolean) + (parameters +'(guint keycode) + ) +) + +(define-method is_specific_modifier + (of-object SugarKeyGrabber) + (c-name sugar_key_grabber_is_specific_modifier) + (return-type gboolean) + (parameters +'(guint keycode) +'(guint mask) + ) +) + ;; From sexy-icon-entry.h (define-function sexy_icon_entry_get_type diff --git a/src/sugar/sugar-key-grabber.c b/src/sugar/sugar-key-grabber.c index baddab5..ed0cf9f 100644 --- a/src/sugar/sugar-key-grabber.c +++ b/src/sugar/sugar-key-grabber.c @@ -217,3 +217,62 @@ sugar_key_grabber_grab(SugarKeyGrabber *grabber, const char *key) grabber-keys = g_list_append(grabber-keys, keyinfo); } + +gboolean +sugar_key_grabber_is_modifier(SugarKeyGrabber *grabber, guint keycode) +{ + Display *xdisplay; + XModifierKeymap *modmap; + gint size, i; + gboolean is_modifier = FALSE; + + xdisplay = gdk_x11_drawable_get_xdisplay(GDK_DRAWABLE (grabber-root)); + + modmap = XGetModifierMapping (xdisplay); + + size = 8 * modmap-max_keypermod; + for (i = 0; i size; i++) { + if (keycode == modmap-modifiermap[i]) { + is_modifier = TRUE; + break; + } + } + + XFreeModifiermap (modmap); + + return is_modifier; +} + + +gboolean +sugar_key_grabber_is_specific_modifier(SugarKeyGrabber *grabber, guint keycode, guint mask) +{ + Display *xdisplay; + XModifierKeymap *modmap; + gint start, end, i, mod_index; + gboolean is_modifier = FALSE; + + xdisplay = gdk_x11_drawable_get_xdisplay(GDK_DRAWABLE (grabber-root)); + + modmap = XGetModifierMapping (xdisplay); + + mod_index = 0; + mask = mask 1; + while (mask != 0) { + mask = mask 1; + mod_index += 1; + } + + start = mod_index * modmap-max_keypermod; + end = (mod_index + 1) * modmap-max_keypermod; + for (i = start; i end; i++) { + if (keycode == modmap-modifiermap[i]) { + is_modifier = TRUE; + break; + } + } + + XFreeModifiermap (modmap); + + return is_modifier; +} diff --git a/src/sugar/sugar-key-grabber.h b/src/sugar/sugar-key-grabber.h index 5b734e7..cf6efe2 100644 --- a/src/sugar/sugar-key-grabber.h +++ b/src/sugar/sugar-key-grabber.h @@ -60,6 +60,12 @@ void sugar_key_grabber_grab (SugarKeyGrabber *grabber, char*sugar_key_grabber_get_key (SugarKeyGrabber *grabber, guint keycode, guint state); +gboolean sugar_key_grabber_is_modifier (SugarKeyGrabber *grabber, + guintkeycode); +gboolean sugar_key_grabber_is_specific_modifier (SugarKeyGrabber *grabber, + guintkeycode, + guintmask); + G_END_DECLS ___ Sugar mailing list Sugar@lists.laptop.org http://lists.laptop.org/listinfo/sugar
[sugar] [PATCH 2/2] Add scroll_to_item functions to the trays to show a button that may be hidden.
--- src/sugar/graphics/tray.py | 25 + 1 files changed, 25 insertions(+), 0 deletions(-) diff --git a/src/sugar/graphics/tray.py b/src/sugar/graphics/tray.py index d7d5918..2f5db9a 100644 --- a/src/sugar/graphics/tray.py +++ b/src/sugar/graphics/tray.py @@ -67,6 +67,25 @@ class _TrayViewport(gtk.Viewport): elif direction == _NEXT_PAGE: self._scroll_next() +def scroll_to_item(self, item): +This function scrolls the viewport so that item will be visible. +assert item in self.traybar.get_children() + +# Get the allocation, and make sure that it is visible +if self.orientation == gtk.ORIENTATION_HORIZONTAL: +adj = self.get_hadjustment() +start = item.allocation.x +stop = item.allocation.x + item.allocation.width +else: +adj = self.get_vadjustment() +start = item.allocation.y +stop = item.allocation.y + item.allocation.height + +if start adj.value: +adj.value = start +elif stop adj.value + adj.page_size: +adj.value = stop - adj.page_size + def _scroll_next(self): allocation = self.get_allocation() if self.orientation == gtk.ORIENTATION_HORIZONTAL: @@ -218,6 +237,9 @@ class HTray(gtk.HBox): def get_item_index(self, item): return self._viewport.traybar.get_item_index(item) +def scroll_to_item(self, item): +self._viewport.scroll_to_item(item) + class VTray(gtk.VBox): def __init__(self, **kwargs): gobject.GObject.__init__(self, **kwargs) @@ -249,6 +271,9 @@ class VTray(gtk.VBox): def get_item_index(self, item): return self._viewport.traybar.get_item_index(item) +def scroll_to_item(self, item): +self._viewport.scroll_to_item(item) + class TrayButton(ToolButton): def __init__(self, **kwargs): ToolButton.__init__(self, **kwargs) ___ Sugar mailing list Sugar@lists.laptop.org http://lists.laptop.org/listinfo/sugar
[sugar] [PATCH 1/6] Add functionallity for tabbing trough activities.
To be able to raise activity windows on a delay, the homemodel needs to be set directly. It also needs to ignore window raises while the user is tabbing. --- src/model/homemodel.py | 15 +++ 1 files changed, 15 insertions(+), 0 deletions(-) diff --git a/src/model/homemodel.py b/src/model/homemodel.py index a75adcf..be46ffb 100644 --- a/src/model/homemodel.py +++ b/src/model/homemodel.py @@ -64,6 +64,7 @@ class HomeModel(gobject.GObject): self._activities = [] self._active_activity = None +self._tabbing = False screen = wnck.screen_get_default() screen.connect('window-opened', self._window_opened_cb) @@ -102,6 +103,15 @@ class HomeModel(gobject.GObject): Returns the activity that the user is currently working in return self._active_activity +def tabbing_set_activity(self, activity): +self._set_active_activity(activity) + +def tabbing_start(self): +self._tabbing = True + +def tabbing_stop(self): +self._tabbing = False + def _set_active_activity(self, home_activity): if self._active_activity == home_activity: return @@ -185,6 +195,11 @@ class HomeModel(gobject.GObject): logging.error(set_active() failed: %s % err) def _active_window_changed_cb(self, screen, previous_window=None): +if self._tabbing: +# Ignore any window changes when tabbing, as these are comming +# in delayed. +return + window = screen.get_active_window() if window is None: return ___ Sugar mailing list Sugar@lists.laptop.org http://lists.laptop.org/listinfo/sugar
[sugar] [PATCH 2/6] Implement API to handle tabbing.
This adds some functions to to start/stop the tabbing and to switch to the next and previous activity. It also takes care of raising the activities window after a short delay. --- src/view/Shell.py | 63 + 1 files changed, 63 insertions(+), 0 deletions(-) diff --git a/src/view/Shell.py b/src/view/Shell.py index b77465d..35f4073 100644 --- a/src/view/Shell.py +++ b/src/view/Shell.py @@ -57,6 +57,7 @@ class Shell(gobject.GObject): self._screen = wnck.screen_get_default() self._current_host = None self._screen_rotation = 0 +self._tabbing_timeout = None self._key_handler = KeyHandler() @@ -222,6 +223,68 @@ class Shell(gobject.GObject): self.take_activity_screenshot() next_activity.get_window().activate(gtk.get_current_event_time()) +def tabbing_activate_current(self): +home_model = self._model.get_home() +activity = home_model.get_active_activity() +if activity and activity.get_window(): +activity.get_window().activate(1) + +def __tabbing_timeout_cb(self): +self._tabbing_timeout = None +self.tabbing_activate_current() +return False + +def tabbing_previous_activity(self, first_switch): +home_model = self._model.get_home() +zoom_level = self._model.get_zoom_level() + +if first_switch and zoom_level != shellmodel.ShellModel.ZOOM_ACTIVITY: +activity = home_model.get_active_activity() +else: +activity = home_model.get_previous_activity() + +home_model.tabbing_set_activity(activity) + +if self._tabbing_timeout: +gobject.source_remove(self._tabbing_timeout) +self._tabbing_timeout = \ +gobject.timeout_add(250, self.__tabbing_timeout_cb) + +def tabbing_next_activity(self, first_switch): +home_model = self._model.get_home() +zoom_level = self._model.get_zoom_level() + +if first_switch and zoom_level != shellmodel.ShellModel.ZOOM_ACTIVITY: +activity = home_model.get_active_activity() +else: +activity = home_model.get_next_activity() + +home_model.tabbing_set_activity(activity) + +if self._tabbing_timeout: +gobject.source_remove(self._tabbing_timeout) +self._tabbing_timeout = \ +gobject.timeout_add(250, self.__tabbing_timeout_cb) + +def tabbing_start(self): +self.take_activity_screenshot() +self._frame.show(self._frame.MODE_NON_INTERACTIVE) + +home_model = self._model.get_home() +home_model.tabbing_start() + +def tabbing_stop(self): +self._frame.hide() + +if self._tabbing_timeout: +gobject.source_remove(self._tabbing_timeout) + +self.tabbing_activate_current() +self._model.get_home().tabbing_stop() + +home_model = self._model.get_home() +home_model.tabbing_stop() + def close_current_activity(self): if self._model.get_zoom_level() != shellmodel.ShellModel.ZOOM_ACTIVITY: return ___ Sugar mailing list Sugar@lists.laptop.org http://lists.laptop.org/listinfo/sugar
[sugar] [PATCH 3/6] Handle the keyboard event handling for tabbing.
--- src/view/keyhandler.py | 91 +++- 1 files changed, 89 insertions(+), 2 deletions(-) diff --git a/src/view/keyhandler.py b/src/view/keyhandler.py index 16f5a43..b42b93c 100644 --- a/src/view/keyhandler.py +++ b/src/view/keyhandler.py @@ -33,6 +33,10 @@ _BRIGHTNESS_STEP = 2 _VOLUME_STEP = 10 _BRIGHTNESS_MAX = 15 _VOLUME_MAX = 100 +# The modifier used for tabbing. Should the shortcuts ever be made user +# configurable, then some code to figure out the apropriate modifier is +# needed instead of hardcoding it. +_TABBING_MODIFIER = gtk.gdk.MOD1_MASK _actions_table = { 'F1' : 'zoom_mesh', @@ -78,10 +82,13 @@ class KeyHandler(object): self._keycode_pressed = 0 self._keystate_pressed = 0 self._speech_proxy = None +self._tabbing_windows = False self._key_grabber = KeyGrabber() self._key_grabber.connect('key-pressed', self._key_pressed_cb) +self._key_grabber.connect('key-released', + self._key_released_cb) for key in _actions_table.keys(): self._key_grabber.grab(key) @@ -132,15 +139,70 @@ class KeyHandler(object): self._get_speech_proxy().SayText(text, reply_handler=lambda: None, \ error_handler=self._on_speech_err) +def _window_tabbing(self, direction): +shell = view.Shell.get_instance() +if not self._tabbing_windows: +logging.debug('Grabing the input.') + +screen = gtk.gdk.screen_get_default() +window = screen.get_root_window() +keyboard_grab_result = gtk.gdk.keyboard_grab(window) +pointer_grab_result = gtk.gdk.pointer_grab(window) + +self._tabbing_windows = (keyboard_grab_result == gtk.gdk.GRAB_SUCCESS and + pointer_grab_result == gtk.gdk.GRAB_SUCCESS) + +# Now test that the modifier is still active to prevent race +# conditions. We also test if one of the grabs failed. +mask = window.get_pointer()[2] +if not self._tabbing_windows or not (mask _TABBING_MODIFIER): +logging.debug('Releasing grabs again.') + +if keyboard_grab_result != gtk.gdk.GRAB_SUCCESS: +gtk.gdk.keyboard_ungrab() +if pointer_grab_result != gtk.gdk.GRAB_SUCCESS: +gtk.gdk.pointer_ungrab() +self._tabbing_windows = False +else: +shell.tabbing_start() + +first_switch = True +else: +first_switch = False + +if self._tabbing_windows: +if direction == 1: +shell.tabbing_next_activity(first_switch) +else: +shell.tabbing_previous_activity(first_switch) + +return self._tabbing_windows + +def _stop_window_tabbing(self): +# Some useless key was pressed, or Alt released. +if not self._tabbing_windows: +return + +logging.debug('Releasing grabs again.') +gtk.gdk.keyboard_ungrab() +gtk.gdk.pointer_ungrab() + +shell = view.Shell.get_instance() +shell.tabbing_stop() + +self._tabbing_windows = False + def handle_say_text(self): clipboard = gtk.clipboard_get(selection=PRIMARY) clipboard.request_text(self._primary_selection_cb) def handle_previous_window(self): -view.Shell.get_instance().activate_previous_activity() +if not self._window_tabbing(-1): +view.Shell.get_instance().activate_previous_activity() def handle_next_window(self): -view.Shell.get_instance().activate_next_activity() +if not self._window_tabbing(1): +view.Shell.get_instance().activate_next_activity() def handle_close_window(self): view.Shell.get_instance().close_current_activity() @@ -252,9 +314,34 @@ class KeyHandler(object): self._keystate_pressed = state action = _actions_table[key] +if self._tabbing_windows: +# Only accept window tabbing events, everything else +# cancels the tabbing operation. +if not action in [next_window, previous_window]: +self._stop_window_tabbing() +return True + method = getattr(self, 'handle_' + action) method() return True +else: +# If this is not a registered key, then cancel any active +# tabbing. +if self._tabbing_windows: +if not grabber.is_modifier(keycode): +self._stop_window_tabbing() +return True return False + +def _key_released_cb(self, grabber, keycode, state): +if self._tabbing_windows: +
[sugar] [PATCH 4/6] Ignore click events during the activity_changed_cb.
It is neccessary to ignore any click events that happen as a result of the activity_changed_cb. If these were not ignored they would cause an instant activity switch to the new activity. --- src/view/frame/activitiestray.py |6 +- 1 files changed, 5 insertions(+), 1 deletions(-) diff --git a/src/view/frame/activitiestray.py b/src/view/frame/activitiestray.py index 0c5b3f8..03a523e 100644 --- a/src/view/frame/activitiestray.py +++ b/src/view/frame/activitiestray.py @@ -302,6 +302,7 @@ class ActivitiesTray(HTray): self._buttons = {} self._invite_to_item = {} +self._freeze_button_clicks = False self._home_model = shellmodel.get_instance().get_home() self._home_model.connect('activity-added', self.__activity_added_cb) @@ -336,11 +337,14 @@ class ActivitiesTray(HTray): def __activity_changed_cb(self, home_model, home_activity): logging.debug('__activity_changed_cb: %r' % home_activity) + button = self._buttons[home_activity.get_activity_id()] +self._freeze_button_clicks = True button.props.active = True +self._freeze_button_clicks = True def __activity_clicked_cb(self, button, home_activity): -if button.props.active: +if not self._freeze_button_clicks and button.props.active: logging.debug('ActivitiesTray.__activity_clicked_cb') window = home_activity.get_window() if window: ___ Sugar mailing list Sugar@lists.laptop.org http://lists.laptop.org/listinfo/sugar
[sugar] [PATCH 5/6] Scroll to the button that is selected in the tabbing operation.
--- src/view/frame/activitiestray.py |2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/src/view/frame/activitiestray.py b/src/view/frame/activitiestray.py index 03a523e..c34c119 100644 --- a/src/view/frame/activitiestray.py +++ b/src/view/frame/activitiestray.py @@ -342,6 +342,8 @@ class ActivitiesTray(HTray): self._freeze_button_clicks = True button.props.active = True self._freeze_button_clicks = True + +self.scroll_to_item(button) def __activity_clicked_cb(self, button, home_activity): if not self._freeze_button_clicks and button.props.active: ___ Sugar mailing list Sugar@lists.laptop.org http://lists.laptop.org/listinfo/sugar
[sugar] [PATCH 6/6] Use Alt+Tab for reverse tabbing direction
--- src/view/keyhandler.py |2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/src/view/keyhandler.py b/src/view/keyhandler.py index b42b93c..ab779ec 100644 --- a/src/view/keyhandler.py +++ b/src/view/keyhandler.py @@ -59,7 +59,7 @@ _actions_table = { 'altq' : 'quit_emulator', 'altTab' : 'next_window', 'altn' : 'next_window', -'ctrlaltTab' : 'previous_window', +'altshiftTab': 'previous_window', 'altp' : 'previous_window', 'ctrlEscape' : 'close_window', 'ctrlq': 'close_window', ___ Sugar mailing list Sugar@lists.laptop.org http://lists.laptop.org/listinfo/sugar
Re: [sugar] [PATCH] Change tabbing to show a preview by opening the frame
On Tue, 2008-06-10 at 09:58 +0200, Tomeu Vizoso wrote: On Thu, Jun 5, 2008 at 12:20 AM, Benjamin Berg Unfortunately there are a couple of issues with palettes. One issue is that that at first only the primary palette is shown, but after a while the secondary palette will pop up (as the timeout is started automatically). Can you look at how the secondary palette is displayed by the right click? I understand that's what you want, right? Yup, something like that. Though it will need to be handled differently than the right click code. I guess just adding another parameter to popdown will work. Then I am accessing the _update_position function of the Palette directly, to handle disappearing activities properly. You mean to move left the palette when another activity icon disappears? Perhaps the Invoker should be listening for position changes on the observed widget and move the palette accordingly? Yeah, something like that. Though I am not sure what is needed to get it working right now. Before looking in detail at the code, have you considered relying on the window manager to do the tabbing and have the shell to just observe the changes on the tabbing stack? Perhaps wnck can help on that? To say the truth I did not even consider it, as tabbing is already handled by Sugar in the existing code. That said, I just had a quick look, but could not find anything in metacity or libwnck that suggests that leaving tabbing to the window manager is possible. Benjamin signature.asc Description: This is a digitally signed message part ___ Sugar mailing list Sugar@lists.laptop.org http://lists.laptop.org/listinfo/sugar
[sugar] [PATCH] Change tabbing to show a preview by opening the frame
Hello, Some patches that implement nicer tabbing will follow. With the patches applied, pressing Alt+Tab will show the frame. The activity that will be selected as soon as Alt is released again is shown by popping up its palette. Unfortunately there are a couple of issues with palettes. One issue is that that at first only the primary palette is shown, but after a while the secondary palette will pop up (as the timeout is started automatically). Then I am accessing the _update_position function of the Palette directly, to handle disappearing activities properly. The third thing I noticed is that there is a race condition in the Palette (which I think is my fault ...). I'll need to have a look at how to fix this. (The reason for the race is the code that delays the popup signal until the window has been mapped. IIRC that was needed so the gab drawing would work properly.) Benjamin signature.asc Description: This is a digitally signed message part ___ Sugar mailing list Sugar@lists.laptop.org http://lists.laptop.org/listinfo/sugar
[sugar] [PATCH 1/5] Add an unobscured property that is set when the frame is fully visible.
This commit switches the Frame to be a GObject. By doing this the gobject property system can be used to notify interested parties when the frame becomes fully visible. This is needed, so that the ActivitiesTray can show the palette of an activity as soon as the frame is fully visible. --- src/view/frame/frame.py | 21 +++-- 1 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/view/frame/frame.py b/src/view/frame/frame.py index 6009e7f..9e5af71 100644 --- a/src/view/frame/frame.py +++ b/src/view/frame/frame.py @@ -89,12 +89,19 @@ class _KeyListener(object): else: self._frame.show(Frame.MODE_KEYBOARD) -class Frame(object): +class Frame(gobject.GObject): MODE_MOUSE= 0 MODE_KEYBOARD = 1 MODE_NON_INTERACTIVE = 2 +__gtype_name__ = SugarFrame +__gproperties__ = { +'unobscured' : (bool, None, None, False, +gobject.PARAM_READABLE), +} def __init__(self): +gobject.GObject.__init__(self) + self.mode = None self._palette_group = palettegroup.get_group('frame') @@ -106,6 +113,7 @@ class Frame(object): self._bottom_panel = None self.current_position = 0.0 +self._unobscured = False self._animator = None self._event_area = EventArea() @@ -248,6 +256,11 @@ class Frame(object): self._move_panel(self._right_panel, self.current_position, screen_w, 0, screen_w - self._right_panel.size, 0) +unobscured = (self.current_position == 1.0) +if unobscured != self._unobscured: +self._unobscured = unobscured +self.notify('unobscured') + def _size_changed_cb(self, screen): self._update_position() @@ -274,7 +287,11 @@ class Frame(object): def _enter_corner_cb(self, event_area): self._mouse_listener.mouse_enter() - + +def do_get_property(self, pspec): +if pspec.name == 'unobscured': +return self._unobscured + def notify_key_press(self): self._key_listener.key_press() ___ Sugar mailing list Sugar@lists.laptop.org http://lists.laptop.org/listinfo/sugar
[sugar] [PATCH 2/5] Add functionallity for tabbing trough activities.
This commits adds some functions and properties to keep track of the activities when the user is tabbing. --- src/model/homemodel.py | 61 1 files changed, 61 insertions(+), 0 deletions(-) diff --git a/src/model/homemodel.py b/src/model/homemodel.py index 5538f84..032f019 100644 --- a/src/model/homemodel.py +++ b/src/model/homemodel.py @@ -53,6 +53,9 @@ class HomeModel(gobject.GObject): ([gobject.TYPE_PYOBJECT])), 'pending-activity-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])), +'tabbing-activity-changed': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])) } @@ -62,6 +65,7 @@ class HomeModel(gobject.GObject): self._activities = [] self._active_activity = None self._pending_activity = None +self._tabbing_activity = None screen = wnck.screen_get_default() screen.connect('window-opened', self._window_opened_cb) @@ -126,6 +130,42 @@ class HomeModel(gobject.GObject): return self._active_activity +def tabbing_previous_activity(self): +activities = self._get_activities_with_window() +if len(activities) == 0: +return + +activity = self._tabbing_activity +if activity is None: +activity = self._pending_activity + +i = activities.index(activity) +if i - 1 = 0: +self._set_tabbing_activity(activities[i - 1]) +else: +self._set_tabbing_activity(activities[len(activities) - 1]) + +def tabbing_next_activity(self): +activities = self._get_activities_with_window() +if len(activities) == 0: +return + +activity = self._tabbing_activity +if activity is None: +activity = self._pending_activity + +i = activities.index(activity) +if i + 1 len(activities): +self._set_tabbing_activity(activities[i + 1]) +else: +self._set_tabbing_activity(activities[0]) + +def tabbing_cancel(self): +self._set_tabbing_activity(None) + +def get_tabbing_activity(self): +return self._tabbing_activity + def _set_active_activity(self, home_activity): if self._active_activity == home_activity: return @@ -146,6 +186,13 @@ class HomeModel(gobject.GObject): self._active_activity = home_activity self.emit('active-activity-changed', self._active_activity) +def _set_tabbing_activity(self, tabbing_activity): +if self._tabbing_activity == tabbing_activity: +return + +self._tabbing_activity = tabbing_activity +self.emit('tabbing-activity-changed', self._tabbing_activity) + def __iter__(self): return iter(self._activities) @@ -243,6 +290,20 @@ class HomeModel(gobject.GObject): logging.error('No activities are running') self._set_pending_activity(None) +if home_activity == self._tabbing_activity: +# Find a new tabbing activity +activities = self._get_activities_with_window() +if len(activities) = 1: +# There is no other activity to tab to ... +self._set_tabbing_activity(None) + +i = activities.index(self._tabbing_activity) +if i + 1 len(activities): +self._set_tabbing_activity(activities[i + 1]) +else: +# Do not wrap, but instead select the last activity again +self._set_tabbing_activity(activities[i - 1]) + self.emit('activity-removed', home_activity) self._activities.remove(home_activity) ___ Sugar mailing list Sugar@lists.laptop.org http://lists.laptop.org/listinfo/sugar
[sugar] [PATCH 4/5] Handle the key events for tabbing.
This adds the neccessary keyboard handling, grabbing the input, switching to the new activity, or canceling the operation. It is also responsible for showing the frame during the tabbing operation. --- src/view/keyhandler.py | 96 +++- 1 files changed, 94 insertions(+), 2 deletions(-) diff --git a/src/view/keyhandler.py b/src/view/keyhandler.py index 306bb21..8dee50c 100644 --- a/src/view/keyhandler.py +++ b/src/view/keyhandler.py @@ -32,6 +32,10 @@ _BRIGHTNESS_STEP = 2 _VOLUME_STEP = 10 _BRIGHTNESS_MAX = 15 _VOLUME_MAX = 100 +# The modifier used for tabbing. Should the shortcuts ever be made user +# configurable, then some code to figure out the apropriate modifier is +# needed instead of hardcoding it. +_TABBING_MODIFIER = gtk.gdk.MOD1_MASK _actions_table = { 'F1' : 'zoom_mesh', @@ -77,10 +81,13 @@ class KeyHandler(object): self._keycode_pressed = 0 self._keystate_pressed = 0 self._speech_proxy = None +self._tabbing_windows = False self._key_grabber = KeyGrabber() self._key_grabber.connect('key-pressed', self._key_pressed_cb) +self._key_grabber.connect('key-released', + self._key_released_cb) for key in _actions_table.keys(): self._key_grabber.grab(key) @@ -131,15 +138,75 @@ class KeyHandler(object): self._get_speech_proxy().SayText(text, reply_handler=lambda: None, \ error_handler=self._on_speech_err) +def _window_tabbing(self, direction): +if not self._tabbing_windows: +logging.debug('Grabing the input.') + +screen = gtk.gdk.screen_get_default() +window = screen.get_root_window() +keyboard_grab_result = gtk.gdk.keyboard_grab(window) +pointer_grab_result = gtk.gdk.pointer_grab(window) + +self._tabbing_windows = (keyboard_grab_result == gtk.gdk.GRAB_SUCCESS and + pointer_grab_result == gtk.gdk.GRAB_SUCCESS) + +# Now test that the modifier is still active to prevent race +# conditions. We also test if one of the grabs failed. +mask = window.get_pointer()[2] +if not self._tabbing_windows or not (mask _TABBING_MODIFIER): +logging.debug('Releasing grabs again.') + +if keyboard_grab_result != gtk.gdk.GRAB_SUCCESS: +gtk.gdk.keyboard_ungrab() +if pointer_grab_result != gtk.gdk.GRAB_SUCCESS: +gtk.gdk.pointer_ungrab() +self._tabbing_windows = False + +if self._tabbing_windows: +# We have the grab, so show the frame. +shell = view.Shell.get_instance() +frame = shell.get_frame() +frame.show(frame.MODE_NON_INTERACTIVE) + +if direction == 1: +shell.tabbing_next_activity() +else: +shell.tabbing_previous_activity() + +return self._tabbing_windows + +def _stop_window_tabbing(self, cancel=True): +# Some useless key was pressed, or Alt released. +if not self._tabbing_windows: +return + +logging.debug('Releasing grabs again.') +gtk.gdk.keyboard_ungrab() +gtk.gdk.pointer_ungrab() + +shell = view.Shell.get_instance() +frame = shell.get_frame() + +if not cancel: +shell.tabbing_activate_current() +else: +shell.tabbing_cancel() + +frame.hide() + +self._tabbing_windows = False + def handle_say_text(self): clipboard = gtk.clipboard_get(selection=PRIMARY) clipboard.request_text(self._primary_selection_cb) def handle_previous_window(self): -view.Shell.get_instance().activate_previous_activity() +if not self._window_tabbing(-1): +view.Shell.get_instance().activate_previous_activity() def handle_next_window(self): -view.Shell.get_instance().activate_next_activity() +if not self._window_tabbing(1): +view.Shell.get_instance().activate_next_activity() def handle_close_window(self): view.Shell.get_instance().close_current_activity() @@ -218,9 +285,34 @@ class KeyHandler(object): self._keystate_pressed = state action = _actions_table[key] +if self._tabbing_windows: +# Only accept window tabbing events, everything else +# cancels the tabbing operation. +if not action in [next_window, previous_window]: +self._stop_window_tabbing(True) +return True + method = getattr(self, 'handle_' + action) method() return True +else: +# If this is not a registered