Re: [sugar] [Cross Posted] High res screenshots of Sugar

2008-10-10 Thread Benjamin Berg
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

2008-09-28 Thread Benjamin Berg
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

2008-09-28 Thread Benjamin Berg
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.

2008-06-20 Thread Benjamin Berg
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

2008-06-19 Thread Benjamin Berg
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.

2008-06-19 Thread Benjamin Berg

---

 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.

2008-06-19 Thread Benjamin Berg

---

 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.

2008-06-19 Thread Benjamin Berg
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.

2008-06-19 Thread Benjamin Berg
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.

2008-06-19 Thread Benjamin Berg

---

 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.

2008-06-19 Thread Benjamin Berg
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.

2008-06-19 Thread Benjamin Berg

---

 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

2008-06-19 Thread Benjamin Berg

---

 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

2008-06-10 Thread Benjamin Berg
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

2008-06-04 Thread Benjamin Berg
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.

2008-06-04 Thread Benjamin Berg
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.

2008-06-04 Thread Benjamin Berg
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.

2008-06-04 Thread Benjamin Berg
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