Esempio n. 1
0
class QDockBarItemHandle(QFrame):
    """ A frame which provides a resize border for a QDockBarItem.

    """
    #: A signal emitted when the handle is moved. The payload is a
    #: QPoint which represents the delta drag distance.
    handleMoved = Signal(QPoint)

    def __init__(self, parent=None):
        """ Initialize a QDockBarItemHandle.

        Parameters
        ----------
        parent : QWidget, optional
            The parent widget of the handle.

        """
        super(QDockBarItemHandle, self).__init__(parent)
        policy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
        self.setSizePolicy(policy)
        self._press_pos = QPoint()
        self._size_hint = QSize(5, 5)

    def sizeHint(self):
        """ Get the size hint for the widget.

        """
        return self._size_hint

    def mousePressEvent(self, event):
        """ Handle the mouse press event for the widget.

        """
        event.ignore()
        if event.button() == Qt.LeftButton:
            self._press_pos = event.pos()
            event.accept()

    def mouseReleaseEvent(self, event):
        """ Handle the mouse release event for the widget.

        """
        event.ignore()
        if event.button() == Qt.LeftButton:
            self._press_pos = QPoint()
            event.accept()

    def mouseMoveEvent(self, event):
        """ Handle the mouse move event for the widget.

        """
        event.ignore()
        if not self._press_pos.isNull():
            self.handleMoved.emit(event.pos() - self._press_pos)
            event.accept()
Esempio n. 2
0
class QDockTitleBar(QFrame, IDockTitleBar):
    """ A concrete implementation of IDockTitleBar.

    This class serves as the default title bar for a QDockItem.

    """
    #: A signal emitted when the maximize button is clicked.
    maximizeButtonClicked = Signal(bool)

    #: A signal emitted when the restore button is clicked.
    restoreButtonClicked = Signal(bool)

    #: A signal emitted when the close button is clicked.
    closeButtonClicked = Signal(bool)

    #: A signal emitted when the link button is toggled.
    linkButtonToggled = Signal(bool)

    #: A signal emitted when the pin button is toggled.
    pinButtonToggled = Signal(bool)

    #: A signal emitted when the title is edited by the user.
    titleEdited = Signal(str)

    #: A signal emitted when the empty area is left double clicked.
    leftDoubleClicked = Signal(QPoint)

    #: A signal emitted when the empty area is right clicked.
    rightClicked = Signal(QPoint)

    def __init__(self, parent=None):
        """ Initialize a QDockTitleBar.

        Parameters
        ----------
        parent : QWidget or None
            The parent of the title bar.

        """
        super(QDockTitleBar, self).__init__(parent)
        self._buttons = self.CloseButton | self.MaximizeButton | self.PinButton
        self._is_editable = False
        self._force_hidden = False
        self._last_visible = True
        self._line_edit = None

        title_icon = self._title_icon = QIconWidget(self)
        title_icon.setVisible(False)

        title_label = self._title_label = QTextLabel(self)

        spacer = self._spacer = QWidget(self)
        policy = spacer.sizePolicy()
        policy.setHorizontalPolicy(QSizePolicy.Expanding)
        spacer.setSizePolicy(policy)

        btn_size = QSize(14, 13)

        max_button = self._max_button = QBitmapButton(self)
        max_button.setObjectName('docktitlebar-maximize-button')
        max_button.setBitmap(MAXIMIZE_BUTTON.toBitmap())
        max_button.setIconSize(btn_size)
        max_button.setVisible(self._buttons & self.MaximizeButton)
        max_button.setToolTip('Maximize')

        restore_button = self._restore_button = QBitmapButton(self)
        restore_button.setObjectName('docktitlebar-restore-button')
        restore_button.setBitmap(RESTORE_BUTTON.toBitmap())
        restore_button.setIconSize(btn_size)
        restore_button.setVisible(self._buttons & self.RestoreButton)
        restore_button.setToolTip('Restore Down')

        close_button = self._close_button = QBitmapButton(self)
        close_button.setObjectName('docktitlebar-close-button')
        close_button.setBitmap(CLOSE_BUTTON.toBitmap())
        close_button.setIconSize(btn_size)
        close_button.setVisible(self._buttons & self.CloseButton)
        close_button.setToolTip('Close')

        link_button = self._link_button = QCheckedBitmapButton(self)
        link_button.setObjectName('docktitlebar-link-button')
        link_button.setBitmap(UNLINKED_BUTTON.toBitmap())
        link_button.setCheckedBitmap(LINKED_BUTTON.toBitmap())
        link_button.setIconSize(btn_size)
        link_button.setVisible(self._buttons & self.LinkButton)
        link_button.setToolTip('Link Window')
        link_button.setCheckedToolTip('Unlink Window')

        pin_button = self._pin_button = QCheckedBitmapButton(self)
        pin_button.setObjectName('docktitlebar-pin-button')
        pin_button.setBitmap(PIN_BUTTON.toBitmap())
        pin_button.setCheckedBitmap(UNPIN_BUTTON.toBitmap())
        pin_button.setIconSize(QSize(13, 13))
        pin_button.setVisible(self._buttons & self.PinButton)
        pin_button.setToolTip('Pin Window')
        pin_button.setCheckedToolTip('Unpin Window')

        layout = QHBoxLayout()
        layout.setContentsMargins(QMargins(5, 2, 5, 2))
        layout.setSpacing(1)
        layout.addWidget(title_icon)
        layout.addSpacing(0)
        layout.addWidget(title_label)
        layout.addWidget(spacer)
        layout.addSpacing(4)
        layout.addWidget(pin_button)
        layout.addWidget(link_button)
        layout.addWidget(max_button)
        layout.addWidget(restore_button)
        layout.addWidget(close_button)

        self.setLayout(layout)

        max_button.clicked.connect(self.maximizeButtonClicked)
        restore_button.clicked.connect(self.restoreButtonClicked)
        close_button.clicked.connect(self.closeButtonClicked)
        link_button.toggled.connect(self.linkButtonToggled)
        pin_button.toggled.connect(self.pinButtonToggled)

    #--------------------------------------------------------------------------
    # Event Handlers
    #--------------------------------------------------------------------------
    def mouseDoubleClickEvent(self, event):
        """ Handle the mouse double click event for the title bar.

        """
        event.ignore()
        if event.button() == Qt.LeftButton:
            pos = event.pos()
            is_editable = self._is_editable
            if self._adjustedLabelGeometry().contains(pos) and is_editable:
                self._showTitleLineEdit()
                event.accept()
                return
            if self._clickableGeometry().contains(pos):
                self.leftDoubleClicked.emit(event.globalPos())
                event.accept()
                return

    def mousePressEvent(self, event):
        """ Handle the mouse press event for the title bar.

        """
        event.ignore()
        if event.button() == Qt.RightButton:
            if self._clickableGeometry().contains(event.pos()):
                self.rightClicked.emit(event.globalPos())
                event.accept()
                return

    #--------------------------------------------------------------------------
    # Overrides
    #--------------------------------------------------------------------------
    def setVisible(self, visible):
        """ An overridden virtual visibility setter.

        This handler enforces the force-hidden setting.

        """
        self._last_visible = visible
        if visible and self._force_hidden:
            return
        super(QDockTitleBar, self).setVisible(visible)

    #--------------------------------------------------------------------------
    # Private API
    #--------------------------------------------------------------------------
    def _adjustedLabelGeometry(self):
        """ Get the adjust label geometry.

        Returns
        -------
        result : QRect
            A rectangle representing the label geometry which has been
            adjusted for potentially empty text. This rect can be used
            for a usable hit-testing rect for the label text.

        """
        label = self._title_label
        label_geo = label.geometry()
        if not label.text():
            label_geo = label_geo.adjusted(0, 0, 10, 0)
        return label_geo

    def _clickableGeometry(self):
        """ Get the geometry rect which represents clickable area.

        Returns
        -------
        result : QRect
            A rectangle adjusted for the clickable geometry.

        """
        rect = self.rect().adjusted(5, 2, -5, -2)
        rect.setRight(self._spacer.geometry().right())
        return rect

    def _showTitleLineEdit(self):
        """ Setup the line edit widget for editing the title.

        """
        old_line_edit = self._line_edit
        if old_line_edit is not None:
            old_line_edit.hide()
            old_line_edit.deleteLater()
        line_edit = self._line_edit = QLineEdit(self)
        line_edit.setFrame(False)
        line_edit.setText(self._title_label.text())
        line_edit.selectAll()
        h = self._title_label.height()
        line_edit.setMinimumHeight(h)
        line_edit.setMaximumHeight(h)
        line_edit.editingFinished.connect(self._onEditingFinished)
        layout = self.layout()
        idx = layout.indexOf(self._spacer)
        layout.insertWidget(idx, line_edit)
        self._spacer.hide()
        self._title_label.hide()
        line_edit.show()
        line_edit.setFocus(Qt.MouseFocusReason)

    def _onEditingFinished(self):
        """ Handle the 'editingFinished' signal for title line edit.

        """
        line_edit = self._line_edit
        if line_edit is not None:
            text = line_edit.text()
            line_edit.hide()
            line_edit.deleteLater()
            self._line_edit = None
            changed = self._title_label.text() != text
            if changed:
                self._title_label.setText(text)
            self._title_label.show()
            self._spacer.show()
            if changed:
                self.titleEdited.emit(text)

    #--------------------------------------------------------------------------
    # IDockItemTitleBar API
    #--------------------------------------------------------------------------
    def buttons(self):
        """ Get the buttons to show in the title bar.

        Returns
        -------
        result : int
            An or'd combination of the buttons to show.

        """
        return self._buttons

    def setButtons(self, buttons):
        """ Set the buttons to show in the title bar.

        Parameters
        ----------
        buttons : int
            An or'd combination of the buttons to show.

        """
        self._buttons = buttons
        self._max_button.setVisible(buttons & self.MaximizeButton)
        self._restore_button.setVisible(buttons & self.RestoreButton)
        self._close_button.setVisible(buttons & self.CloseButton)
        self._link_button.setVisible(buttons & self.LinkButton)
        self._pin_button.setVisible(buttons & self.PinButton)

    def title(self):
        """ Get the title string of the title bar.

        Returns
        -------
        result : unicode
            The unicode title string for the title bar.

        """
        return self._title_label.text()

    def setTitle(self, title):
        """ Set the title string of the title bar.

        Parameters
        ----------
        title : unicode
            The unicode string to use for the title bar.

        """
        self._title_label.setText(title)

    def label(self):
        """ Get the label which holds the title string.

        Returns
        -------
        result : QTextLabel
            The label widget which holds the title string.

        """
        return self._title_label

    def icon(self):
        """ Get the icon for the title bar.

        Returns
        -------
        result : QIcon
            The icon set for the title bar.

        """
        return self._title_icon.icon()

    def setIcon(self, icon):
        """ Set the icon for the title bar.

        Parameters
        ----------
        icon : QIcon
            The icon to use for the title bar.

        """
        visible, spacing = (False, 0) if icon.isNull() else (True, 4)
        title_icon = self._title_icon
        title_icon.setIcon(icon)
        title_icon.setVisible(visible)
        layout = self.layout()
        layout.takeAt(1)
        layout.insertSpacing(1, spacing)

    def iconSize(self):
        """ Get the icon size for the title bar.

        Returns
        -------
        result : QSize
            The size to use for the icons in the title bar.

        """
        return self._title_icon.iconSize()

    def setIconSize(self, size):
        """ Set the icon size for the title bar.

        Parameters
        ----------
        icon : QSize
            The icon size to use for the title bar. Icons smaller than
            this size will not be scaled up.

        """
        self._title_icon.setIconSize(size)

    def isLinked(self):
        """ Get whether the link button is checked.

        Returns
        -------
        result : bool
            True if the link button is checked, False otherwise.

        """
        return self._link_button.isChecked()

    def setLinked(self, linked):
        """ Set whether or not the link button is checked.

        Parameters
        ----------
        linked : bool
            True if the link button should be checked, False otherwise.

        """
        self._link_button.setChecked(linked)

    def isPinned(self):
        """ Get whether the pin button is checked.

        Returns
        -------
        result : bool
            True if the pin button is checked, False otherwise.

        """
        return self._pin_button.isChecked()

    def setPinned(self, pinned, quiet=False):
        """ Set whether or not the pin button is checked.

        Parameters
        ----------
        pinned : bool
            True if the pin button should be checked, False otherwise.

        quiet : bool, optional
            True if the state should be set without emitted the toggled
            signal. The default is False.

        """
        old = self._pin_button.blockSignals(quiet)
        self._pin_button.setChecked(pinned)
        self._pin_button.blockSignals(old)

    def isEditable(self):
        """ Get whether the title is user editable.

        Returns
        -------
        result : bool
            True if the title is user editable, False otherwise.

        """
        return self._is_editable

    def setEditable(self, editable):
        """ Set whether or not the title is user editable.

        Parameters
        ----------
        editable : bool
            True if the title is user editable, False otherwise.

        """
        self._is_editable = editable

    def isForceHidden(self):
        """ Get whether or not the title bar is force hidden.

        Returns
        -------
        result : bool
            Whether or not the title bar is always hidden.

        """
        return self._force_hidden

    def setForceHidden(self, hidden):
        """ Set the force hidden state of the title bar.

        Parameters
        ----------
        hidden : bool
            True if the title bar should be hidden, False otherwise.

        """
        self._force_hidden = hidden
        if not hidden and self._last_visible:
            super(QDockTitleBar, self).setVisible(True)
        elif hidden:
            super(QDockTitleBar, self).setVisible(False)
