Mercurial > libervia-web
diff src/browser/sat_browser/base_menu.py @ 498:60be99de3808
browser_side: menus refactorization + handle levels > 2
author | souliane <souliane@mailoo.org> |
---|---|
date | Fri, 25 Jul 2014 02:38:30 +0200 |
parents | 5d8632a7bfde |
children | 4aa627b059df |
line wrap: on
line diff
--- a/src/browser/sat_browser/base_menu.py Thu Jul 24 12:20:36 2014 +0200 +++ b/src/browser/sat_browser/base_menu.py Fri Jul 25 02:38:30 2014 +0200 @@ -30,6 +30,7 @@ from pyjamas.ui.MenuBar import MenuBar from pyjamas.ui.MenuItem import MenuItem +from pyjamas import Window class MenuCmd: @@ -56,12 +57,6 @@ 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): @@ -70,15 +65,30 @@ class GenericMenuBar(MenuBar): + """A menu bar with sub-categories and items""" - def __init__(self, host, vertical=False, **kwargs): + def __init__(self, host, vertical=False, styles=None, **kwargs): + """ + @param host (SatWebFrontend): host instance + @param vertical (bool): True to display the popup menu vertically + @param styles (dict): specific styles to be applied: + - key: a value in ('moved_popup', 'menu_bar') + - value: a CSS class + the popup that are not displayed at the position computed by pyjamas. + """ MenuBar.__init__(self, vertical, **kwargs) self.host = host - self.moved_popup_style = None + self.styles = styles or {} + if 'menu_bar' in self.styles: + # XXX: pyjamas set the style to object string representation! + # FIXME: fix the bug upstream + first = 'gwt-MenuBar' + second = first + '-' + ('vertical' if self.vertical else 'horizontal') + self.setStyleName(' '.join([first, second, self.styles['menu_bar']])) @classmethod def getCategoryHTML(cls, type_, menu_name_i18n): - """Build from the given parameters the html to be displayed for a category item. + """Build the html to be used for displaying a category item. Inheriting classes may overwrite this method. @param type_ (str): category type @@ -90,65 +100,108 @@ 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) + if not self.popup: + return + if self.vertical: + # move the popup if it would go over the screen's viewport + max_left = Window.getClientWidth() - self.getOffsetWidth() + 1 - self.popup.getOffsetWidth() + new_left = self.getAbsoluteLeft() - self.popup.getOffsetWidth() + 1 + top = item.getAbsoluteTop() + else: + # move the popup if it would go over the menu bar right extremity + max_left = self.getAbsoluteLeft() + self.getOffsetWidth() - self.popup.getOffsetWidth() + new_left = max_left + top = self.getAbsoluteTop() + self.getOffsetHeight() - 1 + if item.getAbsoluteLeft() > max_left: + self.popup.setPopupPosition(new_left, top) + # eventually smooth the popup edges to fit the menu own style + if 'moved_popup' in self.styles: + self.popup.addStyleName(self.styles['moved_popup']) def getCategories(self): - """Return the categories items. + """Return all the categories items. @return: list[CategoryItem] """ return [item for item in self.items if isinstance(item, CategoryItem)] - def getSubMenu(self, category): + def getCategoryItem(self, path): + """Return the requested category item + + @param path (list[str]): path to the category + @return: CategoryInstance or None + """ + assert(len(path) > 0) + if len(path) > 1: + menu = self.getCategoryMenu(path[:1]) + return menu.getCategoryItem(path[1:]) if menu else None + items = [item for item in self.items if isinstance(item, CategoryItem) and item.name == path[0]] + if len(items) == 1: + return items[0] + assert(items == []) # there should not be more than 1 category with the same name + return None + + def getCategoryMenu(self, path): """Return the popup menu for the given category - @param category (str): category name + @param path (list[str]): path to the category @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 + item = self.getCategoryItem(path) + return item.getSubMenu() if item else 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 + def addCategory(self, path, path_i18n, type_, sub_menu=None): + """Add a category item and its associated sub-menu. - @param menu_name (str): category name - @param menu_name_i18n (str): internationalized category name + If the category already exists, do not overwrite the current sub-menu. + @param path (list[str], str): path to the category. Passing a string for + the category name is also accepted if there's no sub-category. + @param path_i18n (list[str], str): internationalized path to the category. + Passing a string for the internationalized category name is also accepted + if there's no sub-category. @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)) + if isinstance(path, str): + path = [path] + if isinstance(path_i18n, str): + path_i18n = [path_i18n] + assert(len(path) > 0 and len(path) == len(path_i18n)) + current = self + count = len(path) + for menu_name, menu_name_i18n in zip(path, path_i18n): + tmp = current.getCategoryMenu([menu_name]) + if not tmp: + html = self.getCategoryHTML(type_, menu_name_i18n) + tmp = CategoryMenuBar(self.host) if (count > 1 or not sub_menu) else sub_menu + current.addItem(CategoryItem(menu_name, text=html, asHTML=True, subMenu=tmp)) + current = tmp + count -= 1 - def addMenu(self, menu_name, menu_name_i18n, item_name_i18n, type_, menu_cmd): + def addMenuItem(self, path, path_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 path (list[str], str): path to the category, completed by a dummy + value for the item in last position. Passing a string for the category + name is also accepted if there's no sub-category. + @param path_i18n (list[str]): internationalized path to the item @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 isinstance(path, str): + assert(len(path_i18n) == 2) + path = [path, None] + assert(len(path) > 1 and len(path) == len(path_i18n)) + log.info("addMenuItem: %s %s %s %s" % (path, path_i18n, type_, menu_cmd)) + sub_menu = self.getCategoryMenu(path[:-1]) 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) + sub_menu = CategoryMenuBar(self.host) + self.addCategory(path[:-1], path_i18n[:-1], type_, sub_menu) + if menu_cmd: + sub_menu.addItem(path_i18n[-1], menu_cmd) def addCachedMenus(self, type_, menu_data=None): """Add cached menus to instance @@ -157,10 +210,14 @@ """ 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) + self.addMenuItem(path, path_i18n, 'plugins', callback) + + +class CategoryMenuBar(GenericMenuBar): + """A menu bar for a category (sub-menu)""" + def __init__(self, host): + GenericMenuBar.__init__(self, host, vertical=True)