diff --git a/configure.ac b/configure.ac index 4d8a2f98..9a5b0581 100644 --- a/configure.ac +++ b/configure.ac @@ -66,6 +66,7 @@ sugar/activity/Makefile sugar/canvas/Makefile sugar/chat/Makefile sugar/chat/sketchpad/Makefile +sugar/graphics/Makefile sugar/p2p/Makefile sugar/p2p/model/Makefile sugar/presence/Makefile diff --git a/shell/sugar-activity-factory b/shell/sugar-activity-factory index 7be73d15..aeba2df7 100755 --- a/shell/sugar-activity-factory +++ b/shell/sugar-activity-factory @@ -4,11 +4,12 @@ import logging import pygtk pygtk.require('2.0') +import gobject import gtk import dbus.glib # Work around for dbus mutex locking issue -gtk.gdk.threads_init() +gobject.threads_init() dbus.glib.threads_init() from sugar.activity import ActivityFactory diff --git a/shell/view/frame/BottomPanel.py b/shell/view/frame/ActivitiesBox.py similarity index 54% rename from shell/view/frame/BottomPanel.py rename to shell/view/frame/ActivitiesBox.py index 7509576f..d46f3bed 100644 --- a/shell/view/frame/BottomPanel.py +++ b/shell/view/frame/ActivitiesBox.py @@ -1,26 +1,25 @@ -import gtk -import goocanvas +import hippo import logging import conf -from sugar.canvas.IconItem import IconItem -from sugar.canvas.IconColor import IconColor +from sugar.graphics.canvasicon import CanvasIcon from sugar.presence import PresenceService -from sugar.canvas.CanvasBox import CanvasBox +from sugar.graphics import style -class ActivityItem(IconItem): +class ActivityItem(CanvasIcon): def __init__(self, activity): icon_name = activity.get_icon() - IconItem.__init__(self, icon_name=icon_name, color=IconColor('white')) + CanvasIcon.__init__(self, icon_name=icon_name) + style.apply_stylesheet(self, 'frame-activity-icon') self._activity = activity def get_bundle_id(self): return self._activity.get_id() -class InviteItem(IconItem): +class InviteItem(CanvasIcon): def __init__(self, invite): - IconItem.__init__(self, icon_name=invite.get_icon(), - color=invite.get_color()) + CanvasIcon.__init__(self, icon_name=invite.get_icon(), + color=invite.get_color()) self._invite = invite def get_activity_id(self): @@ -32,9 +31,9 @@ class InviteItem(IconItem): def get_invite(self): return self._invite -class BottomPanel(CanvasBox): +class ActivitiesBox(hippo.CanvasBox): def __init__(self, shell): - CanvasBox.__init__(self, shell.get_grid(), CanvasBox.HORIZONTAL) + hippo.CanvasBox.__init__(self, orientation=hippo.ORIENTATION_HORIZONTAL) self._shell = shell self._invite_to_item = {} @@ -47,37 +46,35 @@ class BottomPanel(CanvasBox): for invite in self._invites: self.add_invite(invite) - self._invites.connect('invite-added', self.__invite_added_cb) - self._invites.connect('invite-removed', self.__invite_removed_cb) + self._invites.connect('invite-added', self._invite_added_cb) + self._invites.connect('invite-removed', self._invite_removed_cb) - def __activity_clicked_cb(self, icon): + def _activity_clicked_cb(self, icon): self._shell.start_activity(icon.get_bundle_id()) - def __invite_clicked_cb(self, icon): + def _invite_clicked_cb(self, icon): self._invites.remove_invite(icon.get_invite()) self._shell.join_activity(icon.get_bundle_id(), icon.get_activity_id()) - def __invite_added_cb(self, invites, invite): + def _invite_added_cb(self, invites, invite): self.add_invite(invite) - def __invite_removed_cb(self, invites, invite): + def _invite_removed_cb(self, invites, invite): self.remove_invite(invite) def add_activity(self, activity): item = ActivityItem(activity) - item.connect('clicked', self.__activity_clicked_cb) - self.set_constraints(item, 5, 5) - self.add_child(item) + item.connect('activated', self._activity_clicked_cb) + self.append(item, 0) def add_invite(self, invite): item = InviteItem(invite) - item.connect('clicked', self.__invite_clicked_cb) - self.set_constraints(item, 5, 5) - self.add_child(item, 0) + item.connect('activated', self._invite_clicked_cb) + self.append(item, 0) self._invite_to_item[invite] = item def remove_invite(self, invite): - self.remove_child(self._invite_to_item[invite]) + self.remove(self._invite_to_item[invite]) del self._invite_to_item[invite] diff --git a/shell/view/frame/Frame.py b/shell/view/frame/Frame.py index 0206080b..bd7e51f4 100644 --- a/shell/view/frame/Frame.py +++ b/shell/view/frame/Frame.py @@ -1,15 +1,14 @@ import gtk import gobject -import goocanvas +import hippo import wnck -from view.frame.BottomPanel import BottomPanel -from view.frame.RightPanel import RightPanel -from view.frame.TopPanel import TopPanel +from view.frame.ActivitiesBox import ActivitiesBox +from view.frame.ZoomBox import ZoomBox from view.frame.PanelWindow import PanelWindow -from sugar.canvas.Grid import Grid from sugar.canvas.Timeline import Timeline from sugar.canvas.MenuShell import MenuShell +from sugar.graphics.grid import Grid class EventFrame(gobject.GObject): __gsignals__ = { @@ -116,51 +115,52 @@ class Frame: 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() - - grid = shell.get_grid() - self._menu_shell = MenuShell(grid) - self._menu_shell.connect('activated', self._menu_shell_activated_cb) - self._menu_shell.connect('deactivated', self._menu_shell_deactivated_cb) - - bg = goocanvas.Rect(fill_color="#4f4f4f", line_width=0) - grid.set_constraints(bg, 0, 0, 80, 60) - root.add_child(bg) - - panel = BottomPanel(shell) - grid.set_constraints(panel, 5, 55) - root.add_child(panel) - - self._add_panel(model, 0, 55, 80, 5) - - panel = TopPanel(shell, self._menu_shell) - root.add_child(panel) - - self._add_panel(model, 0, 0, 80, 5) - - panel = RightPanel(shell, self._menu_shell) - grid.set_constraints(panel, 75, 5) - root.add_child(panel) - - self._add_panel(model, 75, 5, 5, 50) - - self._add_panel(model, 0, 5, 5, 50) - self._event_frame = EventFrame() self._event_frame.connect('enter-edge', self._enter_edge_cb) self._event_frame.connect('enter-corner', self._enter_corner_cb) self._event_frame.connect('leave', self._event_frame_leave_cb) self._event_frame.show() - def _add_panel(self, model, x, y, width, height): - grid = self._shell.get_grid() + grid = Grid() - panel_window = PanelWindow(grid, model, x, y, width, height) - panel_window.connect('enter-notify-event', self._enter_notify_cb) - panel_window.connect('leave-notify-event', self._leave_notify_cb) + self._menu_shell = MenuShell(grid) + self._menu_shell.connect('activated', self._menu_shell_activated_cb) + self._menu_shell.connect('deactivated', self._menu_shell_deactivated_cb) - self._windows.append(panel_window) + top_panel = self._create_panel(grid, 0, 0, 16, 1) + + box = ZoomBox(self._shell, self._menu_shell) + top_panel.append(box, hippo.PACK_FIXED) + + [x, y] = grid.point(1, 0) + top_panel.move(box, x, y) + + bottom_panel = self._create_panel(grid, 0, 11, 16, 1) + + box = ActivitiesBox(self._shell) + bottom_panel.append(box, hippo.PACK_FIXED) + + [x, y] = grid.point(1, 0) + bottom_panel.move(box, x, y) + + left_panel = self._create_panel(grid, 0, 1, 1, 10) + + right_panel = self._create_panel(grid, 15, 1, 1, 10) + + def _create_panel(self, grid, x, y, width, height): + panel = PanelWindow() + + panel.connect('enter-notify-event', self._enter_notify_cb) + panel.connect('leave-notify-event', self._leave_notify_cb) + + [x, y, width, height] = grid.rectangle(x, y, width, height) + + panel.move(x, y) + panel.resize(width, height) + + self._windows.append(panel) + + return panel.get_root() def _menu_shell_activated_cb(self, menu_shell): self._timeline.goto('slide_in', True) diff --git a/shell/view/frame/MenuStrategy.py b/shell/view/frame/MenuStrategy.py index 314cb047..0519d234 100644 --- a/shell/view/frame/MenuStrategy.py +++ b/shell/view/frame/MenuStrategy.py @@ -1,28 +1,15 @@ +from sugar.graphics.grid import Grid + class MenuStrategy: - def get_menu_position(self, menu, grid_x1, grid_y1, grid_x2, grid_y2): - grid = menu.get_grid() + def get_menu_position(self, menu, x, y, width, height): + grid = Grid() - [x1, y1] = grid.micro_to_macro(grid_x1, grid_y1) - [x2, y2] = grid.micro_to_macro(grid_x2, grid_y2) + [grid_x1, grid_y1] = grid.fit_point(x, y) + [grid_x2, grid_y2] = grid.fit_point(x + width, y + height) - if x1 == 0: - x = x2 - y = y1 - elif x2 == grid.get_macro_cols(): - x = x1 - y = y1 - elif y2 == grid.get_macro_rows(): - x = x1 - y = y1 - else: - x = x1 - y = y2 + menu_grid_x = grid_x1 + menu_grid_y = grid_y2 - [grid_x, grid_y] = grid.macro_to_micro(x, y) + [menu_x, menu_y] = grid.point(menu_grid_x, menu_grid_y) - if x2 == grid.get_macro_cols(): - grid_x -= menu.get_width() - elif y2 == grid.get_macro_rows(): - grid_y -= menu.get_height() - - return [grid_x, grid_y] + return [menu_x, menu_y] diff --git a/shell/view/frame/PanelWindow.py b/shell/view/frame/PanelWindow.py index 549776f5..0c2930cd 100644 --- a/shell/view/frame/PanelWindow.py +++ b/shell/view/frame/PanelWindow.py @@ -1,27 +1,24 @@ import gtk -import goocanvas - -from sugar.canvas.CanvasView import CanvasView +import hippo class PanelWindow(gtk.Window): - def __init__(self, grid, model, x, y, width, height): + def __init__(self): gtk.Window.__init__(self) - self._grid = grid - self.set_decorated(False) + self.connect('realize', self._realize_cb) - self.realize() + canvas = hippo.Canvas() + + self._bg = hippo.CanvasBox(background_color=0x4f4f4fff) + canvas.set_root(self._bg) + + self.add(canvas) + canvas.show() + + def get_root(self): + return self._bg + + def _realize_cb(self, widget): self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) self.window.set_accept_focus(False) - - screen = gtk.gdk.screen_get_default() - self.window.set_transient_for(screen.get_root_window()) - - view = CanvasView() - view.show() - self.add(view) - view.set_model(model) - - self._grid.set_constraints(self, x, y, width, height) - self._grid.set_constraints(view, x, y, width, height) diff --git a/shell/view/frame/RightPanel.py b/shell/view/frame/RightPanel.py index 28c81b3a..9c164020 100644 --- a/shell/view/frame/RightPanel.py +++ b/shell/view/frame/RightPanel.py @@ -1,16 +1,15 @@ -import goocanvas +import hippo -from sugar.canvas.IconItem import IconItem +from sugar.graphics.CanvasIcon import CanvasIcon from sugar.canvas.IconColor import IconColor -from sugar.canvas.CanvasBox import CanvasBox from sugar.presence import PresenceService from view.BuddyIcon import BuddyIcon from model.BuddyModel import BuddyModel from view.frame.MenuStrategy import MenuStrategy -class RightPanel(CanvasBox): +class RightPanel(hippo.CanvasBox): def __init__(self, shell, menu_shell): - CanvasBox.__init__(self, shell.get_grid(), CanvasBox.VERTICAL) + CanvasBox.__init__(self) self._shell = shell self._menu_shell = menu_shell self._activity_ps = None @@ -28,8 +27,7 @@ class RightPanel(CanvasBox): model = BuddyModel(buddy=buddy) icon = BuddyIcon(self._shell, self._menu_shell, model) icon.set_menu_strategy(MenuStrategy()) - self.set_constraints(icon, 5, 5) - self.add_child(icon) + self.append(icon, 0) self._buddies[buddy.get_name()] = icon diff --git a/shell/view/frame/TopPanel.py b/shell/view/frame/ZoomBox.py similarity index 54% rename from shell/view/frame/TopPanel.py rename to shell/view/frame/ZoomBox.py index e0aec2ea..26fda37c 100644 --- a/shell/view/frame/TopPanel.py +++ b/shell/view/frame/ZoomBox.py @@ -1,9 +1,10 @@ import goocanvas +import hippo -from sugar.canvas.CanvasBox import CanvasBox -from sugar.canvas.IconItem import IconItem -from sugar.canvas.MenuIcon import MenuIcon -from sugar.canvas.Menu import Menu +from sugar.graphics.canvasicon import CanvasIcon +from sugar.graphics.menuicon import MenuIcon +from sugar.graphics.menu import Menu +from sugar.graphics import style from view.frame.MenuStrategy import MenuStrategy import sugar @@ -12,13 +13,14 @@ class ActivityMenu(Menu): ACTION_CLOSE = 2 def __init__(self, grid, activity_host): - title = activity_host.get_title() - Menu.__init__(self, grid, title) + Menu.__init__(self, activity_host.get_title()) - icon = IconItem(icon_name='stock-share-mesh') + icon = CanvasIcon(icon_name='stock-share-mesh') + style.apply_stylesheet(icon, 'menu-action-icon') self.add_action(icon, ActivityMenu.ACTION_SHARE) - icon = IconItem(icon_name='stock-close') + icon = CanvasIcon(icon_name='stock-close') + style.apply_stylesheet(icon, 'menu-action-icon') self.add_action(icon, ActivityMenu.ACTION_CLOSE) class ActivityIcon(MenuIcon): @@ -51,53 +53,45 @@ class ActivityIcon(MenuIcon): if action == ActivityMenu.ACTION_CLOSE: activity.close() -class TopPanel(goocanvas.Group): +class ZoomBox(hippo.CanvasBox): def __init__(self, shell, menu_shell): - goocanvas.Group.__init__(self) + hippo.CanvasBox.__init__(self, orientation=hippo.ORIENTATION_HORIZONTAL) self._shell = shell self._menu_shell = menu_shell self._activity_icon = None - grid = shell.get_grid() + icon = CanvasIcon(icon_name='stock-zoom-mesh') + style.apply_stylesheet(icon, 'frame-zoom-icon') + icon.connect('activated', self._level_clicked_cb, sugar.ZOOM_MESH) + self.append(icon) - box = CanvasBox(grid, CanvasBox.HORIZONTAL) - grid.set_constraints(box, 5, 0) - self.add_child(box) + icon = CanvasIcon(icon_name='stock-zoom-friends') + style.apply_stylesheet(icon, 'frame-zoom-icon') + icon.connect('activated', self._level_clicked_cb, sugar.ZOOM_FRIENDS) + self.append(icon) - icon = IconItem(icon_name='stock-zoom-mesh') - icon.connect('clicked', self._level_clicked_cb, sugar.ZOOM_MESH) - box.set_constraints(icon, 5, 5) - box.add_child(icon) + icon = CanvasIcon(icon_name='stock-zoom-home') + style.apply_stylesheet(icon, 'frame-zoom-icon') + icon.connect('activated', self._level_clicked_cb, sugar.ZOOM_HOME) + self.append(icon) - icon = IconItem(icon_name='stock-zoom-friends') - icon.connect('clicked', self._level_clicked_cb, sugar.ZOOM_FRIENDS) - box.set_constraints(icon, 5, 5) - box.add_child(icon) - - icon = IconItem(icon_name='stock-zoom-home') - icon.connect('clicked', self._level_clicked_cb, sugar.ZOOM_HOME) - box.set_constraints(icon, 5, 5) - box.add_child(icon) - - icon = IconItem(icon_name='stock-zoom-activity') - icon.connect('clicked', self._level_clicked_cb, sugar.ZOOM_ACTIVITY) - box.set_constraints(icon, 5, 5) - box.add_child(icon) - - self._box = box + icon = CanvasIcon(icon_name='stock-zoom-activity') + style.apply_stylesheet(icon, 'frame-zoom-icon') + icon.connect('activated', self._level_clicked_cb, sugar.ZOOM_ACTIVITY) + self.append(icon) shell.connect('activity-changed', self._activity_changed_cb) self._set_current_activity(shell.get_current_activity()) def _set_current_activity(self, activity): if self._activity_icon: - self._box.remove_child(self._activity_icon) + self.remove(self._activity_icon) if activity: icon = ActivityIcon(self._shell, self._menu_shell, activity) - self._box.set_constraints(icon, 5, 5) - self._box.add_child(icon) + style.apply_stylesheet(icon, 'frame-zoom-icon') + self.append(icon, 0) self._activity_icon = icon else: self._activity_icon = None diff --git a/sugar/Makefile.am b/sugar/Makefile.am index 2cee687d..0e653511 100644 --- a/sugar/Makefile.am +++ b/sugar/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = activity canvas chat p2p presence session +SUBDIRS = activity canvas chat graphics p2p presence session sugardir = $(pythondir)/sugar sugar_PYTHON = \ diff --git a/sugar/graphics/Makefile.am b/sugar/graphics/Makefile.am new file mode 100644 index 00000000..a5f81664 --- /dev/null +++ b/sugar/graphics/Makefile.am @@ -0,0 +1,5 @@ +sugardir = $(pythondir)/sugar/graphics +sugar_PYTHON = \ + __init__.py \ + canvasicon.py \ + grid.py diff --git a/sugar/graphics/__init__.py b/sugar/graphics/__init__.py new file mode 100644 index 00000000..b494b9f9 --- /dev/null +++ b/sugar/graphics/__init__.py @@ -0,0 +1,25 @@ +import gtk + +from sugar.graphics import style +from sugar.canvas.IconColor import IconColor + +if gtk.gdk.screen_width() == 1200: + _medium_icon_size = 75 +else: + _medium_icon_size = 50 + +_stylesheet = { + 'color' : IconColor('white'), + 'size' : _medium_icon_size +} +style.register_stylesheet('frame-activity-icon', _stylesheet) + +_stylesheet = { + 'size' : _medium_icon_size +} +style.register_stylesheet('frame-zoom-icon', _stylesheet) + +_stylesheet = { + 'size' : _medium_icon_size +} +style.register_stylesheet('menu-action-icon', _stylesheet) diff --git a/sugar/graphics/canvasicon.py b/sugar/graphics/canvasicon.py new file mode 100644 index 00000000..17a0193d --- /dev/null +++ b/sugar/graphics/canvasicon.py @@ -0,0 +1,139 @@ +import re + +import gobject +import gtk +import hippo +import rsvg +import cairo + +from sugar.canvas.IconColor import IconColor + +class _IconCache: + def __init__(self): + self._icons = {} + self._theme = gtk.icon_theme_get_default() + + def _read_icon(self, filename, color): + icon_file = open(filename, 'r') + + if color == None: + return rsvg.Handle(file=filename) + else: + data = icon_file.read() + icon_file.close() + + fill = color.get_fill_color() + stroke = color.get_stroke_color() + + entity = '' % fill + data = re.sub('', entity, data) + + entity = '' % stroke + data = re.sub('', entity, data) + + return rsvg.Handle(data=data) + + def get_handle(self, name, color, size): + info = self._theme.lookup_icon(name, int(size), 0) + + if color: + key = (info.get_filename(), color.to_string()) + else: + key = info.get_filename() + + if self._icons.has_key(key): + icon = self._icons[key] + else: + icon = self._read_icon(info.get_filename(), color) + self._icons[key] = icon + return icon + +class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem): + __gtype_name__ = 'CanvasIcon' + + __gproperties__ = { + 'icon-name': (str, None, None, None, + gobject.PARAM_READWRITE), + 'color' : (object, None, None, + gobject.PARAM_READWRITE), + 'size' : (int, None, None, + 0, 1024, 24, + gobject.PARAM_READWRITE) + } + + _cache = _IconCache() + + def __init__(self, **kwargs): + self._size = 24 + self._color = None + self._icon_name = None + + hippo.CanvasBox.__init__(self, **kwargs) + + self._buffer = None + self._buffer_size = 0.0 + + self.connect('button-press-event', self._button_press_event_cb) + + def do_set_property(self, pspec, value): + if pspec.name == 'icon-name': + self._icon_name = value + self.emit_paint_needed(0, 0, -1, -1) + elif pspec.name == 'color': + self._color = value + self.emit_paint_needed(0, 0, -1, -1) + elif pspec.name == 'size': + self._size = value + self.emit_request_changed() + + def do_get_property(self, pspec): + if pspec.name == 'size': + return self._size + elif pspec.name == 'icon-name': + return self._icon_name + elif pspec.name == 'color': + return self._color + + def _get_buffer(self, cr, handle, size): + if self._buffer and self._buffer_size != size: + del self._buffer + self._buffer = None + + if self._buffer == None: + target = cr.get_target() + surface = target.create_similar(cairo.CONTENT_COLOR_ALPHA, + int(size) + 1, int(size) + 1) + + dimensions = handle.get_dimension_data() + scale = float(size) / float(dimensions[0]) + + ctx = cairo.Context(surface) + ctx.scale(scale, scale) + handle.render_cairo(ctx) + del ctx + + self._buffer = surface + self._buffer_scale = scale + + return self._buffer + + def do_paint_below_children(self, cr, damaged_box): + icon_name = self._icon_name + if icon_name == None: + icon_name = 'stock-missing' + + handle = CanvasIcon._cache.get_handle( + icon_name, self._color, self._size) + buf = self._get_buffer(cr, handle, self._size) + + cr.set_source_surface(buf, 0.0, 0.0) + cr.paint() + + def do_get_width_request(self): + return self._size + + def do_get_height_request(self, for_width): + return self._size + + def _button_press_event_cb(self, item, event): + item.emit_activated() diff --git a/sugar/graphics/grid.py b/sugar/graphics/grid.py new file mode 100644 index 00000000..8d8d2ab2 --- /dev/null +++ b/sugar/graphics/grid.py @@ -0,0 +1,19 @@ +import gtk + +COLS = 16 +ROWS = 12 + +class Grid(object): + def __init__(self): + self._factor = gtk.gdk.screen_width() / COLS + + def point(self, grid_x, grid_y): + return [grid_x * self._factor, grid_y * self._factor] + + def rectangle(self, grid_x, grid_y, grid_w, grid_h): + return [grid_x * self._factor, grid_y * self._factor, + grid_w * self._factor, grid_h * self._factor] + + def fit_point(self, x, y): + return [int(x / self._factor), int(y / self._factor)] + diff --git a/sugar/graphics/menu.py b/sugar/graphics/menu.py new file mode 100644 index 00000000..9a85bcf4 --- /dev/null +++ b/sugar/graphics/menu.py @@ -0,0 +1,50 @@ +import gtk +import hippo +import gobject + +from sugar.graphics.canvasicon import CanvasIcon + +class Menu(gtk.Window): + __gsignals__ = { + 'action': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([int])), + } + + def __init__(self, title, content_box=None): + gtk.Window.__init__(self, gtk.WINDOW_POPUP) + + canvas = hippo.Canvas() + self.add(canvas) + canvas.show() + + self._root = hippo.CanvasBox(background_color=0x000000FF, + spacing=6) + canvas.set_root(self._root) + + text = hippo.CanvasText(text=title, color=0xFFFFFFFF) + self._root.append(text) + + if content_box: + separator = self._create_separator() + self._root.append(separator) + self._root.append(content_box) + + separator = self._create_separator() + self._root.append(separator) + + self._action_box = hippo.CanvasBox( + orientation=hippo.ORIENTATION_HORIZONTAL) + self._root.append(self._action_box) + + def _create_separator(self): + separator = hippo.CanvasBox(background_color=0xFFFFFFFF, + border_left=6, border_right=6, + box_height=2) + return separator + + def add_action(self, icon, action_id): + icon.connect('activated', self._action_clicked_cb, action_id) + self._action_box.append(icon) + + def _action_clicked_cb(self, icon, action): + self.emit('action', action) diff --git a/sugar/graphics/menuicon.py b/sugar/graphics/menuicon.py new file mode 100644 index 00000000..3f6d4771 --- /dev/null +++ b/sugar/graphics/menuicon.py @@ -0,0 +1,93 @@ +import hippo +import gobject + +from sugar.graphics.canvasicon import CanvasIcon + +class _MenuStrategy: + def get_menu_position(self, menu, x1, y1, x2, y2): + return [x1, y1] + +class MenuIcon(CanvasIcon): + def __init__(self, menu_shell, **kwargs): + CanvasIcon.__init__(self, **kwargs) + + self._menu_shell = menu_shell + self._menu = None + self._hover_menu = False + self._popdown_on_leave = False + self._popdown_sid = 0 + self._menu_strategy = _MenuStrategy() + + self.connect('motion-notify-event', self._motion_notify_event_cb) + + def popdown(self): + if self._menu: + self._menu.destroy() + self._menu = None + self._menu_shell.set_active(None) + + def set_menu_strategy(self, strategy): + self._menu_strategy = strategy + + def _popup(self, x1, y1, x2, y2): + self.popdown() + + self._menu_shell.set_active(None) + + self._menu = self.create_menu() + self._menu.connect('enter-notify-event', + self._menu_enter_notify_event_cb) + self._menu.connect('leave-notify-event', + self._menu_leave_notify_event_cb) + + strategy = self._menu_strategy + [x, y] = strategy.get_menu_position(self._menu, x1, y1, x2, y2) + + self._menu.move(x, y) + self._menu.show() + + self._menu_shell.set_active(self) + + def _menu_enter_notify_event_cb(self, widget, event): + self._hover_menu = True + + def _menu_leave_notify_event_cb(self, widget, event): + self._hover_menu = False + if self._popdown_on_leave: + self.popdown() + + def _start_popdown_timeout(self): + self._stop_popdown_timeout() + self._popdown_sid = gobject.timeout_add(1000, self._popdown_timeout_cb) + + def _stop_popdown_timeout(self): + if self._popdown_sid > 0: + gobject.source_remove(self._popdown_sid) + self._popdown_sid = 0 + + def _motion_notify_event_cb(self, item, event): + if event.detail == hippo.MOTION_DETAIL_ENTER: + self._motion_notify_enter() + elif event.detail == hippo.MOTION_DETAIL_LEAVE: + self._motion_notify_leave() + + def _motion_notify_enter(self): + self._stop_popdown_timeout() + + [x, y] = self.get_context().translate_to_widget(self) + [width, height] = self.get_allocation() + + self._popup(x, y, width, height) + + def _motion_notify_leave(self): + self._start_popdown_timeout() + + def _popdown_timeout_cb(self): + self._popdown_sid = 0 + + if not self._hover_menu: + self.popdown() + else: + self._popdown_on_leave = True + + return False diff --git a/sugar/graphics/style.py b/sugar/graphics/style.py new file mode 100644 index 00000000..4b1bc536 --- /dev/null +++ b/sugar/graphics/style.py @@ -0,0 +1,10 @@ +_styles = {} + +def register_stylesheet(name, style): + _styles[name] = style + +def apply_stylesheet(item, stylesheet_name): + if _styles.has_key(stylesheet_name): + style_sheet = _styles[stylesheet_name] + for name in style_sheet.keys(): + item.set_property(name, style_sheet[name]) diff --git a/tests/test-icons.py b/tests/test-icons.py index ff578e0c..c4247060 100755 --- a/tests/test-icons.py +++ b/tests/test-icons.py @@ -11,42 +11,35 @@ import sys import random import gtk -import goocanvas +import hippo from sugar.canvas import IconColor -from sugar.canvas.IconItem import IconItem +from sugar.canvas.CanvasIcon import CanvasIcon from sugar.canvas.CanvasView import CanvasView window = gtk.Window() window.connect("destroy", lambda w: gtk.main_quit()) window.show() -canvas = CanvasView() +canvas = hippo.Canvas() canvas.show() window.add(canvas) -canvas_model = goocanvas.CanvasModelSimple() -root = canvas_model.get_root_item() - -item = goocanvas.Rect(x=0, y=0, width=1200, height=900, - line_width=0.0, fill_color="#4f4f4f") -root.add_child(item) +box = hippo.CanvasBox(background_color=0x4f4f4fff) +canvas.set_root(box) icon_names = [ 'stock-buddy', 'activity-groupchat', 'activity-web'] k = 0 -while k < 12: +while k < 1: i = 0 - while i < 16: + while i < 10: color = IconColor.IconColor() icon_name_n = int(random.random() * len(icon_names)) - icon = IconItem(x=i * 75, y=k * 75, - size=75, color=color, - icon_name=icon_names[icon_name_n]) - root.add_child(icon) + icon = CanvasIcon(icon_name=icon_names[icon_name_n], + size=75, color=color) + box.append(icon, 0) i += 1 k += 1 -canvas.set_model(canvas_model) - gtk.main() diff --git a/tests/test-theme.py b/tests/test-theme.py index e73f861c..37fe8dc9 100755 --- a/tests/test-theme.py +++ b/tests/test-theme.py @@ -14,8 +14,101 @@ from sugar.canvas import IconColor from sugar.canvas.IconItem import IconItem from sugar.canvas.CanvasView import CanvasView +# Main window window = gtk.Window() window.connect("destroy", lambda w: gtk.main_quit()) -window.show() +#window.set_border_width(10) + +# Main VBox + +main_vbox = gtk.VBox(homogeneous=False, spacing=0) +window.add(main_vbox) + +############################### ############################## +############################### Menus ############################## +############################### ############################## + +menu = gtk.Menu() +file_menu = gtk.Menu() # Don't need to show menus +edit_menu = gtk.Menu() + +# Create the menu items +dummy_item_1 = gtk.MenuItem("Dummy Item 1") +dummy_item_2 = gtk.MenuItem("Dummy Item 2") +quit_item = gtk.MenuItem("Quit") +dummy_item_3 = gtk.MenuItem("Dummy Item 3") +dummy_item_4 = gtk.MenuItem("Dummy Item 4") +dummy_item_5 = gtk.MenuItem("Dummy Item 5") + +# Add them to the menu +file_menu.append(dummy_item_1) +file_menu.append(dummy_item_2) +file_menu.append(quit_item) + +edit_menu.append(dummy_item_3) +edit_menu.append(dummy_item_4) +edit_menu.append(dummy_item_5) + +# We can attach the Quit menu item to our exit function +quit_item.connect_object ("activate", lambda w: gtk.main_quit (), "file.quit") + +# We do need to show menu items +dummy_item_1.show() +dummy_item_2.show() +quit_item.show() +dummy_item_3.show() +dummy_item_4.show() +dummy_item_5.show() + +# Pack the menu into the menubar +menu_bar = gtk.MenuBar() +main_vbox.pack_start(menu_bar, False, False, 0) +menu_bar.show() + +file_item = gtk.MenuItem("File") +file_item.show() +menu_bar.append(file_item) +file_item.set_submenu(file_menu) + +edit_item = gtk.MenuItem("Edit") +edit_item.show() +menu_bar.append(edit_item) +edit_item.set_submenu(edit_menu) + + +# Scrolled window +scrolled_window = gtk.ScrolledWindow(hadjustment=None, vadjustment=None) +#scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS) +scrolled_window.set_border_width(10) +main_vbox.pack_start(scrolled_window, True, True, 0) + +# Vbox inside the scrolled window +vbox = gtk.VBox(homogeneous=False, spacing=10) +scrolled_window.add_with_viewport(vbox) +vbox.set_border_width (10) + +# Label +label = gtk.Label("This is a label") +vbox.pack_start(label, False, False, 0) + +# Entry +entry = gtk.Entry () +entry.set_text("Type some text here") +vbox.pack_start(entry, False, False, 0) + +# Buttons +buttons_hbox = gtk.HBox(homogeneous=False, spacing=5) +vbox.pack_start(buttons_hbox, False, False, 0) + +button_1 = gtk.Button ("Button 1") +buttons_hbox.pack_start(button_1, False, False, 0) + +button_2 = gtk.Button ("Button 2") +buttons_hbox.pack_start(button_2, False, False, 0) + +button_3 = gtk.Button ("Button 3") +buttons_hbox.pack_start(button_3, False, False, 0) + +window.show_all() gtk.main() diff --git a/tests/test-window-manager.py b/tests/test-window-manager.py new file mode 100755 index 00000000..413a9dd5 --- /dev/null +++ b/tests/test-window-manager.py @@ -0,0 +1,28 @@ +#!/usr/bin/python +import pygtk +pygtk.require('2.0') + +from sugar.session.UITestSession import UITestSession + +session = UITestSession() +session.start() + +import gtk + +def _show_dialog(window): + dialog = gtk.Dialog(title='No Unviewed Media', + parent=window, flags=gtk.DIALOG_MODAL, + buttons=(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) + label = gtk.Label('There is no unviewed media to download.') + dialog.vbox.pack_start(label, True, True, 0) + label.show() + response = dialog.run() + dialog.hide() + del dialog + +window = gtk.Window() +window.show() + +_show_dialog(window) + +gtk.main()