Esempio n. 3
0
class IDockTitleBar(QWidget):
    """ An interface class for defining a title bar.

    """
    #: A signal emitted when the maximize button is clicked.
    maximizeButtonClicked = Signal(bool)

    #: A signal emitted when the restore button is clicked.
    restoreButtonClicked = Signal(bool)

    #: A signal emitted when the close button is clicked.
    closeButtonClicked = Signal(bool)

    #: A signal emitted when the link button is toggled.
    linkButtonToggled = Signal(bool)

    #: A signal emitted when the pin button is toggled.
    pinButtonToggled = Signal(bool)

    #: A signal emitted when the title is edited by the user.
    titleEdited = Signal(str)

    #: A signal emitted when the title bar is left double clicked.
    leftDoubleClicked = Signal(QPoint)

    #: A signal emitted when the title bar is right clicked.
    rightClicked = Signal(QPoint)

    #: Do not show any buttons in the title bar.
    NoButtons = 0x0

    #: Show the maximize button in the title bar.
    MaximizeButton = 0x1

    #: Show the restore button in the title bar.
    RestoreButton = 0x2

    #: Show the close button in the title bar.
    CloseButton = 0x4

    #: Show the link button in the title bar.
    LinkButton = 0x8

    #: Show the pin button in the title bar.
    PinButton = 0x10

    def buttons(self):
        """ Get the buttons to show in the title bar.

        Returns
        -------
        result : int
            An or'd combination of the buttons to show.

        """
        raise NotImplementedError

    def setButtons(self, buttons):
        """ Set the buttons to show in the title bar.

        Parameters
        ----------
        buttons : int
            An or'd combination of the buttons to show.

        """
        raise NotImplementedError

    def title(self):
        """ Get the title string of the title bar.

        Returns
        -------
        result : unicode
            The unicode title string for the title bar.

        """
        raise NotImplementedError

    def setTitle(self, title):
        """ Set the title string of the title bar.

        Parameters
        ----------
        title : unicode
            The unicode string to use for the title bar.

        """
        raise NotImplementedError

    def label(self):
        """ Get the label for the title bar.

        Returns
        -------
        result : QTextLabel
            The label for the title bar.

        """
        raise NotImplementedError

    def icon(self):
        """ Get the icon for the title bar.

        Returns
        -------
        result : QIcon
            The icon set for the title bar.

        """
        raise NotImplementedError

    def setIcon(self, icon):
        """ Set the icon for the title bar.

        Parameters
        ----------
        icon : QIcon
            The icon to use for the title bar.

        """
        raise NotImplementedError

    def iconSize(self):
        """ Get the icon size for the title bar.

        Returns
        -------
        result : QSize
            The size to use for the icons in the title bar.

        """
        raise NotImplementedError

    def setIconSize(self, size):
        """ Set the icon size for the title bar.

        Parameters
        ----------
        icon : QSize
            The icon size to use for the title bar. Icons smaller than
            this size will not be scaled up.

        """
        raise NotImplementedError

    def isLinked(self):
        """ Get whether the link button is checked.

        Returns
        -------
        result : bool
            True if the link button is checked, False otherwise.

        """
        raise NotImplementedError

    def setLinked(self, linked):
        """ Set whether or not the link button is checked.

        Parameters
        ----------
        linked : bool
            True if the link button should be checked, False otherwise.

        """
        raise NotImplementedError

    def isPinned(self):
        """ Get whether the pin button is checked.

        Returns
        -------
        result : bool
            True if the pin button is checked, False otherwise.

        """
        raise NotImplementedError

    def setPinned(self, pinned, quiet=False):
        """ Set whether or not the pin button is checked.

        Parameters
        ----------
        pinned : bool
            True if the pin button should be checked, False otherwise.

        quiet : bool, optional
            True if the state should be set without emitted the toggled
            signal. The default is False.

        """
        raise NotImplementedError

    def isEditable(self):
        """ Get whether the title is user editable.

        Returns
        -------
        result : bool
            True if the title is user editable, False otherwise.

        """
        raise NotImplementedError

    def setEditable(self, editable):
        """ Set whether or not the title is user editable.

        Parameters
        ----------
        editable : bool
            True if the title is user editable, False otherwise.

        """
        raise NotImplementedError

    def isForceHidden(self):
        """ Get whether or not the title bar is force hidden.

        Returns
        -------
        result : bool
            Whether or not the title bar is force hidden.

        """
        raise NotImplementedError

    def setForceHidden(self, hidden):
        """ Set the force hidden state of the title bar.

        Parameters
        ----------
        hidden : bool
            True if the title bar should be hidden, False otherwise.

        """
        raise NotImplementedError
