Example #1
0
class TabContainer(AbstractContainer):
    dockMove = Signal(object, object, object)

    class Point(object):
        def __init__(self, x, y):
            self.__x = x
            self.__y = y

        def x(self):
            return self.__x

        def y(self):
            return self.__y

    def __init__(self, bench, parent_container):
        """ Container which layouts its child items on stacked layout and provides a tab bar.

        :param bench:
        :param parent_container:
        """
        super(TabContainer, self).__init__(bench, parent_container)

        self.setAcceptDrops(True)
        self.refDropRegions = None
        self.absDropRegions = None
        self.overlay = DropOverlay(self)

        self.layout = QVBoxLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.setSpacing(0)
        if CONFIG.debug_layout:
            self.layout.setContentsMargins(2, 16, 2, 2)
        self.setLayout(self.layout)

        self._tabbar = TabHeader()
        self.layout.addWidget(self._tabbar)

        self._dockstack = QStackedLayout()
        self._dockstack.setContentsMargins(0, 0, 0, 0)
        self._dockstack.setSpacing(0)
        self.layout.addLayout(self._dockstack)

    def activateTab(self, uid):
        _log.debug("Activating tab for dock: {}".format(uid))

        for d in self.flatDockList:
            if d.uid == uid:
                self._dockstack.setCurrentWidget(d)
                d.tab.setActive(True)
            else:
                d.tab.setActive(False)

    def closeChild(self, uid):
        _log.debug("Closing dock: {}".format(uid))

        was_active = False
        for d in self.flatDockList:
            if d.uid == uid:
                _log.debug("Removing dock: {}".format(d))

                was_active = d.tab.active

                d.tab.setParent(None)
                d.tab._dock = None
                d.parentContainer = None
                d.close()
                d.deleteLater()
                d.setParent(None)

        self.contentModified.emit()
        if self._dockstack.count() == 0:
            # Container is empty, close and propagate
            self.closing.emit(self._uid)
        elif was_active:
            dock = self.flatDockList[0]
            self.activateTab(dock.uid)

    @property
    def flatDockList(self):
        return [
            self._dockstack.itemAt(k).widget()
            for k in range(self._dockstack.count())
        ]

    @property
    def docks(self):
        return self.flatDockList

    def addItem(self, index, item):

        # _config.debug check
        if not isinstance(item, Dock):
            raise BenchException("Misuse")

        item.parentContainer = self
        item.closing.connect(self.closeChild)
        item.activated.connect(self.activateTab)

        self._tabbar.addTab(index, item)
        self._dockstack.insertWidget(index, item)
        self.activateTab(item.uid)

        item.setVisible(True)
        item.tab.setVisible(True)

    @classmethod
    def __checkEventMimeTypeData(cls, event):
        """ Checks the drag events MIME type, and that at least two dockbench items on the area.

        :param event: drag event / drop event
        :return: true when the MIME type can be handled
        """
        if not event.mimeData().hasFormat(MIME_TYPE):
            event.ignore()
            return False

        event.accept()
        return True

    def dragEnterEvent(self, event):
        _log.debug("TabContainer: Drag enter event")
        if not self.__checkEventMimeTypeData(event):
            return

        data = event.mimeData().data(MIME_TYPE).data()
        dock_uid = pickle.loads(data)
        _log.debug("ETID: {}".format(dock_uid))

        for d in self.flatDockList:
            if d.uid == dock_uid and len(self.flatDockList) == 1:
                _log.debug("Tab in container")
                event.ignore()
                return

        if self.rect().width() < 200 and self.rect().height() < 200:
            _log.debug("To less widget left...")
            event.ignore()
            return

        if self.overlay.isHidden():
            _log.debug("Drop overlay")
            self.overlay.raise_()
            self.overlay.show()
            w = self._dockstack.currentWidget()
            pos = w.mapTo(self, QPoint(0, 0))
            rect = QRect(pos.x(), pos.y(), w.width(), w.height())
            self.overlay.setGeometry(rect)

            xc = pos.x() + w.width() / 2.0
            yc = pos.y() + w.height() / 2.0

            self.refDropRegions = {
                TabContainer.Point(xc - 34, yc): Placement.LEFT,
                TabContainer.Point(xc + 34, yc): Placement.RIGHT,
                TabContainer.Point(xc, yc - 34): Placement.TOP,
                TabContainer.Point(xc, yc + 34): Placement.BOTTOM,
                TabContainer.Point(xc, yc): Placement.TAB,
            }
            self.absDropRegions = {
                TabContainer.Point(xc - 68, yc): Placement.LEFT,
                TabContainer.Point(xc + 68, yc): Placement.RIGHT,
                TabContainer.Point(xc, yc - 68): Placement.TOP,
                TabContainer.Point(xc, yc + 68): Placement.BOTTOM,
            }

    def dragMoveEvent(self, event):
        if not self.__checkEventMimeTypeData(event):
            return

        pos = event.pos()
        x = pos.x()
        y = pos.y()

        for region in self.refDropRegions:
            if abs(region.x() - x) <= 12 and abs(region.y() - y) <= 12:
                _log.debug("Drop ref over: {}".format(
                    self.refDropRegions[region]))
                event.accept()
                self.overlay.setActiveDropRegion(self.refDropRegions[region])
                return

        for region in self.absDropRegions:
            if abs(region.x() - x) <= 12 and abs(region.y() - y) <= 12:
                _log.debug("Drop abs over: {}".format(
                    self.absDropRegions[region]))
                event.accept()
                self.overlay.setActiveDropRegion(self.absDropRegions[region],
                                                 True)
                return

        self.overlay.setActiveDropRegion(None)
        event.ignore()

    def dragLeaveEvent(self, event):
        _log.debug("TabContainer: Drag leave event")

        if not self.overlay.isHidden():
            self.overlay.hide()

            self.refDropRegions = None
            self.absDropRegions = None

    def dropEvent(self, event):
        _log.debug("TabContainer: Drop event")

        if not self.__checkEventMimeTypeData(event):
            event.ignore()
            return

        data = event.mimeData().data(MIME_TYPE).data()
        dock_uid = pickle.loads(data)
        _log.debug("ETID: {}".format(dock_uid))

        pos = event.pos()
        x = pos.x()
        y = pos.y()

        for region in self.refDropRegions:
            if abs(region.x() - x) <= 12 and abs(region.y() - y) <= 12:
                _log.debug("Drop ref over: {}".format(
                    self.refDropRegions[region]))

                ref = self._dockstack.currentWidget()
                # self.dockMove.emit(dock_uid, self.refDropRegions[region], ref.uid)
                self._bench.dockMove(dock_uid, self.refDropRegions[region],
                                     ref.uid)
                break

        if self.absDropRegions is not None:
            for region in self.absDropRegions:
                if abs(region.x() - x) <= 12 and abs(region.y() - y) <= 12:
                    _log.debug("Drop abs over: {}".format(
                        self.absDropRegions[region]))

                    # self.dockMove.emit(dock_uid, self.absDropRegions[region], None)
                    self._bench.dockMove(dock_uid, self.absDropRegions[region],
                                         None)
                    break

        if not self.overlay.isHidden():
            self.overlay.hide()

            self.refDropRegions = None
            self.absDropRegions = None

    def saveLayout(self):
        layout = super(TabContainer, self).saveLayout()

        children = []
        for dock in self.flatDockList:
            children.append(dock.saveLayout())
        layout["children"] = children

        return layout

    def loadLayout(self, layout):

        for k, child in enumerate(layout["children"]):
            module_str = child["module"]
            class_str = child["class"]

            # Bootstrap part II: Make the child containers
            mod = __import__(module_str, fromlist=[class_str])
            klass = getattr(mod, class_str)

            child_obj = klass()
            self.addItem(k, child_obj)

            child_obj.loadLayout(child)