Mercurial > libervia-web
diff src/browser/sat_browser/base_widget.py @ 467:97c72fe4a5f2
browser_side: import fixes:
- moved browser modules in a sat_browser packages, to avoid import conflicts with std lib (e.g. logging), and let pyjsbuild work normaly
- refactored bad import practices: classes are most of time not imported directly, module is imported instead.
author | Goffi <goffi@goffi.org> |
---|---|
date | Mon, 09 Jun 2014 22:15:26 +0200 |
parents | src/browser/base_widget.py@1eeed8028199 |
children | 5d8632a7bfde |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/browser/sat_browser/base_widget.py Mon Jun 09 22:15:26 2014 +0200 @@ -0,0 +1,732 @@ +#!/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/>. + +import pyjd # this is dummy in pyjs +from sat.core.log import getLogger +log = getLogger(__name__) +from pyjamas.ui.SimplePanel import SimplePanel +from pyjamas.ui.AbsolutePanel import AbsolutePanel +from pyjamas.ui.VerticalPanel import VerticalPanel +from pyjamas.ui.HorizontalPanel import HorizontalPanel +from pyjamas.ui.ScrollPanel import ScrollPanel +from pyjamas.ui.FlexTable import FlexTable +from pyjamas.ui.TabPanel import TabPanel +from pyjamas.ui.HTMLPanel import HTMLPanel +from pyjamas.ui.Label import Label +from pyjamas.ui.Button import Button +from pyjamas.ui.Image import Image +from pyjamas.ui.Widget import Widget +from pyjamas.ui.DragWidget import DragWidget +from pyjamas.ui.DropWidget import DropWidget +from pyjamas.ui.ClickListener import ClickHandler +from pyjamas.ui import HasAlignment +from pyjamas import DOM +from pyjamas import Window +from __pyjamas__ import doc + +import dialog + + +class DragLabel(DragWidget): + + def __init__(self, text, _type): + DragWidget.__init__(self) + self._text = text + self._type = _type + + def onDragStart(self, event): + dt = event.dataTransfer + dt.setData('text/plain', "%s\n%s" % (self._text, self._type)) + dt.setDragImage(self.getElement(), 15, 15) + + +class LiberviaDragWidget(DragLabel): + """ A DragLabel which keep the widget being dragged as class value """ + current = None # widget currently dragged + + def __init__(self, text, _type, widget): + DragLabel.__init__(self, text, _type) + self.widget = widget + + def onDragStart(self, event): + LiberviaDragWidget.current = self.widget + DragLabel.onDragStart(self, event) + + def onDragEnd(self, event): + LiberviaDragWidget.current = None + + +class DropCell(DropWidget): + """Cell in the middle grid which replace itself with the dropped widget on DnD""" + drop_keys = {} + + def __init__(self, host): + DropWidget.__init__(self) + self.host = host + self.setStyleName('dropCell') + + @classmethod + def addDropKey(cls, key, callback): + DropCell.drop_keys[key] = callback + + def onDragEnter(self, event): + if self == LiberviaDragWidget.current: + return + self.addStyleName('dragover') + DOM.eventPreventDefault(event) + + def onDragLeave(self, event): + if event.clientX <= self.getAbsoluteLeft() or event.clientY <= self.getAbsoluteTop() or\ + event.clientX >= self.getAbsoluteLeft() + self.getOffsetWidth() - 1 or event.clientY >= self.getAbsoluteTop() + self.getOffsetHeight() - 1: + # We check that we are inside widget's box, and we don't remove the style in this case because + # if the mouse is over a widget inside the DropWidget, if will leave the DropWidget, and we + # don't want that + self.removeStyleName('dragover') + + def onDragOver(self, event): + DOM.eventPreventDefault(event) + + def _getCellAndRow(self, grid, event): + """Return cell and row index where the event is occuring""" + cell = grid.getEventTargetCell(event) + row = DOM.getParent(cell) + return (row.rowIndex, cell.cellIndex) + + def onDrop(self, event): + self.removeStyleName('dragover') + DOM.eventPreventDefault(event) + dt = event.dataTransfer + # 'text', 'text/plain', and 'Text' are equivalent. + try: + item, item_type = dt.getData("text/plain").split('\n') # Workaround for webkit, only text/plain seems to be managed + if item_type and item_type[-1] == '\0': # Workaround for what looks like a pyjamas bug: the \0 should not be there, and + item_type = item_type[:-1] # .strip('\0') and .replace('\0','') don't work. TODO: check this and fill a bug report + # item_type = dt.getData("type") + log.debug("message: %s" % item) + log.debug("type: %s" % item_type) + except: + log.debug("no message found") + item = ' ' + item_type = None + if item_type == "WIDGET": + if not LiberviaDragWidget.current: + log.error("No widget registered in LiberviaDragWidget !") + return + _new_panel = LiberviaDragWidget.current + if self == _new_panel: # We can't drop on ourself + return + # we need to remove the widget from the panel as it will be inserted elsewhere + widgets_panel = _new_panel.getWidgetsPanel() + wid_row = widgets_panel.getWidgetCoords(_new_panel)[0] + row_wids = widgets_panel.getLiberviaRowWidgets(wid_row) + if len(row_wids) == 1 and wid_row == widgets_panel.getWidgetCoords(self)[0]: + # the dropped widget is the only one in the same row + # as the target widget (self), we don't do anything + return + widgets_panel.removeWidget(_new_panel) + elif item_type in self.drop_keys: + _new_panel = self.drop_keys[item_type](self.host, item) + else: + log.warning("unmanaged item type") + return + if isinstance(self, LiberviaWidget): + self.host.unregisterWidget(self) + self.onQuit() + if not isinstance(_new_panel, LiberviaWidget): + log.warning("droping an object which is not a class of LiberviaWidget") + _flextable = self.getParent() + _widgetspanel = _flextable.getParent().getParent() + row_idx, cell_idx = self._getCellAndRow(_flextable, event) + if self.host.getSelected == self: + self.host.setSelected(None) + _widgetspanel.changeWidget(row_idx, cell_idx, _new_panel) + """_unempty_panels = filter(lambda wid:not isinstance(wid,EmptyWidget),list(_flextable)) + _width = 90/float(len(_unempty_panels) or 1) + #now we resize all the cell of the column + for panel in _unempty_panels: + td_elt = panel.getElement().parentNode + DOM.setStyleAttribute(td_elt, "width", "%s%%" % _width)""" + #FIXME: delete object ? Check the right way with pyjamas + + +class WidgetHeader(AbsolutePanel, LiberviaDragWidget): + + def __init__(self, parent, title): + AbsolutePanel.__init__(self) + self.add(title) + button_group_wrapper = SimplePanel() + button_group_wrapper.setStyleName('widgetHeader_buttonsWrapper') + button_group = HorizontalPanel() + button_group.setStyleName('widgetHeader_buttonGroup') + setting_button = Image("media/icons/misc/settings.png") + setting_button.setStyleName('widgetHeader_settingButton') + setting_button.addClickListener(parent.onSetting) + close_button = Image("media/icons/misc/close.png") + close_button.setStyleName('widgetHeader_closeButton') + close_button.addClickListener(parent.onClose) + button_group.add(setting_button) + button_group.add(close_button) + button_group_wrapper.setWidget(button_group) + self.add(button_group_wrapper) + self.addStyleName('widgetHeader') + LiberviaDragWidget.__init__(self, "", "WIDGET", parent) + + +class LiberviaWidget(DropCell, VerticalPanel, ClickHandler): + """Libervia's widget which can replace itself with a dropped widget on DnD""" + + def __init__(self, host, title='', selectable=False): + """Init the widget + @param host: SatWebFrontend object + @param title: title show in the header of the widget + @param selectable: True is widget can be selected by user""" + VerticalPanel.__init__(self) + DropCell.__init__(self, host) + ClickHandler.__init__(self) + self.__selectable = selectable + self.__title_id = HTMLPanel.createUniqueId() + self.__setting_button_id = HTMLPanel.createUniqueId() + self.__close_button_id = HTMLPanel.createUniqueId() + self.__title = Label(title) + self.__title.setStyleName('widgetHeader_title') + self._close_listeners = [] + header = WidgetHeader(self, self.__title) + self.add(header) + self.setSize('100%', '100%') + self.addStyleName('widget') + if self.__selectable: + self.addClickListener(self) + + def onClose(sender): + """Check dynamically if the unibox is enable or not""" + if self.host.uni_box: + self.host.uni_box.onWidgetClosed(sender) + + self.addCloseListener(onClose) + self.host.registerWidget(self) + + def getDebugName(self): + return "%s (%s)" % (self, self.__title.getText()) + + def getWidgetsPanel(self, expect=True): + return self.getParent(WidgetsPanel, expect) + + def getParent(self, class_=None, expect=True): + """Return the closest ancestor of the specified class. + + Note: this method overrides pyjamas.ui.Widget.getParent + + @param class_: class of the ancestor to look for or None to return the first parent + @param expect: set to True if the parent is expected (print a message if not found) + @return: the parent/ancestor or None if it has not been found + """ + current = Widget.getParent(self) + if class_ is None: + return current # this is the default behavior + while current is not None and not isinstance(current, class_): + current = Widget.getParent(current) + if current is None and expect: + log.debug("Can't find parent %s for %s" % (class_, self)) + return current + + def onClick(self, sender): + self.host.setSelected(self) + + def onClose(self, sender): + """ Called when the close button is pushed """ + _widgetspanel = self.getWidgetsPanel() + _widgetspanel.removeWidget(self) + for callback in self._close_listeners: + callback(self) + self.onQuit() + + def onQuit(self): + """ Called when the widget is actually ending """ + pass + + def addCloseListener(self, callback): + """Add a close listener to this widget + @param callback: function to be called from self.onClose""" + self._close_listeners.append(callback) + + def refresh(self): + """This can be overwritten by a child class to refresh the display when, + instead of creating a new one, an existing widget is found and reused. + """ + pass + + def onSetting(self, sender): + widpanel = self.getWidgetsPanel() + row, col = widpanel.getIndex(self) + body = VerticalPanel() + + # colspan & rowspan + colspan = widpanel.getColSpan(row, col) + rowspan = widpanel.getRowSpan(row, col) + + def onColSpanChange(value): + widpanel.setColSpan(row, col, value) + + def onRowSpanChange(value): + widpanel.setRowSpan(row, col, value) + colspan_setter = dialog.IntSetter("Columns span", colspan) + colspan_setter.addValueChangeListener(onColSpanChange) + colspan_setter.setWidth('100%') + rowspan_setter = dialog.IntSetter("Rows span", rowspan) + rowspan_setter.addValueChangeListener(onRowSpanChange) + rowspan_setter.setWidth('100%') + body.add(colspan_setter) + body.add(rowspan_setter) + + # size + width_str = self.getWidth() + if width_str.endswith('px'): + width = int(width_str[:-2]) + else: + width = 0 + height_str = self.getHeight() + if height_str.endswith('px'): + height = int(height_str[:-2]) + else: + height = 0 + + def onWidthChange(value): + if not value: + self.setWidth('100%') + else: + self.setWidth('%dpx' % value) + + def onHeightChange(value): + if not value: + self.setHeight('100%') + else: + self.setHeight('%dpx' % value) + width_setter = dialog.IntSetter("width (0=auto)", width) + width_setter.addValueChangeListener(onWidthChange) + width_setter.setWidth('100%') + height_setter = dialog.IntSetter("height (0=auto)", height) + height_setter.addValueChangeListener(onHeightChange) + height_setter.setHeight('100%') + body.add(width_setter) + body.add(height_setter) + + # reset + def onReset(sender): + colspan_setter.setValue(1) + rowspan_setter.setValue(1) + width_setter.setValue(0) + height_setter.setValue(0) + + reset_bt = Button("Reset", onReset) + body.add(reset_bt) + body.setCellHorizontalAlignment(reset_bt, HasAlignment.ALIGN_CENTER) + + _dialog = dialog.GenericDialog("Widget setting", body) + _dialog.show() + + def setTitle(self, text): + """change the title in the header of the widget + @param text: text of the new title""" + self.__title.setText(text) + + def isSelectable(self): + return self.__selectable + + def setSelectable(self, selectable): + if not self.__selectable: + try: + self.removeClickListener(self) + except ValueError: + pass + if self.selectable and not self in self._clickListeners: + self.addClickListener(self) + self.__selectable = selectable + + def getWarningData(self): + """ Return exposition warning level when this widget is selected and something is sent to it + This method should be overriden by children + @return: tuple (warning level type/HTML msg). Type can be one of: + - PUBLIC + - GROUP + - ONE2ONE + - MISC + - NONE + """ + if not self.__selectable: + log.error("getWarningLevel must not be called for an unselectable widget") + raise Exception + # TODO: cleaner warning types (more general constants) + return ("NONE", None) + + def setWidget(self, widget, scrollable=True): + """Set the widget that will be in the body of the LiberviaWidget + @param widget: widget to put in the body + @param scrollable: if true, the widget will be in a ScrollPanelWrapper""" + if scrollable: + _scrollpanelwrapper = ScrollPanelWrapper() + _scrollpanelwrapper.setStyleName('widgetBody') + _scrollpanelwrapper.setWidget(widget) + body_wid = _scrollpanelwrapper + else: + body_wid = widget + self.add(body_wid) + self.setCellHeight(body_wid, '100%') + + def doDetachChildren(self): + # We need to force the use of a panel subclass method here, + # for the same reason as doAttachChildren + VerticalPanel.doDetachChildren(self) + + def doAttachChildren(self): + # We need to force the use of a panel subclass method here, else + # the event will not propagate to children + VerticalPanel.doAttachChildren(self) + + def matchEntity(self, entity): + """This method should be overwritten by child classes.""" + raise NotImplementedError + + +class ScrollPanelWrapper(SimplePanel): + """Scroll Panel like component, wich use the full available space + to work around percent size issue, it use some of the ideas found + here: http://code.google.com/p/google-web-toolkit/issues/detail?id=316 + specially in code given at comment #46, thanks to Stefan Bachert""" + + def __init__(self, *args, **kwargs): + SimplePanel.__init__(self) + self.spanel = ScrollPanel(*args, **kwargs) + SimplePanel.setWidget(self, self.spanel) + DOM.setStyleAttribute(self.getElement(), "position", "relative") + DOM.setStyleAttribute(self.getElement(), "top", "0px") + DOM.setStyleAttribute(self.getElement(), "left", "0px") + DOM.setStyleAttribute(self.getElement(), "width", "100%") + DOM.setStyleAttribute(self.getElement(), "height", "100%") + DOM.setStyleAttribute(self.spanel.getElement(), "position", "absolute") + DOM.setStyleAttribute(self.spanel.getElement(), "width", "100%") + DOM.setStyleAttribute(self.spanel.getElement(), "height", "100%") + + def setWidget(self, widget): + self.spanel.setWidget(widget) + + def setScrollPosition(self, position): + self.spanel.setScrollPosition(position) + + def scrollToBottom(self): + self.setScrollPosition(self.spanel.getElement().scrollHeight) + + +class EmptyWidget(DropCell, SimplePanel): + """Empty dropable panel""" + + def __init__(self, host): + SimplePanel.__init__(self) + DropCell.__init__(self, host) + #self.setWidget(HTML('')) + self.setSize('100%', '100%') + + +class BorderWidget(EmptyWidget): + def __init__(self, host): + EmptyWidget.__init__(self, host) + self.addStyleName('borderPanel') + + +class LeftBorderWidget(BorderWidget): + def __init__(self, host): + BorderWidget.__init__(self, host) + self.addStyleName('leftBorderWidget') + + +class RightBorderWidget(BorderWidget): + def __init__(self, host): + BorderWidget.__init__(self, host) + self.addStyleName('rightBorderWidget') + + +class BottomBorderWidget(BorderWidget): + def __init__(self, host): + BorderWidget.__init__(self, host) + self.addStyleName('bottomBorderWidget') + + +class WidgetsPanel(ScrollPanelWrapper): + + def __init__(self, host, locked=False): + ScrollPanelWrapper.__init__(self) + self.setSize('100%', '100%') + self.host = host + self.locked = locked # if True: tab will not be removed when there are no more widgets inside + self.selected = None + self.flextable = FlexTable() + self.flextable.setSize('100%', '100%') + self.setWidget(self.flextable) + self.setStyleName('widgetsPanel') + _bottom = BottomBorderWidget(self.host) + self.flextable.setWidget(0, 0, _bottom) # There will be always an Empty widget on the last row, + # dropping a widget there will add a new row + td_elt = _bottom.getElement().parentNode + DOM.setStyleAttribute(td_elt, "height", "1px") # needed so the cell adapt to the size of the border (specially in webkit) + self._max_cols = 1 # give the maximum number of columns i a raw + + def isLocked(self): + return self.locked + + def changeWidget(self, row, col, wid): + """Change the widget in the given location, add row or columns when necessary""" + log.debug("changing widget: %s %s %s" % (wid.getDebugName(), row, col)) + last_row = max(0, self.flextable.getRowCount() - 1) + try: + prev_wid = self.flextable.getWidget(row, col) + except: + log.error("Trying to change an unexisting widget !") + return + + cellFormatter = self.flextable.getFlexCellFormatter() + + if isinstance(prev_wid, BorderWidget): + # We are on a border, we must create a row and/or columns + log.debug("BORDER WIDGET") + prev_wid.removeStyleName('dragover') + + if isinstance(prev_wid, BottomBorderWidget): + # We are on the bottom border, we create a new row + self.flextable.insertRow(last_row) + self.flextable.setWidget(last_row, 0, LeftBorderWidget(self.host)) + self.flextable.setWidget(last_row, 1, wid) + self.flextable.setWidget(last_row, 2, RightBorderWidget(self.host)) + cellFormatter.setHorizontalAlignment(last_row, 2, HasAlignment.ALIGN_RIGHT) + row = last_row + + elif isinstance(prev_wid, LeftBorderWidget): + if col != 0: + log.error("LeftBorderWidget must be on the first column !") + return + self.flextable.insertCell(row, col + 1) + self.flextable.setWidget(row, 1, wid) + + elif isinstance(prev_wid, RightBorderWidget): + if col != self.flextable.getCellCount(row) - 1: + log.error("RightBorderWidget must be on the last column !") + return + self.flextable.insertCell(row, col) + self.flextable.setWidget(row, col, wid) + + else: + prev_wid.removeFromParent() + self.flextable.setWidget(row, col, wid) + + _max_cols = max(self._max_cols, self.flextable.getCellCount(row)) + if _max_cols != self._max_cols: + self._max_cols = _max_cols + self._sizesAdjust() + + def _sizesAdjust(self): + cellFormatter = self.flextable.getFlexCellFormatter() + width = 100.0 / max(1, self._max_cols - 2) # we don't count the borders + + for row_idx in xrange(self.flextable.getRowCount()): + for col_idx in xrange(self.flextable.getCellCount(row_idx)): + _widget = self.flextable.getWidget(row_idx, col_idx) + if not isinstance(_widget, BorderWidget): + td_elt = _widget.getElement().parentNode + DOM.setStyleAttribute(td_elt, "width", "%.2f%%" % width) + + last_row = max(0, self.flextable.getRowCount() - 1) + cellFormatter.setColSpan(last_row, 0, self._max_cols) + + def addWidget(self, wid): + """Add a widget to a new cell on the next to last row""" + last_row = max(0, self.flextable.getRowCount() - 1) + log.debug("putting widget %s at %d, %d" % (wid.getDebugName(), last_row, 0)) + self.changeWidget(last_row, 0, wid) + + def removeWidget(self, wid): + """Remove a widget and the cell where it is""" + _row, _col = self.flextable.getIndex(wid) + self.flextable.remove(wid) + self.flextable.removeCell(_row, _col) + if not self.getLiberviaRowWidgets(_row): # we have no more widgets, we remove the row + self.flextable.removeRow(_row) + _max_cols = 1 + for row_idx in xrange(self.flextable.getRowCount()): + _max_cols = max(_max_cols, self.flextable.getCellCount(row_idx)) + if _max_cols != self._max_cols: + self._max_cols = _max_cols + self._sizesAdjust() + current = self + + blank_page = self.getLiberviaWidgetsCount() == 0 # do we still have widgets on the page ? + + if blank_page and not self.isLocked(): + # we now notice the MainTabPanel that the WidgetsPanel is empty and need to be removed + while current is not None: + if isinstance(current, MainTabPanel): + current.onWidgetPanelRemove(self) + return + current = current.getParent() + log.error("no MainTabPanel found !") + + def getWidgetCoords(self, wid): + return self.flextable.getIndex(wid) + + def getLiberviaRowWidgets(self, row): + """ Return all the LiberviaWidget in the row """ + return [wid for wid in self.getRowWidgets(row) if isinstance(wid, LiberviaWidget)] + + def getRowWidgets(self, row): + """ Return all the widgets in the row """ + widgets = [] + cols = self.flextable.getCellCount(row) + for col in xrange(cols): + widgets.append(self.flextable.getWidget(row, col)) + return widgets + + def getLiberviaWidgetsCount(self): + """ Get count of contained widgets """ + return len([wid for wid in self.flextable if isinstance(wid, LiberviaWidget)]) + + def getIndex(self, wid): + return self.flextable.getIndex(wid) + + def getColSpan(self, row, col): + cellFormatter = self.flextable.getFlexCellFormatter() + return cellFormatter.getColSpan(row, col) + + def setColSpan(self, row, col, value): + cellFormatter = self.flextable.getFlexCellFormatter() + return cellFormatter.setColSpan(row, col, value) + + def getRowSpan(self, row, col): + cellFormatter = self.flextable.getFlexCellFormatter() + return cellFormatter.getRowSpan(row, col) + + def setRowSpan(self, row, col, value): + cellFormatter = self.flextable.getFlexCellFormatter() + return cellFormatter.setRowSpan(row, col, value) + + +class DropTab(Label, DropWidget): + + def __init__(self, tab_panel, text): + Label.__init__(self, text) + DropWidget.__init__(self, tab_panel) + self.tab_panel = tab_panel + self.setStyleName('dropCell') + self.setWordWrap(False) + DOM.setStyleAttribute(self.getElement(), "min-width", "30px") + + def _getIndex(self): + """ get current index of the DropTab """ + # XXX: awful hack, but seems the only way to get index + return self.tab_panel.tabBar.panel.getWidgetIndex(self.getParent().getParent()) - 1 + + def onDragEnter(self, event): + #if self == LiberviaDragWidget.current: + # return + self.addStyleName('dragover') + DOM.eventPreventDefault(event) + + def onDragLeave(self, event): + self.removeStyleName('dragover') + + def onDragOver(self, event): + DOM.eventPreventDefault(event) + + def onDrop(self, event): + DOM.eventPreventDefault(event) + self.removeStyleName('dragover') + if self._getIndex() == self.tab_panel.tabBar.getSelectedTab(): + # the widget come from the DragTab, so nothing to do, we let it there + return + + # FIXME: quite the same stuff as in DropCell, need some factorisation + dt = event.dataTransfer + # 'text', 'text/plain', and 'Text' are equivalent. + try: + item, item_type = dt.getData("text/plain").split('\n') # Workaround for webkit, only text/plain seems to be managed + if item_type and item_type[-1] == '\0': # Workaround for what looks like a pyjamas bug: the \0 should not be there, and + item_type = item_type[:-1] # .strip('\0') and .replace('\0','') don't work. TODO: check this and fill a bug report + # item_type = dt.getData("type") + log.debug("message: %s" % item) + log.debug("type: %s" % item_type) + except: + log.debug("no message found") + item = ' ' + item_type = None + if item_type == "WIDGET": + if not LiberviaDragWidget.current: + log.error("No widget registered in LiberviaDragWidget !") + return + _new_panel = LiberviaDragWidget.current + _new_panel.getWidgetsPanel().removeWidget(_new_panel) + elif item_type in DropCell.drop_keys: + _new_panel = DropCell.drop_keys[item_type](self.tab_panel.host, item) + else: + log.warning("unmanaged item type") + return + + widgets_panel = self.tab_panel.getWidget(self._getIndex()) + widgets_panel.addWidget(_new_panel) + + +class MainTabPanel(TabPanel): + + def __init__(self, host): + TabPanel.__init__(self) + self.host = host + self.tabBar.setVisible(False) + self.setStyleName('liberviaTabPanel') + self.addStyleName('mainTabPanel') + Window.addWindowResizeListener(self) + + def getCurrentPanel(self): + """ Get the panel of the currently selected tab """ + return self.deck.visibleWidget + + def onWindowResized(self, width, height): + tab_panel_elt = self.getElement() + _elts = doc().getElementsByClassName('gwt-TabBar') + if not _elts.length: + log.error("no TabBar found, it should exist !") + tab_bar_h = 0 + else: + tab_bar_h = _elts.item(0).offsetHeight + ideal_height = height - DOM.getAbsoluteTop(tab_panel_elt) - tab_bar_h - 5 + ideal_width = width - DOM.getAbsoluteLeft(tab_panel_elt) - 5 + self.setWidth("%s%s" % (ideal_width, "px")) + self.setHeight("%s%s" % (ideal_height, "px")) + + def add(self, widget, text=''): + tab = DropTab(self, text) + TabPanel.add(self, widget, tab, False) + if self.getWidgetCount() > 1: + self.tabBar.setVisible(True) + self.host.resize() + + def onWidgetPanelRemove(self, panel): + """ Called when a child WidgetsPanel is empty and need to be removed """ + self.remove(panel) + widgets_count = self.getWidgetCount() + if widgets_count == 1: + self.tabBar.setVisible(False) + self.host.resize() + self.selectTab(0) + else: + self.selectTab(widgets_count - 1)