Esempio n. 4
0
class QDockFrame(QFrame):
    """ A QFrame base class for creating dock frames.

    """
    #: No resize border.
    NoBorder = 0

    #: Resize the window vertically from the north edge.
    NorthBorder = 1

    #: Resize the window horizontally from the east edge.
    EastBorder = 2

    #: Resize the window vertically from the south edge.
    SouthBorder = 3

    #: Resize the window horizontally from the west edge.
    WestBorder = 4

    #: Resize the window diagonally from the northeast edge.
    NorthEastBorder = 5

    #: Resize the window diagonally from the northwest edge.
    NorthWestBorder = 6

    #: Resize the window diagonally from the southeast edge.
    SouthEastBorder = 7

    #: Resize the window diagonally from the southwest edge.
    SouthWestBorder = 8

    #: The cursors to use for a given resize border.
    ResizeCursors = {
        NorthBorder: Qt.SizeVerCursor,
        SouthBorder: Qt.SizeVerCursor,
        EastBorder: Qt.SizeHorCursor,
        WestBorder: Qt.SizeHorCursor,
        NorthEastBorder: Qt.SizeBDiagCursor,
        SouthWestBorder: Qt.SizeBDiagCursor,
        NorthWestBorder: Qt.SizeFDiagCursor,
        SouthEastBorder: Qt.SizeFDiagCursor,
    }

    #: The handlers to use for resizing the frame.
    ResizeHandlers = {
        NorthBorder: '_resizeNorth',
        SouthBorder: '_resizeSouth',
        EastBorder: '_resizeEast',
        WestBorder: '_resizeWest',
        NorthEastBorder: '_resizeNortheast',
        SouthWestBorder: '_resizeSouthwest',
        NorthWestBorder: '_resizeNorthwest',
        SouthEastBorder: '_resizeSoutheast',
    }

    #: The size of the extra space for hit testing a resize corner.
    ResizeCornerExtra = 8

    #: A signal emitted when the linked button is toggled. This should
    #: be emitted at the appropriate times by a subclass.
    linkButtonToggled = Signal(bool)

    class FrameState(Atom):
        """ A private class for tracking dock frame state.

        """
        #: Whether the title bar is consuming the mouse events.
        mouse_title = Bool(False)

        #: The resize border based on the mouse hover position.
        resize_border = Int(0)

        #: The last size of the frame before a resize.
        last_size = Typed(QSize)

        #: The offset point of the cursor during a resize press.
        resize_offset = Typed(QPoint)

    def __init__(self, manager, parent=None):
        """ Initialize a QDockFrame.

        Parameters
        ----------
        manager : DockManager
            The manager which owns the frame.

        parent : QWidget or None
            The parent of the QDockFrame.

        """
        super(QDockFrame, self).__init__(parent)
        self.frame_state = self.FrameState()
        self._manager = manager

    def manager(self):
        """ Get a reference to the manager which owns the frame.

        Returns
        -------
        result : DockManager
            The dock manager which owns this dock frame.

        """
        return self._manager

    def raiseFrame(self):
        """ Raise this frame to the top of the dock manager Z-order.

        """
        manager = self._manager
        if manager is not None:
            manager.raise_frame(self)

    def titleBarGeometry(self):
        """ Get the geometry rect for the title bar.

        Returns
        -------
        result : QRect
            The geometry rect for the title bar, expressed in frame
            coordinates. An invalid rect should be returned if title
            bar should not be active.

        """
        return QRect()

    def resizeMargins(self):
        """ Get the margins to use for resizing the frame.

        Returns
        -------
        result : QMargins
            The margins to use for frame resizing when the frame is
            a top-level window.

        """
        return QMargins()

    def isLinked(self):
        """ Get whether or not the frame is linked.

        This method should be reimplemented by a subclass.

        Returns
        -------
        result : bool
            True if the frame is considered linked, False otherwise.

        """
        return False

    def setLinked(self, linked):
        """ Set whether or not the frame is linked.

        This method should be reimplemented by a subclass.

        Parameters
        ----------
        linked : bool
            True if the frame is considered linked, False otherwise.

        """
        pass

    #--------------------------------------------------------------------------
    # Event Handlers
    #--------------------------------------------------------------------------
    def event(self, event):
        """ Handle the generic events for the frame.

        This handler maintains proper Z-order of the frames within the
        manager's frame list and exposes some custom event handlers
        appropriate for dock frames.

        """
        if event.type() == QEvent.HoverMove:
            self.hoverMoveEvent(event)
            return event.isAccepted()
        if event.type() == QEvent.WindowActivate and self.isWindow():
            self.raiseFrame()
        return super(QDockFrame, self).event(event)

    def mousePressEvent(self, event):
        """ Handle the mouse press event for the dock frame.

        """
        event.ignore()
        state = self.frame_state
        geo = self.titleBarGeometry()
        if geo.isValid() and geo.contains(event.pos()):
            if self.titleBarMousePressEvent(event):
                if self.isWindow():
                    self.activateWindow()
                    self.raise_()
                event.accept()
                state.mouse_title = True
                return
        if self.isWindow() and event.button() == Qt.LeftButton:
            border, offset = self._resizeBorderTest(event.pos())
            if border != self.NoBorder:
                state.resize_border = border
                state.resize_offset = offset
                state.last_size = self.size()
                event.accept()

    def mouseMoveEvent(self, event):
        """ Handle the mouse move event for the dock frame.

        """
        event.ignore()
        state = self.frame_state
        if state.mouse_title:
            if self.titleBarMouseMoveEvent(event):
                event.accept()
                return
        if self.isWindow() and state.resize_border != self.NoBorder:
            border = state.resize_border
            handler = getattr(self, self.ResizeHandlers[border])
            handler(event.pos(), state.resize_offset)
            event.accept()

    def mouseReleaseEvent(self, event):
        """ Handle the mouse release event for the dock frame.

        """
        event.ignore()
        state = self.frame_state
        self._refreshCursor(event.pos())
        if state.mouse_title:
            if self.titleBarMouseReleaseEvent(event):
                event.accept()
                state.mouse_title = False
                return
        if self.isWindow() and event.button() == Qt.LeftButton:
            state.resize_border = self.NoBorder
            state.resize_offset = None
            if state.last_size is not None:
                if state.last_size != self.size():
                    self.manager().frame_resized(self)
                del state.last_size
            event.accept()

    def hoverMoveEvent(self, event):
        """ Handle the hover move event for the frame.

        """
        event.ignore()
        if not self.isWindow() or self.isMaximized():
            return
        if QApplication.mouseButtons() != Qt.NoButton:
            return
        state = self.frame_state
        if state.mouse_title:
            return
        if state.resize_border != self.NoBorder:
            return
        self._refreshCursor(event.pos())
        event.accept()

    def titleBarMousePressEvent(self, event):
        """ Handle a mouse press event on the title bar.

        Returns
        -------
        result : bool
            True if the event is handled, False otherwise.

        """
        return False

    def titleBarMouseMoveEvent(self, event):
        """ Handle a mouse move event on the title bar.

        Returns
        -------
        result : bool
            True if the event is handled, False otherwise.

        """
        return False

    def titleBarMouseReleaseEvent(self, event):
        """ Handle a mouse release event on the title bar.

        Returns
        -------
        result : bool
            True if the event is handled, False otherwise.

        """
        return False

    #--------------------------------------------------------------------------
    # Resize Handling
    #--------------------------------------------------------------------------
    def _refreshCursor(self, pos):
        """ Refresh the resize cursor for the given position.

        Parameters
        ----------
        pos : QPoint
            The point of interest, expressed in local coordinates.

        """
        border, ignored = self._resizeBorderTest(pos)
        cursor = self.ResizeCursors.get(border)
        if cursor is None:
            self.unsetCursor()
        else:
            self.setCursor(cursor)

    def _resizeBorderTest(self, pos):
        """ Hit test the frame for resizing.

        Parameters
        ----------
        pos : QPoint
            The point of interest, expressed in local coordinates.

        Returns
        -------
        result : tuple
            A 2-tuple of (int, QPoint) representing the resize border
            and offset for the border.

        """
        rect = self.rect()
        if not rect.contains(pos):
            return (self.NoBorder, QPoint())
        x = pos.x()
        y = pos.y()
        width = rect.width()
        height = rect.height()
        margins = self.resizeMargins()
        extra = self.ResizeCornerExtra
        if x < margins.left():
            if y < margins.top() + extra:
                mode = self.NorthWestBorder
                offset = QPoint(x, y)
            elif y > height - (margins.bottom() + extra):
                mode = self.SouthWestBorder
                offset = QPoint(x, height - y)
            else:
                mode = self.WestBorder
                offset = QPoint(x, 0)
        elif y < margins.top():
            if x < margins.left() + extra:
                mode = self.NorthWestBorder
                offset = QPoint(x, y)
            elif x > width - (margins.right() + extra):
                mode = self.NorthEastBorder
                offset = QPoint(width - x, y)
            else:
                mode = self.NorthBorder
                offset = QPoint(0, y)
        elif x > width - margins.right():
            if y < margins.top() + extra:
                mode = self.NorthEastBorder
                offset = QPoint(width - x, y)
            elif y > height - (margins.bottom() + extra):
                mode = self.SouthEastBorder
                offset = QPoint(width - x, height - y)
            else:
                mode = self.EastBorder
                offset = QPoint(width - x, 0)
        elif y > height - margins.bottom():
            if x < margins.left() + extra:
                mode = self.SouthWestBorder
                offset = QPoint(x, height - y)
            elif x > width - (margins.right() + extra):
                mode = self.SouthEastBorder
                offset = QPoint(width - x, height - y)
            else:
                mode = self.SouthBorder
                offset = QPoint(0, height - y)
        else:
            mode = self.NoBorder
            offset = QPoint()
        return mode, offset

    def _resizeNorth(self, pos, offset):
        """ A resize handler for north resizing.

        """
        dh = pos.y() - offset.y()
        height = self.height()
        min_height = self.minimumSizeHint().height()
        if height - dh < min_height:
            dh = height - min_height
        rect = self.geometry()
        rect.setY(rect.y() + dh)
        self.setGeometry(rect)

    def _resizeSouth(self, pos, offset):
        """ A resize handler for south resizing.

        """
        dh = pos.y() - self.height() + offset.y()
        size = self.size()
        size.setHeight(size.height() + dh)
        self.resize(size)

    def _resizeEast(self, pos, offset):
        """ A resize handler for east resizing.

        """
        dw = pos.x() - self.width() + offset.x()
        size = self.size()
        size.setWidth(size.width() + dw)
        self.resize(size)

    def _resizeWest(self, pos, offset):
        """ A resize handler for west resizing.

        """
        dw = pos.x() - offset.x()
        width = self.width()
        min_width = self.minimumSizeHint().width()
        if width - dw < min_width:
            dw = width - min_width
        rect = self.geometry()
        rect.setX(rect.x() + dw)
        self.setGeometry(rect)

    def _resizeNortheast(self, pos, offset):
        """ A resize handler for northeast resizing.

        """
        dw = pos.x() - self.width() + offset.x()
        dh = pos.y() - offset.y()
        size = self.size()
        min_size = self.minimumSizeHint()
        if size.height() - dh < min_size.height():
            dh = size.height() - min_size.height()
        rect = self.geometry()
        rect.setWidth(rect.width() + dw)
        rect.setY(rect.y() + dh)
        self.setGeometry(rect)

    def _resizeNorthwest(self, pos, offset):
        """ A resize handler for northwest resizing.

        """
        dw = pos.x() - offset.x()
        dh = pos.y() - offset.y()
        size = self.size()
        min_size = self.minimumSizeHint()
        if size.width() - dw < min_size.width():
            dw = size.width() - min_size.width()
        if size.height() - dh < min_size.height():
            dh = size.height() - min_size.height()
        rect = self.geometry()
        rect.setX(rect.x() + dw)
        rect.setY(rect.y() + dh)
        self.setGeometry(rect)

    def _resizeSouthwest(self, pos, offset):
        """ A resize handler for southwest resizing.

        """
        dw = pos.x() - offset.x()
        dh = pos.y() - self.height() + offset.y()
        size = self.size()
        min_size = self.minimumSizeHint()
        if size.width() - dw < min_size.width():
            dw = size.width() - min_size.width()
        rect = self.geometry()
        rect.setX(rect.x() + dw)
        rect.setHeight(rect.height() + dh)
        self.setGeometry(rect)

    def _resizeSoutheast(self, pos, offset):
        """ A resize handler for southeast resizing.

        """
        dw = pos.x() - self.width() + offset.x()
        dh = pos.y() - self.height() + offset.y()
        size = self.size()
        size.setWidth(size.width() + dw)
        size.setHeight(size.height() + dh)
        self.resize(size)
