diff src/browser/sat_browser/base_menu.py @ 494:5d8632a7bfde

browser_side: refactorisation of menus and LiberviaWidget's header
author souliane <souliane@mailoo.org>
date Tue, 15 Jul 2014 18:43:55 +0200
parents
children 60be99de3808
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/browser/sat_browser/base_menu.py	Tue Jul 15 18:43:55 2014 +0200
@@ -0,0 +1,166 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Libervia: a Salut à Toi frontend
+# Copyright (C) 2011, 2012, 2013, 2014 Jérôme Poisson <goffi@goffi.org>
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Base classes for building a menu.
+
+These classes have been moved here from menu.py because they are also used
+by base_widget.py, and the import sequence caused a JS runtime error."""
+
+
+import pyjd  # this is dummy in pyjs
+from sat.core.log import getLogger
+log = getLogger(__name__)
+
+from pyjamas.ui.MenuBar import MenuBar
+from pyjamas.ui.MenuItem import MenuItem
+
+
+class MenuCmd:
+    """Return an object with an "execute" method that can be set to a menu item callback"""
+
+    def __init__(self, object_, handler):
+        self._object = object_
+        self._handler = handler
+
+    def execute(self):
+        handler = getattr(self._object, self._handler)
+        handler()
+
+
+class PluginMenuCmd:
+    """Like MenuCmd, but instead of executing a method, it will command the bridge to launch an action"""
+
+    def __init__(self, host, action_id, menu_data=None):
+        self.host = host
+        self.action_id = action_id
+        self.menu_data = menu_data
+
+    def execute(self):
+        self.host.launchAction(self.action_id, self.menu_data)
+
+
+class CategoryMenuBar(MenuBar):
+    """A menu bar for a category (sub menu)"""
+    def __init__(self):
+        MenuBar.__init__(self, vertical=True)
+
+
+class CategoryItem(MenuItem):
+    """A category item with a non-internationalized name"""
+    def __init__(self, name, *args, **kwargs):
+        MenuItem.__init__(self, *args, **kwargs)
+        self.name = name
+
+
+class GenericMenuBar(MenuBar):
+
+    def __init__(self, host, vertical=False, **kwargs):
+        MenuBar.__init__(self, vertical, **kwargs)
+        self.host = host
+        self.moved_popup_style = None
+
+    @classmethod
+    def getCategoryHTML(cls, type_, menu_name_i18n):
+        """Build from the given parameters the html to be displayed for a category item.
+
+        Inheriting classes may overwrite this method.
+        @param type_ (str): category type
+        @param menu_name_i18n (str): internationalized category name
+        @return: str
+        """
+        return menu_name_i18n
+
+    def doItemAction(self, item, fireCommand):
+        """Overwrites the default behavior for the popup menu to fit in the screen"""
+        MenuBar.doItemAction(self, item, fireCommand)
+        if not self.vertical and self.popup:
+            # we not only move the last popup, but any which would go over the menu right extremity
+            most_left = self.getAbsoluteLeft() + self.getOffsetWidth() - self.popup.getOffsetWidth()
+            if item.getAbsoluteLeft() > most_left:
+                self.popup.setPopupPosition(most_left,
+                                            self.getAbsoluteTop() +
+                                            self.getOffsetHeight() - 1)
+                # eventually smooth the popup edges to fit the menu own style
+                if self.moved_popup_style:
+                    self.popup.addStyleName(self.moved_popup_style)
+
+    def getCategories(self):
+        """Return the categories items.
+
+        @return: list[CategoryItem]
+        """
+        return [item for item in self.items if isinstance(item, CategoryItem)]
+
+    def getSubMenu(self, category):
+        """Return the popup menu for the given category
+
+        @param category (str): category name
+        @return: CategoryMenuBar instance or None
+        """
+        try:
+            return [item for item in self.items if isinstance(item, CategoryItem) and item.name == category][0].getSubMenu()
+        except IndexError:
+            return None
+
+    def addSeparator(self):
+        """Add a separator between the categories"""
+        self.addItem(CategoryItem(None, text='', asHTML=None, StyleName='menuSeparator'))
+
+    def addCategory(self, menu_name, menu_name_i18n, type_, sub_menu):
+        """Add a category
+
+        @param menu_name (str): category name
+        @param menu_name_i18n (str): internationalized category name
+        @param type_ (str): category type
+        @param sub_menu (CategoryMenuBar): category sub-menu
+        """
+        html = self.getCategoryHTML(type_, menu_name_i18n)
+        self.addItem(CategoryItem(menu_name, text=html, asHTML=True, subMenu=sub_menu))
+
+    def addMenu(self, menu_name, menu_name_i18n, item_name_i18n, type_, menu_cmd):
+        """Add a new menu item
+        @param menu_name (str): category name
+        @param menu_name_i18n (str): internationalized menu name
+        @param item_name_i18n (str): internationalized item name
+        @param type_ (str): category type in ('games', 'help', 'home', 'photos', 'plugins', 'settings', 'social')
+        @param menu_cmd (MenuCmd or PluginMenuCmd): instance to execute as the item callback
+        """
+        log.info("addMenu: %s %s %s %s %s" % (menu_name, menu_name_i18n, item_name_i18n, type_, menu_cmd))
+        sub_menu = self.getSubMenu(menu_name)
+        if not sub_menu:
+            sub_menu = CategoryMenuBar()
+            self.addCategory(menu_name, menu_name_i18n, type_, sub_menu)
+        if item_name_i18n and menu_cmd:
+            sub_menu.addItem(item_name_i18n, menu_cmd)
+
+    def addCachedMenus(self, type_, menu_data=None):
+        """Add cached menus to instance
+        @param type_: menu type like is sat.core.sat_main.importMenu
+        @param menu_data: data to send with these menus
+        """
+        menus = self.host.menus.get(type_, [])
+        for action_id, path, path_i18n in menus:
+            if len(path) != 2:
+                raise NotImplementedError("Menu with a path != 2 are not implemented yet")
+            if len(path) != len(path_i18n):
+                log.error("inconsistency between menu paths")
+                continue
+            callback = PluginMenuCmd(self.host, action_id, menu_data)
+            self.addMenu(path[0], path_i18n[0], path_i18n[1], 'plugins', callback)