Mercurial > libervia-web
comparison browser/sat_browser/base_menu.py @ 1124:28e3eb3bb217
files reorganisation and installation rework:
- files have been reorganised to follow other SàT projects and usual Python organisation (no more "/src" directory)
- VERSION file is now used, as for other SàT projects
- replace the overcomplicated setup.py be a more sane one. Pyjamas part is not compiled anymore by setup.py, it must be done separatly
- removed check for data_dir if it's empty
- installation tested working in virtual env
- libervia launching script is now in bin/libervia
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 25 Aug 2018 17:59:48 +0200 |
parents | src/browser/sat_browser/base_menu.py@f2170536ba23 |
children | 2af117bfe6cc |
comparison
equal
deleted
inserted
replaced
1123:63a4b8fe9782 | 1124:28e3eb3bb217 |
---|---|
1 #!/usr/bin/python | |
2 # -*- coding: utf-8 -*- | |
3 | |
4 # Libervia: a Salut à Toi frontend | |
5 # Copyright (C) 2011-2018 Jérôme Poisson <goffi@goffi.org> | |
6 | |
7 # This program is free software: you can redistribute it and/or modify | |
8 # it under the terms of the GNU Affero General Public License as published by | |
9 # the Free Software Foundation, either version 3 of the License, or | |
10 # (at your option) any later version. | |
11 | |
12 # This program is distributed in the hope that it will be useful, | |
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 # GNU Affero General Public License for more details. | |
16 | |
17 # You should have received a copy of the GNU Affero General Public License | |
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | |
20 | |
21 """Base classes for building a menu. | |
22 | |
23 These classes have been moved here from menu.py because they are also used | |
24 by base_widget.py, and the import sequence caused a JS runtime error.""" | |
25 | |
26 | |
27 from sat.core.log import getLogger | |
28 log = getLogger(__name__) | |
29 | |
30 from pyjamas.ui.MenuBar import MenuBar | |
31 from pyjamas.ui.MenuItem import MenuItem | |
32 from pyjamas import Window | |
33 from sat_frontends.quick_frontend import quick_menus | |
34 from sat_browser import html_tools | |
35 | |
36 | |
37 unicode = str # FIXME: pyjamas workaround | |
38 | |
39 | |
40 class MenuCmd(object): | |
41 """Return an object with an "execute" method that can be set to a menu item callback""" | |
42 | |
43 def __init__(self, menu_item, caller=None): | |
44 """ | |
45 @param menu_item(quick_menu.MenuItem): instance of a callbable MenuItem | |
46 @param caller: menu caller | |
47 """ | |
48 self.item = menu_item | |
49 self._caller = caller | |
50 | |
51 def execute(self): | |
52 self.item.call(self._caller) | |
53 | |
54 | |
55 class SimpleCmd(object): | |
56 """Return an object with an "executre" method that launch a callback""" | |
57 | |
58 def __init__(self, callback): | |
59 """ | |
60 @param callback: method to call when menu is selected | |
61 """ | |
62 self.callback = callback | |
63 | |
64 def execute(self): | |
65 self.callback() | |
66 | |
67 | |
68 class GenericMenuBar(MenuBar): | |
69 """A menu bar with sub-categories and items""" | |
70 | |
71 def __init__(self, host, vertical=False, styles=None, flat_level=0, **kwargs): | |
72 """ | |
73 @param host (SatWebFrontend): host instance | |
74 @param vertical (bool): True to display the popup menu vertically | |
75 @param styles (dict): specific styles to be applied: | |
76 - key: a value in ('moved_popup', 'menu_bar') | |
77 - value: a CSS class name | |
78 @param flat_level (int): sub-menus until that level see their items | |
79 displayed in the parent menu bar instead of in a callback popup. | |
80 """ | |
81 MenuBar.__init__(self, vertical, **kwargs) | |
82 self.host = host | |
83 self.styles = {} | |
84 if styles: | |
85 self.styles.update(styles) | |
86 try: | |
87 self.setStyleName(self.styles['menu_bar']) | |
88 except KeyError: | |
89 pass | |
90 self.menus_container = None | |
91 self.flat_level = flat_level | |
92 | |
93 def update(self, type_, caller=None): | |
94 """Method to call when menus have changed | |
95 | |
96 @param type_: menu type like in sat.core.sat_main.importMenu | |
97 @param caller: instance linked to the menus | |
98 """ | |
99 self.menus_container = self.host.menus.getMainContainer(type_) | |
100 self._caller=caller | |
101 self.createMenus() | |
102 | |
103 @classmethod | |
104 def getCategoryHTML(cls, category): | |
105 """Build the html to be used for displaying a category item. | |
106 | |
107 Inheriting classes may overwrite this method. | |
108 @param category (quick_menus.MenuCategory): category to add | |
109 @return unicode: HTML to display | |
110 """ | |
111 return html_tools.html_sanitize(category.name) | |
112 | |
113 def _buildMenus(self, container, flat_level, caller=None): | |
114 """Recursively build menus of the container | |
115 | |
116 @param container: a quick_menus.MenuContainer instance | |
117 @param caller: instance linked to the menus | |
118 """ | |
119 for child in container.getActiveMenus(): | |
120 if isinstance(child, quick_menus.MenuContainer): | |
121 item = self.addCategory(child, flat=bool(flat_level)) | |
122 submenu = item.getSubMenu() | |
123 if submenu is None: | |
124 submenu = self | |
125 submenu._buildMenus(child, flat_level-1 if flat_level else 0, caller) | |
126 elif isinstance(child, quick_menus.MenuSeparator): | |
127 item = MenuItem(text='', asHTML=None, StyleName="menuSeparator") | |
128 self.addItem(item) | |
129 elif isinstance(child, quick_menus.MenuItem): | |
130 self.addItem(child.name, False, MenuCmd(child, caller) if child.CALLABLE else None) | |
131 else: | |
132 log.error(u"Unknown child type: {}".format(child)) | |
133 | |
134 def createMenus(self): | |
135 self.clearItems() | |
136 if self.menus_container is None: | |
137 log.debug("Menu is empty") | |
138 return | |
139 self._buildMenus(self.menus_container, self.flat_level, self._caller) | |
140 | |
141 def doItemAction(self, item, fireCommand): | |
142 """Overwrites the default behavior for the popup menu to fit in the screen""" | |
143 MenuBar.doItemAction(self, item, fireCommand) | |
144 if not self.popup: | |
145 return | |
146 if self.vertical: | |
147 # move the popup if it would go over the screen's viewport | |
148 max_left = Window.getClientWidth() - self.getOffsetWidth() + 1 - self.popup.getOffsetWidth() | |
149 new_left = self.getAbsoluteLeft() - self.popup.getOffsetWidth() + 1 | |
150 top = item.getAbsoluteTop() | |
151 else: | |
152 # move the popup if it would go over the menu bar right extremity | |
153 max_left = self.getAbsoluteLeft() + self.getOffsetWidth() - self.popup.getOffsetWidth() | |
154 new_left = max_left | |
155 top = self.getAbsoluteTop() + self.getOffsetHeight() - 1 | |
156 if item.getAbsoluteLeft() > max_left: | |
157 self.popup.setPopupPosition(new_left, top) | |
158 # eventually smooth the popup edges to fit the menu own style | |
159 try: | |
160 self.popup.addStyleName(self.styles['moved_popup']) | |
161 except KeyError: | |
162 pass | |
163 | |
164 def addCategory(self, category, menu_bar=None, flat=False): | |
165 """Add a new category. | |
166 | |
167 @param category (quick_menus.MenuCategory): category to add | |
168 @param menu_bar (GenericMenuBar): instance to popup as the category sub-menu. | |
169 """ | |
170 html = self.getCategoryHTML(category) | |
171 | |
172 if menu_bar is not None: | |
173 assert not flat # can't have a menu_bar and be flat at the same time | |
174 sub_menu = menu_bar | |
175 elif not flat: | |
176 sub_menu = GenericMenuBar(self.host, vertical=True) | |
177 else: | |
178 sub_menu = None | |
179 | |
180 item = self.addItem(html, True, sub_menu) | |
181 if flat: | |
182 item.setStyleName("menuFlattenedCategory") | |
183 return item |