Esempio n. 5
0
class QDockWindowButtons(QFrame):
    """ A custom QFrame which provides the buttons for a QDockWindow.

    """
    #: A signal emitted when the maximize button is clicked.
    maximizeButtonClicked = Signal(bool)

    #: A signal emitted when the restore button is clicked.
    restoreButtonClicked = Signal(bool)

    #: A signal emitted when the close button is closed.
    closeButtonClicked = Signal(bool)

    #: A signal emitted when the link button is toggled.
    linkButtonToggled = Signal(bool)

    #: Do not show any buttons in the widget.
    NoButtons = 0x0

    #: Show the maximize button in the widget.
    MaximizeButton = 0x1

    #: Show the restore button in the widget.
    RestoreButton = 0x2

    #: Show the close button in the widget.
    CloseButton = 0x4

    #: Show the link button in the widget.
    LinkButton = 0x8

    def __init__(self, parent=None):
        """ Initialize a QDockWindowButtons instance.

        Parameters
        ----------
        parent : QWidget, optional
            The parent of the window buttons.

        """
        super(QDockWindowButtons, self).__init__(parent)
        self._buttons = (self.CloseButton | self.MaximizeButton
                         | self.LinkButton)

        max_button = self._max_button = QBitmapButton(self)
        max_button.setObjectName('dockwindow-maximize-button')
        max_button.setBitmap(MAXIMIZE_BUTTON.toBitmap())
        max_button.setIconSize(QSize(20, 15))
        max_button.setVisible(self._buttons & self.MaximizeButton)
        max_button.setToolTip('Maximize')

        restore_button = self._restore_button = QBitmapButton(self)
        restore_button.setObjectName('dockwindow-restore-button')
        restore_button.setBitmap(RESTORE_BUTTON.toBitmap())
        restore_button.setIconSize(QSize(20, 15))
        restore_button.setVisible(self._buttons & self.RestoreButton)
        restore_button.setToolTip('Restore Down')

        close_button = self._close_button = QBitmapButton(self)
        close_button.setObjectName('dockwindow-close-button')
        close_button.setBitmap(CLOSE_BUTTON.toBitmap())
        close_button.setIconSize(QSize(34, 15))
        close_button.setVisible(self._buttons & self.CloseButton)
        close_button.setToolTip('Close')

        link_button = self._link_button = QCheckedBitmapButton(self)
        link_button.setObjectName('dockwindow-link-button')
        link_button.setBitmap(UNLINKED_BUTTON.toBitmap())
        link_button.setCheckedBitmap(LINKED_BUTTON.toBitmap())
        link_button.setIconSize(QSize(20, 15))
        link_button.setVisible(self._buttons & self.LinkButton)
        link_button.setToolTip('Link Window')
        link_button.setCheckedToolTip('Unlink Window')

        layout = QHBoxLayout()
        layout.setContentsMargins(QMargins(0, 0, 0, 0))
        layout.setSpacing(1)

        layout.addWidget(link_button)
        layout.addWidget(max_button)
        layout.addWidget(restore_button)
        layout.addWidget(close_button)

        self.setLayout(layout)

        max_button.clicked.connect(self.maximizeButtonClicked)
        restore_button.clicked.connect(self.restoreButtonClicked)
        close_button.clicked.connect(self.closeButtonClicked)
        link_button.toggled.connect(self.linkButtonToggled)

    #--------------------------------------------------------------------------
    # Public API
    #--------------------------------------------------------------------------
    def buttons(self):
        """ Get the buttons to show in the title bar.

        Returns
        -------
        result : int
            An or'd combination of the buttons to show.

        """
        return self._buttons

    def setButtons(self, buttons):
        """ Set the buttons to show in the title bar.

        Parameters
        ----------
        buttons : int
            An or'd combination of the buttons to show.

        """
        self._buttons = buttons
        self._max_button.setVisible(buttons & self.MaximizeButton)
        self._restore_button.setVisible(buttons & self.RestoreButton)
        self._close_button.setVisible(buttons & self.CloseButton)
        self._link_button.setVisible(buttons & self.LinkButton)

    def isLinked(self):
        """ Get whether the link button is checked.

        Returns
        -------
        result : bool
            True if the link button is checked, False otherwise.

        """
        return self._link_button.isChecked()

    def setLinked(self, linked):
        """ Set whether or not the link button is checked.

        Parameters
        ----------
        linked : bool
            True if the link button should be checked, False otherwise.

        """
        self._link_button.setChecked(linked)
Esempio n. 6
0
class QDockContainer(QDockFrame):
    """ A QDockFrame which holds a QDockItem instance.

    A QDockContainer has a dynamic boolean property 'floating' which
    can be used to apply custom stylesheet styling when the container
    is a floating top level window versus docked in a dock area.

    """
    #: A signal emitted when the container changes its toplevel state.
    topLevelChanged = Signal(bool)

    #: A signal emitted when the container is alerted.
    alerted = Signal(str)

    class FrameState(QDockFrame.FrameState):
        """ A private class for managing container drag state.

        """
        #: The original title bar press position.
        press_pos = Typed(QPoint)

        #: The position of the frame when first moved.
        start_pos = Typed(QPoint)

        #: Whether or not the dock item is being dragged.
        dragging = Bool(False)

        #: Whether the frame was maximized before moving.
        frame_was_maximized = Bool(False)

        #: Whether the dock item is maximized in the dock area.
        item_is_maximized = Bool(False)

        #: Whether or not the container is stored in a dock bar. This
        #: value is manipulated directly by the QDockBarManager.
        in_dock_bar = Bool(False)

    def __init__(self, manager, parent=None):
        """ Initialize a QDockContainer.

        Parameters
        ----------
        manager : DockManager
            The manager which owns the container.

        parent : QWidget or None
            The parent of the QDockContainer.

        """
        super(QDockContainer, self).__init__(manager, parent)
        layout = QDockContainerLayout()
        layout.setSizeConstraint(QLayout.SetMinAndMaxSize)
        self.setLayout(layout)
        self.setProperty('floating', False)
        self.alerted.connect(self.onAlerted)
        self._dock_item = None

    def titleBarGeometry(self):
        """ Get the geometry rect for the title bar.

        Returns
        -------
        result : QRect
            The geometry rect for the title bar, expressed in frame
            coordinates. An invalid rect is returned if title bar
            should not be active.

        """
        title_bar = self.dockItem().titleBarWidget()
        if title_bar.isHidden():
            return QRect()
        pt = title_bar.mapTo(self, QPoint(0, 0))
        return QRect(pt, title_bar.size())

    def resizeMargins(self):
        """ Get the margins to use for resizing the container.

        Returns
        -------
        result : QMargins
            The margins to use for container resizing when the container
            is a top-level window.

        """
        if self.isMaximized():
            return QMargins()
        return self.layout().contentsMargins()

    def showMaximized(self):
        """ Handle the show maximized request for the dock container.

        """
        def update_buttons(bar, link=False, pin=False):
            buttons = bar.buttons()
            buttons |= bar.RestoreButton
            buttons &= ~bar.MaximizeButton
            if link:
                buttons &= ~bar.LinkButton
            if pin:
                buttons &= ~bar.PinButton
            bar.setButtons(buttons)

        if self.isWindow():
            super(QDockContainer, self).showMaximized()
            self.setLinked(False)
            update_buttons(self.dockItem().titleBarWidget(), link=True)
        else:
            area = self.parentDockArea()
            if area is not None:
                item = self.dockItem()
                update_buttons(item.titleBarWidget(), pin=True)
                area.setMaximizedWidget(item)
                self.frame_state.item_is_maximized = True
                item.installEventFilter(self)

    def showNormal(self):
        """ Handle the show normal request for the dock container.

        """
        def update_buttons(bar, link=False, pin=False):
            buttons = bar.buttons()
            buttons |= bar.MaximizeButton
            buttons &= ~bar.RestoreButton
            if link:
                buttons |= bar.LinkButton
            if pin:
                buttons |= bar.PinButton
            bar.setButtons(buttons)

        if self.isWindow():
            super(QDockContainer, self).showNormal()
            self.setLinked(False)
            update_buttons(self.dockItem().titleBarWidget(), link=True)
        elif self.frame_state.item_is_maximized:
            item = self.dockItem()
            update_buttons(item.titleBarWidget(), pin=True)
            self.layout().setWidget(item)
            self.frame_state.item_is_maximized = False
            item.removeEventFilter(self)

    #--------------------------------------------------------------------------
    # Framework API
    #--------------------------------------------------------------------------
    def dockItem(self):
        """ Get the dock item installed on the container.

        Returns
        -------
        result : QDockItem or None
            The dock item installed in the container, or None.

        """
        return self._dock_item

    def setDockItem(self, dock_item):
        """ Set the dock item for the container.

        Parameters
        ----------
        dock_item : QDockItem
            The dock item to use in the container.

        """
        layout = self.layout()
        old = layout.getWidget()
        if old is not None:
            old.maximizeButtonClicked.disconnect(self.showMaximized)
            old.restoreButtonClicked.disconnect(self.showNormal)
            old.closeButtonClicked.disconnect(self.close)
            old.linkButtonToggled.disconnect(self.linkButtonToggled)
            old.pinButtonToggled.disconnect(self.onPinButtonToggled)
            old.titleBarLeftDoubleClicked.disconnect(self.toggleMaximized)
            old.alerted.disconnect(self.alerted)
        if dock_item is not None:
            dock_item.maximizeButtonClicked.connect(self.showMaximized)
            dock_item.restoreButtonClicked.connect(self.showNormal)
            dock_item.closeButtonClicked.connect(self.close)
            dock_item.linkButtonToggled.connect(self.linkButtonToggled)
            dock_item.pinButtonToggled.connect(self.onPinButtonToggled)
            dock_item.titleBarLeftDoubleClicked.connect(self.toggleMaximized)
            dock_item.alerted.connect(self.alerted)
        layout.setWidget(dock_item)
        self._dock_item = dock_item

    def title(self):
        """ Get the title for the container.

        This proxies the call to the underlying dock item.

        """
        item = self.dockItem()
        if item is not None:
            return item.title()
        return u''

    def icon(self):
        """ Get the icon for the container.

        This proxies the call to the underlying dock item.

        """
        item = self.dockItem()
        if item is not None:
            return item.icon()
        return QIcon()

    def closable(self):
        """ Get whether or not the container is closable.

        This proxies the call to the underlying dock item.

        """
        item = self.dockItem()
        if item is not None:
            return item.closable()
        return True

    def isLinked(self):
        """ Get whether or not the container is linked.

        This proxies the call to the underlying dock item.

        """
        item = self.dockItem()
        if item is not None:
            return item.isLinked()
        return False

    def setLinked(self, linked):
        """ Set whether or not the container should be linked.

        This proxies the call to the underlying dock item.

        """
        item = self.dockItem()
        if item is not None:
            item.setLinked(linked)

    def isPinned(self):
        """ Get whether or not the container is pinned.

        This proxies the call to the underlying dock item.

        """
        item = self.dockItem()
        if item is not None:
            return item.isPinned()
        return False

    def setPinned(self, pinned, quiet=False):
        """ Set whether or not the container should be pinned.

        This proxies the call to the underlying dock item.

        """
        item = self.dockItem()
        if item is not None:
            item.setPinned(pinned, quiet)

    def showTitleBar(self):
        """ Show the title bar for the container.

        This proxies the call to the underlying dock item.

        """
        item = self.dockItem()
        if item is not None:
            item.titleBarWidget().show()

    def hideTitleBar(self):
        """ Hide the title bar for the container.

        This proxies the call to the underlying dock item.

        """
        item = self.dockItem()
        if item is not None:
            item.titleBarWidget().hide()

    def showLinkButton(self):
        """ Show the link button on the title bar.

        """
        item = self.dockItem()
        if item is not None:
            bar = item.titleBarWidget()
            bar.setButtons(bar.buttons() | bar.LinkButton)

    def hideLinkButton(self):
        """ Hide the link button on the title bar.

        """
        item = self.dockItem()
        if item is not None:
            bar = item.titleBarWidget()
            bar.setButtons(bar.buttons() & ~bar.LinkButton)

    def showPinButton(self):
        """ Show the pin button on the title bar.

        """
        item = self.dockItem()
        if item is not None:
            bar = item.titleBarWidget()
            bar.setButtons(bar.buttons() | bar.PinButton)

    def hidePinButton(self):
        """ Hide the pin button on the title bar.

        """
        item = self.dockItem()
        if item is not None:
            bar = item.titleBarWidget()
            bar.setButtons(bar.buttons() & ~bar.PinButton)

    def toggleMaximized(self):
        """ Toggle the maximized state of the container.

        """
        is_win = self.isWindow()
        is_maxed = self.isMaximized()
        item_maxed = self.frame_state.item_is_maximized
        if is_win and is_maxed or item_maxed:
            self.showNormal()
        else:
            self.showMaximized()

    def reset(self):
        """ Reset the container to the initial pre-docked state.

        """
        state = self.frame_state
        state.dragging = False
        state.press_pos = None
        state.start_pos = None
        state.frame_was_maximized = False
        state.in_dock_bar = False
        self.showNormal()
        self.unfloat()
        self.hideLinkButton()
        self.setLinked(False)
        self.showTitleBar()
        self.setAttribute(Qt.WA_WState_ExplicitShowHide, False)
        self.setAttribute(Qt.WA_WState_Hidden, False)

    def float(self):
        """ Set the window state to be a toplevel floating window.

        """
        self.hide()
        self.setAttribute(Qt.WA_Hover, True)
        flags = Qt.Tool | Qt.FramelessWindowHint
        self.setParent(self.manager().dock_area(), flags)
        self.layout().setContentsMargins(QMargins(5, 5, 5, 5))
        self.setProperty('floating', True)
        self.setLinked(False)
        self.showLinkButton()
        self.hidePinButton()
        repolish(self)
        repolish(self.dockItem())
        self.topLevelChanged.emit(True)

    def unfloat(self):
        """ Set the window state to be non-floating window.

        """
        self.hide()
        self.setAttribute(Qt.WA_Hover, False)
        self.setParent(self.manager().dock_area(), Qt.Widget)
        self.layout().setContentsMargins(QMargins(0, 0, 0, 0))
        self.unsetCursor()
        self.setProperty('floating', False)
        self.setLinked(False)
        self.hideLinkButton()
        self.showPinButton()
        repolish(self)
        repolish(self.dockItem())
        self.topLevelChanged.emit(False)

    def parentDockArea(self):
        """ Get the parent dock area of the container.

        Returns
        -------
        result : QDockArea or None
            The nearest ancestor which is an instance of QDockArea, or
            None if no such ancestor exists.

        """
        parent = self.parent()
        while parent is not None:
            if isinstance(parent, QDockArea):
                return parent
            parent = parent.parent()

    def parentDockTabWidget(self):
        """ Get the parent dock tab of the container.

        Returns
        -------
        result : QDockTabWidget or None
            The nearest ancestor which is an instance of QDockTabWidget,
            or None if no such ancestor exists.

        """
        parent = self.parent()
        parent_area = self.parentDockArea()
        while parent is not None:
            if isinstance(parent, QDockTabWidget):
                return parent
            elif parent is parent_area:
                return None
            parent = parent.parent()

    def unplug(self):
        """ Unplug the container from its containing dock area.

        This method is invoked by the framework when appropriate. It
        should not need to be called by user code.

        Returns
        -------
        result : bool
            True if the container was unplugged, False otherwise.

        """
        dock_area = self.parentDockArea()
        if dock_area is None:
            return False
        if self.frame_state.in_dock_bar:
            dock_area.removeFromDockBar(self)
            return True
        # avoid a circular import
        from .layout_handling import unplug_container
        return unplug_container(dock_area, self)

    def untab(self, pos):
        """ Unplug the container from a tab control.

        This method is invoked by the QDockTabBar when the container
        should be torn out. It synthesizes the appropriate internal
        state so that the item can continue to be dock dragged. This
        method should not be called by user code.

        Parameters
        ----------
        pos : QPoint
            The global mouse position.

        Returns
        -------
        result : bool
            True on success, False otherwise.

        """
        if not self.unplug():
            return
        self.postUndockedEvent()
        state = self.frame_state
        state.mouse_title = True
        state.dragging = True
        state.frame_was_maximized = False
        self.float()
        self.raiseFrame()
        title_bar = self.dockItem().titleBarWidget()
        title_pos = QPoint(title_bar.width() / 2, title_bar.height() / 2)
        margins = self.layout().contentsMargins()
        offset = QPoint(margins.left(), margins.top())
        state.press_pos = title_bar.mapTo(self, title_pos) + offset
        state.start_pos = pos - state.press_pos
        self.move(state.start_pos)
        self.show()
        self.grabMouse()
        self.activateWindow()
        self.raise_()

    def postUndockedEvent(self):
        """ Post a DockItemUndocked event to the root dock area.

        """
        root_area = self.manager().dock_area()
        if root_area.dockEventsEnabled():
            event = QDockItemEvent(DockItemUndocked, self.objectName())
            QApplication.postEvent(root_area, event)

    #--------------------------------------------------------------------------
    # Signal Handlers
    #--------------------------------------------------------------------------
    def onPinButtonToggled(self, pinned):
        """ The signal handler for the 'pinButtonToggled' signal.

        This handler will pin or unpin the container in response to the
        user toggling the pin button.

        """
        area = self.parentDockArea()
        if area is not None:
            if pinned:
                if not self.frame_state.in_dock_bar:
                    position = _closestDockBar(self)
                    self.unplug()
                    area.addToDockBar(self, position)
            else:
                position = area.dockBarPosition(self)
                if position is not None:
                    # avoid a circular import
                    from .layout_handling import plug_frame
                    area.removeFromDockBar(self)
                    if area.centralWidget() is None:
                        guide = QGuideRose.Guide.AreaCenter
                    else:
                        guide = (QGuideRose.Guide.BorderNorth,
                                 QGuideRose.Guide.BorderEast,
                                 QGuideRose.Guide.BorderSouth,
                                 QGuideRose.Guide.BorderWest)[position]
                    plug_frame(area, None, self, guide)

    def onAlerted(self, level):
        """ A signal handler for the 'alerted' signal.

        """
        self.setProperty('alert', level or None)
        repolish(self)

    #--------------------------------------------------------------------------
    # Event Handlers
    #--------------------------------------------------------------------------
    def eventFilter(self, obj, event):
        """ Filter the events for the dock item.

        This filter will proxy out the mouse events for the dock item.
        This event filter will only be activated when the dock item is
        set to maximzed mode.

        """
        if obj is not self._dock_item:
            return False
        if event.type() == QEvent.MouseButtonPress:
            if event.button() == Qt.LeftButton:
                self._dock_item.clearAlert(
                )  # likely a no-op, but just in case
            return self.filteredMousePressEvent(event)
        elif event.type() == QEvent.MouseMove:
            return self.filteredMouseMoveEvent(event)
        elif event.type() == QEvent.MouseButtonRelease:
            return self.filteredMouseReleaseEvent(event)
        return False

    def filteredMousePressEvent(self, event):
        """ Handle the filtered mouse press event for the dock item.

        """
        bar = self.dockItem().titleBarWidget()
        if bar.isVisible() and bar.geometry().contains(event.pos()):
            self.frame_state.mouse_title = True
            return self.titleBarMousePressEvent(event)
        return False

    def filteredMouseMoveEvent(self, event):
        """ Handle the filtered mouse move event for the dock item.

        """
        if self.frame_state.mouse_title:
            return self.titleBarMouseMoveEvent(event)
        return False

    def filteredMouseReleaseEvent(self, event):
        """ Handle the filtered mouse release event for the dock item.

        """
        if self.frame_state.mouse_title:
            self.frame_state.mouse_title = False
            return self.titleBarMouseReleaseEvent(event)
        return False

    def closeEvent(self, event):
        """ Handle the close event for the dock container.

        """
        self.manager().close_container(self, event)

    def keyPressEvent(self, event):
        """ Handle the key press event for the dock container.

        If the Escape key is pressed while dragging a floating
        container, the container will be released. If it is not
        released over a dock target, it will be moved back to its
        starting position.

        """
        super(QDockContainer, self).keyPressEvent(event)
        state = self.frame_state
        if state.dragging and event.key() == Qt.Key_Escape:
            pos = state.start_pos
            self._releaseFrame()
            if self.isWindow() and pos is not None:
                self.move(pos)
                if state.frame_was_maximized:
                    self.showMaximized()

    def titleBarMousePressEvent(self, event):
        """ Handle a mouse press event on the title bar.

        Returns
        -------
        result : bool
            True if the event is handled, False otherwise.

        """
        if event.button() == Qt.LeftButton:
            state = self.frame_state
            if state.press_pos is None:
                state.press_pos = event.pos()
                state.start_pos = self.pos()
                return True
        return False

    def titleBarMouseMoveEvent(self, event):
        """ Handle a mouse move event on the title bar.

        Returns
        -------
        result : bool
            True if the event is handled, False otherwise.

        """
        state = self.frame_state
        if state.press_pos is None:
            return False

        # If dragging and floating, move the container's position and
        # notify the manager of that the container was mouse moved. If
        # the container is maximized, it is first restored before.
        global_pos = event.globalPos()
        if state.dragging:
            if self.isWindow():
                target_pos = global_pos - state.press_pos
                self.manager().drag_move_frame(self, target_pos, global_pos)
            return True

        # Ensure the drag has crossed the app drag threshold.
        dist = (event.pos() - state.press_pos).manhattanLength()
        if dist <= QApplication.startDragDistance():
            return True

        # If the container is already floating, ensure that it is shown
        # normal size. The next move event will move the window.
        state.dragging = True
        if self.isWindow():
            state.frame_was_maximized = self.isMaximized()
            if state.frame_was_maximized:
                coeff = state.press_pos.x() / float(self.width())
                self.showNormal()
                state.press_pos = _computePressPos(self, coeff)
            return True

        # Restore a maximized dock item before unplugging.
        if state.item_is_maximized:
            bar = self.dockItem().titleBarWidget()
            coeff = state.press_pos.x() / float(bar.width())
            self.showNormal()
            state.press_pos = _computePressPos(self, coeff)

        # Unplug the container from the layout before floating so
        # that layout widgets can clean themselves up when empty.
        if not self.unplug():
            return False
        self.postUndockedEvent()

        # Make the container a toplevel frame, update it's Z-order,
        # and grab the mouse to continue processing drag events.
        self.float()
        self.raiseFrame()
        margins = self.layout().contentsMargins()
        state.press_pos += QPoint(0, margins.top())
        state.start_pos = global_pos - state.press_pos
        self.move(state.start_pos)
        self.show()
        self.grabMouse()
        self.activateWindow()
        self.raise_()
        return True

    def _releaseFrame(self):
        """ A helper method which releases the frame grab.

        Returns
        -------
        result : bool
            True if the frame was released, False otherwise.

        """
        state = self.frame_state
        if state.press_pos is not None:
            self.releaseMouse()
            if self.isWindow():
                self.manager().drag_release_frame(self, QCursor.pos())
            state.dragging = False
            state.press_pos = None
            state.start_pos = None
            return True
        return False

    def titleBarMouseReleaseEvent(self, event):
        """ Handle a mouse release event on the title bar.

        Returns
        -------
        result : bool
            True if the event is handled, False otherwise.

        """
        if event.button() == Qt.LeftButton:
            return self._releaseFrame()
        return False
Esempio n. 7
0
class QDockItem(QFrame):
    """ A QFrame subclass which acts as an item QDockArea.

    """
    #: A signal emitted when the maximize button is clicked. This
    #: signal is proxied from the current dock item title bar.
    maximizeButtonClicked = Signal(bool)

    #: A signal emitted when the restore button is clicked. This
    #: signal is proxied from the current dock item title bar.
    restoreButtonClicked = Signal(bool)

    #: A signal emitted when the close button is clicked. This
    #: signal is proxied from the current dock item title bar.
    closeButtonClicked = Signal(bool)

    #: A signal emitted when the link button is toggled. This
    #: signal is proxied from the current dock item title bar.
    linkButtonToggled = Signal(bool)

    #: A signal emitted when the pin button is toggled. This
    #: signal is proxied from the current dock item title bar.
    pinButtonToggled = Signal(bool)

    #: A signal emitted when the title is edited by the user. This
    #: signal is proxied from the current dock item title bar.
    titleEdited = Signal(str)

    #: A signal emitted when the empty area is left double clicked.
    #: This signal is proxied from the current dock item title bar.
    titleBarLeftDoubleClicked = Signal(QPoint)

    #: A signal emitted when the empty area is right clicked. This
    #: signal is proxied from the current dock item title bar.
    titleBarRightClicked = Signal(QPoint)

    #: A signal emitted when the item is alerted. The payload is the
    #: new alert level. An empty string indicates no alert.
    alerted = Signal(str)

    def __init__(self, parent=None):
        """ Initialize a QDockItem.

        Parameters
        ----------
        parent : QWidget, optional
            The parent of the dock item.

        """
        super(QDockItem, self).__init__(parent)
        layout = QDockItemLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSizeConstraint(QLayout.SetMinAndMaxSize)
        self.setLayout(layout)
        self.setTitleBarWidget(QDockTitleBar())
        self.alerted.connect(self._onAlerted)
        self._manager = None  # Set and cleared by the DockManager
        self._alert_data = None
        self._vis_changed = None
        self._closable = True
        self._closing = False

    #--------------------------------------------------------------------------
    # Reimplementations
    #--------------------------------------------------------------------------
    def close(self):
        """ Handle the close request for the dock item.

        """
        self._closing = True
        try:
            super(QDockItem, self).close()
        finally:
            self._closing = False

    def closeEvent(self, event):
        """ Handle the close event for the dock item.

        This handler will reject the event if the item is not closable.

        """
        event.ignore()
        if self._closable:
            event.accept()
            area = self.rootDockArea()
            if area is not None and area.dockEventsEnabled():
                event = QDockItemEvent(DockItemClosed, self.objectName())
                QApplication.postEvent(area, event)

    def showEvent(self, event):
        """ Handle the show event for the container.

        This handler posts a visibility change event.

        """
        super(QDockItem, self).showEvent(event)
        self._postVisibilityChange(True)

    def hideEvent(self, event):
        """ Handle the hide event for the container.

        This handler posts a visibility change event.

        """
        super(QDockItem, self).hideEvent(event)
        # Don't post when closing; A closed event is posted instead.
        if not self._closing:
            self._postVisibilityChange(False)

    def mousePressEvent(self, event):
        """ Handle the mouse press event for the dock item.

        This handler will clear any alert level on a left click.

        """
        if event.button() == Qt.LeftButton:
            self.clearAlert()
        super(QDockItem, self).mousePressEvent(event)

    #--------------------------------------------------------------------------
    # Public API
    #--------------------------------------------------------------------------
    def manager(self):
        """ Get the dock manager for this dock item.

        Returns
        -------
        result : DockManager or None
            The dock manager which is managing this item.

        """
        return self._manager

    def rootDockArea(self):
        """ Get the root dock area for this dock item.

        Returns
        -------
        result : QDockArea or None
            The root dock area for this dock item.

        """
        manager = self._manager
        if manager is not None:
            return manager.dock_area()

    def title(self):
        """ Get the title for the dock item.

        Returns
        -------
        result : unicode
            The unicode title for the dock item.

        """
        return self.titleBarWidget().title()

    def setTitle(self, title):
        """ Set the title for the dock item.

        Parameters
        ----------
        title : unicode
            The unicode title to use for the dock item.

        """
        self.titleBarWidget().setTitle(title)
        # A concession to practicality: walk the ancestry and update
        # the tab title if this item lives in a dock tab.
        container = self.parent()
        if container is not None:
            stacked = container.parent()
            if stacked is not None:
                tabs = stacked.parent()
                if isinstance(tabs, QDockTabWidget):
                    index = tabs.indexOf(container)
                    tabs.setTabText(index, title)

    def icon(self):
        """ Get the icon for the dock item.

        Returns
        -------
        result : QIcon
            The icon in use for the dock item.

        """
        return self.titleBarWidget().icon()

    def setIcon(self, icon):
        """ Set the icon for the dock item.

        Parameters
        ----------
        icon : QIcon
            The icon to use for the dock item.

        """
        self.titleBarWidget().setIcon(icon)
        # A concession to practicality: walk the ancestry and update
        # the tab icon if this item lives in a dock tab.
        container = self.parent()
        if container is not None:
            stacked = container.parent()
            if stacked is not None:
                tabs = stacked.parent()
                if isinstance(tabs, QDockTabWidget):
                    index = tabs.indexOf(container)
                    tabs.setTabIcon(index, icon)

    def iconSize(self):
        """ Get the icon size for the title bar.

        Returns
        -------
        result : QSize
            The size to use for the icons in the title bar.

        """
        return self.titleBarWidget().iconSize()

    def setIconSize(self, size):
        """ Set the icon size for the title bar.

        Parameters
        ----------
        icon : QSize
            The icon size to use for the title bar. Icons smaller than
            this size will not be scaled up.

        """
        self.titleBarWidget().setIconSize(size)

    def isLinked(self):
        """ Get whether or not this dock item is linked.

        Returns
        -------
        result : bool
            True if the item is linked, False otherwise.

        """
        return self.titleBarWidget().isLinked()

    def setLinked(self, linked):
        """ Set whether or not the dock item is linked.

        Parameters
        ----------
        linked : bool
            True if the dock item should be linked, False otherwise.

        """
        self.titleBarWidget().setLinked(linked)

    def isPinned(self):
        """ Get whether or not this dock item is pinned.

        Returns
        -------
        result : bool
            True if the item is pinned, False otherwise.

        """
        return self.titleBarWidget().isPinned()

    def setPinned(self, pinned, quiet=False):
        """ Set whether or not the dock item is pinned.

        Parameters
        ----------
        pinned : bool
            True if the dock item should be pinned, False otherwise.

        quiet : bool, optional
            True if the state should be set without emitted the toggled
            signal. The default is False.

        """
        self.titleBarWidget().setPinned(pinned, quiet)

    def isFloating(self):
        """ Get whether the dock item is free floating.

        """
        container = self.parent()
        if container is not None:
            return container.isWindow()
        return self.isWindow()

    def titleEditable(self):
        """ Get whether the title is user editable.

        Returns
        -------
        result : bool
            True if the title is user editable, False otherwise.

        """
        return self.titleBarWidget().isEditable()

    def setTitleEditable(self, editable):
        """ Set whether or not the title is user editable.

        Parameters
        ----------
        editable : bool
            True if the title is user editable, False otherwise.

        """
        self.titleBarWidget().setEditable(editable)

    def titleBarForceHidden(self):
        """ Get whether or not the title bar is force hidden.

        Returns
        -------
        result : bool
            Whether or not the title bar is force hidden.

        """
        return self.titleBarWidget().isForceHidden()

    def setTitleBarForceHidden(self, hidden):
        """ Set the force hidden state of the title bar.

        Parameters
        ----------
        hidden : bool
            True if the title bar should be hidden, False otherwise.

        """
        self.titleBarWidget().setForceHidden(hidden)

    def closable(self):
        """ Get whether or not the dock item is closable.

        Returns
        -------
        result : bool
            True if the dock item is closable, False otherwise.

        """
        return self._closable

    def setClosable(self, closable):
        """ Set whether or not the dock item is closable.

        Parameters
        ----------
        closable : bool
            True if the dock item is closable, False otherwise.

        """
        if closable != self._closable:
            self._closable = closable
            bar = self.titleBarWidget()
            buttons = bar.buttons()
            if closable:
                buttons |= bar.CloseButton
            else:
                buttons &= ~bar.CloseButton
            bar.setButtons(buttons)
            # A concession to practicality: walk the ancestry and update
            # the tab close button if this item lives in a dock tab.
            container = self.parent()
            if container is not None:
                stacked = container.parent()
                if stacked is not None:
                    tabs = stacked.parent()
                    if isinstance(tabs, QDockTabWidget):
                        index = tabs.indexOf(container)
                        tabs.setCloseButtonVisible(index, closable)

    def titleBarWidget(self):
        """ Get the title bar widget for the dock item.

        If a custom title bar has not been assigned, a default title
        bar will be returned. To prevent showing a title bar, set the
        visibility on the returned title bar to False.

        Returns
        -------
        result : IDockItemTitleBar
            An implementation of IDockItemTitleBar. This will never be
            None.

        """
        layout = self.layout()
        bar = layout.titleBarWidget()
        if bar is None:
            bar = QDockTitleBar()
            self.setTitleBarWidget(bar)
        return bar

    def setTitleBarWidget(self, title_bar):
        """ Set the title bar widget for the dock item.

        Parameters
        ----------
        title_bar : IDockItemTitleBar or None
            A custom implementation of IDockItemTitleBar, or None. If
            None, then the default title bar will be restored.

        """
        layout = self.layout()
        old = layout.titleBarWidget()
        if old is not None:
            old.maximizeButtonClicked.disconnect(self.maximizeButtonClicked)
            old.restoreButtonClicked.disconnect(self.restoreButtonClicked)
            old.closeButtonClicked.disconnect(self.closeButtonClicked)
            old.linkButtonToggled.disconnect(self.linkButtonToggled)
            old.pinButtonToggled.disconnect(self.pinButtonToggled)
            old.titleEdited.disconnect(self.titleEdited)
            old.leftDoubleClicked.disconnect(self.titleBarLeftDoubleClicked)
            old.rightClicked.disconnect(self.titleBarRightClicked)
        title_bar = title_bar or QDockTitleBar()
        title_bar.maximizeButtonClicked.connect(self.maximizeButtonClicked)
        title_bar.restoreButtonClicked.connect(self.restoreButtonClicked)
        title_bar.closeButtonClicked.connect(self.closeButtonClicked)
        title_bar.linkButtonToggled.connect(self.linkButtonToggled)
        title_bar.pinButtonToggled.connect(self.pinButtonToggled)
        title_bar.titleEdited.connect(self.titleEdited)
        title_bar.leftDoubleClicked.connect(self.titleBarLeftDoubleClicked)
        title_bar.rightClicked.connect(self.titleBarRightClicked)
        layout.setTitleBarWidget(title_bar)

    def dockWidget(self):
        """ Get the dock widget for this dock item.

        Returns
        -------
        result : QWidget or None
            The dock widget being managed by this item.

        """
        return self.layout().dockWidget()

    def setDockWidget(self, widget):
        """ Set the dock widget for this dock item.

        Parameters
        ----------
        widget : QWidget
            The QWidget to use as the dock widget in this item.

        """
        self.layout().setDockWidget(widget)

    def alert(self, level, on=250, off=250, repeat=4, persist=False):
        """ Set the alert level on the dock item.

        This will override any currently applied alert level.

        Parameters
        ----------
        level : unicode
            The alert level token to apply to the dock item.

        on : int
            The duration of the 'on' cycle, in ms. A value of -1 means
            always on.

        off : int
            The duration of the 'off' cycle, in ms. If 'on' is -1, this
            value is ignored.

        repeat : int
            The number of times to repeat the on-off cycle. If 'on' is
            -1, this value is ignored.

        persist : bool
            Whether to leave the alert in the 'on' state when the cycles
            finish. If 'on' is -1, this value is ignored.

        """
        if self._alert_data is not None:
            self.clearAlert()
        app = QApplication.instance()
        app.focusChanged.connect(self._onAppFocusChanged)
        timer = QTimer()
        timer.setSingleShot(True)
        timer.timeout.connect(self._onAlertTimer)
        on, off, repeat = max(-1, on), max(0, off), max(1, repeat)
        self._alert_data = _AlertData(timer, level, on, off, repeat, persist)
        if on < 0:
            self.alerted.emit(level)
        else:
            self._onAlertTimer()

    def clearAlert(self):
        """ Clear the current alert level, if any.

        """
        if self._alert_data is not None:
            self._alert_data.timer.stop()
            self._alert_data = None
            app = QApplication.instance()
            app.focusChanged.disconnect(self._onAppFocusChanged)
            self.alerted.emit(u'')

    #--------------------------------------------------------------------------
    # Private API
    #--------------------------------------------------------------------------
    def _onAlertTimer(self):
        """ Handle the alert data timer timeout.

        This handler will refresh the alert level for the current tick,
        or clear|persist the alert level if the ticks have expired.

        """
        data = self._alert_data
        if data is not None:
            if not data.active:
                data.active = True
                data.timer.start(data.on)
                self.alerted.emit(data.level)
            else:
                data.active = False
                data.remaining -= 1
                if data.remaining > 0:
                    data.timer.start(data.off)
                    self.alerted.emit(u'')
                elif data.persist:
                    data.timer.stop()
                    self.alerted.emit(data.level)
                else:
                    self.clearAlert()

    def _onAlerted(self, level):
        """ A signal handler for the 'alerted' signal.

        This handler will set the 'alert' dynamic property on the
        dock item, the title bar, and the title bar label, and then
        repolish all three items.

        """
        level = level or None
        title_bar = self.titleBarWidget()
        label = title_bar.label()
        self.setProperty(u'alert', level)
        title_bar.setProperty(u'alert', level)
        label.setProperty(u'alert', level)
        repolish(label)
        repolish(title_bar)
        repolish(self)

    def _onAppFocusChanged(self, old, new):
        """ A signal handler for the 'focusChanged' app signal

        This handler will clear the alert if one of the descendant
        widgets or the item itself gains focus.

        """
        while new is not None:
            if new is self:
                self.clearAlert()
                break
            new = new.parent()

    def _onVisibilityTimer(self):
        """ Handle the visibility timer timeout.

        This handler will post the dock item visibility event to the
        root dock area.

        """
        area = self.rootDockArea()
        if area is not None and area.dockEventsEnabled():
            timer, visible = self._vis_changed
            evt_type = DockItemShown if visible else DockItemHidden
            event = QDockItemEvent(evt_type, self.objectName())
            QApplication.postEvent(area, event)
            self._vis_changed = None

    def _postVisibilityChange(self, visible):
        """ Post a visibility changed event for the dock item.

        This method collapses the post on a timer and will not emit
        the event when the visibility temporarily toggles bettwen
        states.

        Parameters
        ----------
        visible : bool
            True if the item was show, False if the item was hidden.

        """
        area = self.rootDockArea()
        if area is not None and area.dockEventsEnabled():
            vis_changed = self._vis_changed
            if vis_changed is None:
                timer = QTimer()
                timer.setSingleShot(True)
                timer.timeout.connect(self._onVisibilityTimer)
                self._vis_changed = (timer, visible)
                timer.start()
            else:
                timer, old_visible = vis_changed
                if old_visible != visible:
                    self._vis_changed = None
                    timer.stop()
Esempio n. 8
0
class QDockItem(QFrame):
    """ A QFrame subclass which acts as an item QDockArea.

    """
    #: A signal emitted when the maximize button is clicked. This
    #: signal is proxied from the current dock item title bar.
    maximizeButtonClicked = Signal(bool)

    #: A signal emitted when the restore button is clicked. This
    #: signal is proxied from the current dock item title bar.
    restoreButtonClicked = Signal(bool)

    #: A signal emitted when the close button is clicked. This
    #: signal is proxied from the current dock item title bar.
    closeButtonClicked = Signal(bool)

    #: A signal emitted when the link button is toggled. This
    #: signal is proxied from the current dock item title bar.
    linkButtonToggled = Signal(bool)

    #: A signal emitted when the pin button is toggled. This
    #: signal is proxied from the current dock item title bar.
    pinButtonToggled = Signal(bool)

    #: A signal emitted when the title is edited by the user. This
    #: signal is proxied from the current dock item title bar.
    titleEdited = Signal(unicode)

    #: A signal emitted when the empty area is left double clicked.
    #: This signal is proxied from the current dock item title bar.
    titleBarLeftDoubleClicked = Signal(QPoint)

    #: A signal emitted when the empty area is right clicked. This
    #: signal is proxied from the current dock item title bar.
    titleBarRightClicked = Signal(QPoint)

    def __init__(self, parent=None):
        """ Initialize a QDockItem.

        Parameters
        ----------
        parent : QWidget, optional
            The parent of the dock item.

        """
        super(QDockItem, self).__init__(parent)
        layout = QDockItemLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSizeConstraint(QLayout.SetMinAndMaxSize)
        self.setLayout(layout)
        self.setTitleBarWidget(QDockTitleBar())
        self._manager = None  # Set and cleared by the DockManager
        self._vis_changed = None
        self._closable = True
        self._closing = False

    #--------------------------------------------------------------------------
    # Reimplementations
    #--------------------------------------------------------------------------
    def close(self):
        """ Handle the close request for the dock item.

        """
        self._closing = True
        try:
            super(QDockItem, self).close()
        finally:
            self._closing = False

    def closeEvent(self, event):
        """ Handle the close event for the dock item.

        This handler will reject the event if the item is not closable.

        """
        event.ignore()
        if self._closable:
            event.accept()
            area = self.rootDockArea()
            if area is not None and area.dockEventsEnabled():
                event = QDockItemEvent(DockItemClosed, self.objectName())
                QApplication.postEvent(area, event)

    def showEvent(self, event):
        """ Handle the show event for the container.

        This handler posts a visibility change event.

        """
        super(QDockItem, self).showEvent(event)
        self._postVisibilityChange(True)

    def hideEvent(self, event):
        """ Handle the hide event for the container.

        This handler posts a visibility change event.

        """
        super(QDockItem, self).hideEvent(event)
        # Don't post when closing; A closed event is posted instead.
        if not self._closing:
            self._postVisibilityChange(False)

    #--------------------------------------------------------------------------
    # Public API
    #--------------------------------------------------------------------------
    def manager(self):
        """ Get the dock manager for this dock item.

        Returns
        -------
        result : DockManager or None
            The dock manager which is managing this item.

        """
        return self._manager

    def rootDockArea(self):
        """ Get the root dock area for this dock item.

        Returns
        -------
        result : QDockArea or None
            The root dock area for this dock item.

        """
        manager = self._manager
        if manager is not None:
            return manager.dock_area()

    def title(self):
        """ Get the title for the dock item.

        Returns
        -------
        result : unicode
            The unicode title for the dock item.

        """
        return self.titleBarWidget().title()

    def setTitle(self, title):
        """ Set the title for the dock item.

        Parameters
        ----------
        title : unicode
            The unicode title to use for the dock item.

        """
        self.titleBarWidget().setTitle(title)
        # A concession to practicality: walk the ancestry and update
        # the tab title if this item lives in a dock tab.
        container = self.parent()
        if container is not None:
            stacked = container.parent()
            if stacked is not None:
                tabs = stacked.parent()
                if isinstance(tabs, QDockTabWidget):
                    index = tabs.indexOf(container)
                    tabs.setTabText(index, title)

    def icon(self):
        """ Get the icon for the dock item.

        Returns
        -------
        result : QIcon
            The icon in use for the dock item.

        """
        return self.titleBarWidget().icon()

    def setIcon(self, icon):
        """ Set the icon for the dock item.

        Parameters
        ----------
        icon : QIcon
            The icon to use for the dock item.

        """
        self.titleBarWidget().setIcon(icon)
        # A concession to practicality: walk the ancestry and update
        # the tab icon if this item lives in a dock tab.
        container = self.parent()
        if container is not None:
            stacked = container.parent()
            if stacked is not None:
                tabs = stacked.parent()
                if isinstance(tabs, QDockTabWidget):
                    index = tabs.indexOf(container)
                    tabs.setTabIcon(index, icon)

    def iconSize(self):
        """ Get the icon size for the title bar.

        Returns
        -------
        result : QSize
            The size to use for the icons in the title bar.

        """
        return self.titleBarWidget().iconSize()

    def setIconSize(self, size):
        """ Set the icon size for the title bar.

        Parameters
        ----------
        icon : QSize
            The icon size to use for the title bar. Icons smaller than
            this size will not be scaled up.

        """
        self.titleBarWidget().setIconSize(size)

    def isLinked(self):
        """ Get whether or not this dock item is linked.

        Returns
        -------
        result : bool
            True if the item is linked, False otherwise.

        """
        return self.titleBarWidget().isLinked()

    def setLinked(self, linked):
        """ Set whether or not the dock item is linked.

        Parameters
        ----------
        linked : bool
            True if the dock item should be linked, False otherwise.

        """
        self.titleBarWidget().setLinked(linked)

    def isPinned(self):
        """ Get whether or not this dock item is pinned.

        Returns
        -------
        result : bool
            True if the item is pinned, False otherwise.

        """
        return self.titleBarWidget().isPinned()

    def setPinned(self, pinned, quiet=False):
        """ Set whether or not the dock item is pinned.

        Parameters
        ----------
        pinned : bool
            True if the dock item should be pinned, False otherwise.

        quiet : bool, optional
            True if the state should be set without emitted the toggled
            signal. The default is False.

        """
        self.titleBarWidget().setPinned(pinned, quiet)

    def titleEditable(self):
        """ Get whether the title is user editable.

        Returns
        -------
        result : bool
            True if the title is user editable, False otherwise.

        """
        return self.titleBarWidget().isEditable()

    def setTitleEditable(self, editable):
        """ Set whether or not the title is user editable.

        Parameters
        ----------
        editable : bool
            True if the title is user editable, False otherwise.

        """
        self.titleBarWidget().setEditable(editable)

    def titleBarForceHidden(self):
        """ Get whether or not the title bar is force hidden.

        Returns
        -------
        result : bool
            Whether or not the title bar is force hidden.

        """
        return self.titleBarWidget().isForceHidden()

    def setTitleBarForceHidden(self, hidden):
        """ Set the force hidden state of the title bar.

        Parameters
        ----------
        hidden : bool
            True if the title bar should be hidden, False otherwise.

        """
        self.titleBarWidget().setForceHidden(hidden)

    def closable(self):
        """ Get whether or not the dock item is closable.

        Returns
        -------
        result : bool
            True if the dock item is closable, False otherwise.

        """
        return self._closable

    def setClosable(self, closable):
        """ Set whether or not the dock item is closable.

        Parameters
        ----------
        closable : bool
            True if the dock item is closable, False otherwise.

        """
        if closable != self._closable:
            self._closable = closable
            bar = self.titleBarWidget()
            buttons = bar.buttons()
            if closable:
                buttons |= bar.CloseButton
            else:
                buttons &= ~bar.CloseButton
            bar.setButtons(buttons)
            # A concession to practicality: walk the ancestry and update
            # the tab close button if this item lives in a dock tab.
            container = self.parent()
            if container is not None:
                stacked = container.parent()
                if stacked is not None:
                    tabs = stacked.parent()
                    if isinstance(tabs, QDockTabWidget):
                        index = tabs.indexOf(container)
                        tabs.setCloseButtonVisible(index, closable)

    def titleBarWidget(self):
        """ Get the title bar widget for the dock item.

        If a custom title bar has not been assigned, a default title
        bar will be returned. To prevent showing a title bar, set the
        visibility on the returned title bar to False.

        Returns
        -------
        result : IDockItemTitleBar
            An implementation of IDockItemTitleBar. This will never be
            None.

        """
        layout = self.layout()
        bar = layout.titleBarWidget()
        if bar is None:
            bar = QDockTitleBar()
            self.setTitleBarWidget(bar)
        return bar

    def setTitleBarWidget(self, title_bar):
        """ Set the title bar widget for the dock item.

        Parameters
        ----------
        title_bar : IDockItemTitleBar or None
            A custom implementation of IDockItemTitleBar, or None. If
            None, then the default title bar will be restored.

        """
        layout = self.layout()
        old = layout.titleBarWidget()
        if old is not None:
            old.maximizeButtonClicked.disconnect(self.maximizeButtonClicked)
            old.restoreButtonClicked.disconnect(self.restoreButtonClicked)
            old.closeButtonClicked.disconnect(self.closeButtonClicked)
            old.linkButtonToggled.disconnect(self.linkButtonToggled)
            old.pinButtonToggled.disconnect(self.pinButtonToggled)
            old.titleEdited.disconnect(self.titleEdited)
            old.leftDoubleClicked.disconnect(self.titleBarLeftDoubleClicked)
            old.rightClicked.disconnect(self.titleBarRightClicked)
        title_bar = title_bar or QDockTitleBar()
        title_bar.maximizeButtonClicked.connect(self.maximizeButtonClicked)
        title_bar.restoreButtonClicked.connect(self.restoreButtonClicked)
        title_bar.closeButtonClicked.connect(self.closeButtonClicked)
        title_bar.linkButtonToggled.connect(self.linkButtonToggled)
        title_bar.pinButtonToggled.connect(self.pinButtonToggled)
        title_bar.titleEdited.connect(self.titleEdited)
        title_bar.leftDoubleClicked.connect(self.titleBarLeftDoubleClicked)
        title_bar.rightClicked.connect(self.titleBarRightClicked)
        layout.setTitleBarWidget(title_bar)

    def dockWidget(self):
        """ Get the dock widget for this dock item.

        Returns
        -------
        result : QWidget or None
            The dock widget being managed by this item.

        """
        return self.layout().dockWidget()

    def setDockWidget(self, widget):
        """ Set the dock widget for this dock item.

        Parameters
        ----------
        widget : QWidget
            The QWidget to use as the dock widget in this item.

        """
        self.layout().setDockWidget(widget)

    #--------------------------------------------------------------------------
    # Private API
    #--------------------------------------------------------------------------
    def _onVisibilityTimer(self):
        """ Handle the visibility timer timeout.

        This handler will post the dock item visibility event to the
        root dock area.

        """
        area = self.rootDockArea()
        if area is not None and area.dockEventsEnabled():
            timer, visible = self._vis_changed
            evt_type = DockItemShown if visible else DockItemHidden
            event = QDockItemEvent(evt_type, self.objectName())
            QApplication.postEvent(area, event)
            self._vis_changed = None

    def _postVisibilityChange(self, visible):
        """ Post a visibility changed event for the dock item.

        This method collapses the post on a timer and will not emit
        the event when the visibility temporarily toggles bettwen
        states.

        Parameters
        ----------
        visible : bool
            True if the item was show, False if the item was hidden.

        """
        area = self.rootDockArea()
        if area is not None and area.dockEventsEnabled():
            vis_changed = self._vis_changed
            if vis_changed is None:
                timer = QTimer()
                timer.setSingleShot(True)
                timer.timeout.connect(self._onVisibilityTimer)
                self._vis_changed = (timer, visible)
                timer.start()
            else:
                timer, old_visible = vis_changed
                if old_visible != visible:
                    self._vis_changed = None
                    timer.stop()