--- a/eric7/E5Gui/EricTabWidget.py Sat May 22 19:52:34 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,358 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright (c) 2005 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> -# - -""" -Module implementing a TabWidget class substituting QTabWidget. -""" - -import contextlib - -from PyQt6.QtCore import pyqtSignal, Qt, QPoint, QMimeData -from PyQt6.QtGui import QDrag -from PyQt6.QtWidgets import QTabWidget, QTabBar, QApplication, QStyle - -from E5Gui.EricAnimatedLabel import EricAnimatedLabel - - -class EricWheelTabBar(QTabBar): - """ - Class implementing a tab bar class substituting QTabBar to support wheel - events. - """ - def __init__(self, parent=None): - """ - Constructor - - @param parent reference to the parent widget (QWidget) - """ - super().__init__(parent) - self._tabWidget = parent - - def wheelEvent(self, event): - """ - Protected slot to support wheel events. - - @param event reference to the wheel event (QWheelEvent) - """ - with contextlib.suppress(AttributeError): - delta = event.angleDelta().y() - if delta > 0: - self._tabWidget.prevTab() - elif delta < 0: - self._tabWidget.nextTab() - - event.accept() - - -class EricDnDTabBar(EricWheelTabBar): - """ - Class implementing a tab bar class substituting QTabBar. - - @signal tabMoveRequested(int, int) emitted to signal a tab move request - giving the old and new index position - """ - tabMoveRequested = pyqtSignal(int, int) - - def __init__(self, parent=None): - """ - Constructor - - @param parent reference to the parent widget (QWidget) - """ - EricWheelTabBar.__init__(self, parent) - self.setAcceptDrops(True) - - self.__dragStartPos = QPoint() - - def mousePressEvent(self, event): - """ - Protected method to handle mouse press events. - - @param event reference to the mouse press event (QMouseEvent) - """ - if event.button() == Qt.MouseButton.LeftButton: - self.__dragStartPos = QPoint(event.position().toPoint()) - EricWheelTabBar.mousePressEvent(self, event) - - def mouseMoveEvent(self, event): - """ - Protected method to handle mouse move events. - - @param event reference to the mouse move event (QMouseEvent) - """ - if ( - event.buttons() == Qt.MouseButton.LeftButton and - (event.position().toPoint() - self.__dragStartPos) - .manhattanLength() > QApplication.startDragDistance() - ): - drag = QDrag(self) - mimeData = QMimeData() - index = self.tabAt(event.position().toPoint()) - mimeData.setText(self.tabText(index)) - mimeData.setData("action", b"tab-reordering") - mimeData.setData("tabbar-id", str(id(self)).encode("utf-8")) - drag.setMimeData(mimeData) - drag.exec() - EricWheelTabBar.mouseMoveEvent(self, event) - - def dragEnterEvent(self, event): - """ - Protected method to handle drag enter events. - - @param event reference to the drag enter event (QDragEnterEvent) - """ - mimeData = event.mimeData() - formats = mimeData.formats() - if ( - "action" in formats and - mimeData.data("action") == b"tab-reordering" and - "tabbar-id" in formats and - int(mimeData.data("tabbar-id")) == id(self) - ): - event.acceptProposedAction() - EricWheelTabBar.dragEnterEvent(self, event) - - def dropEvent(self, event): - """ - Protected method to handle drop events. - - @param event reference to the drop event (QDropEvent) - """ - fromIndex = self.tabAt(self.__dragStartPos) - toIndex = self.tabAt(event.position().toPoint()) - if fromIndex != toIndex: - self.tabMoveRequested.emit(fromIndex, toIndex) - event.acceptProposedAction() - EricWheelTabBar.dropEvent(self, event) - - -class EricTabWidget(QTabWidget): - """ - Class implementing a tab widget class substituting QTabWidget. - - It provides slots to show the previous and next tab and give - them the input focus and it allows to have a context menu for the tabs. - - @signal customTabContextMenuRequested(const QPoint & point, int index) - emitted when a context menu for a tab is requested - """ - customTabContextMenuRequested = pyqtSignal(QPoint, int) - - def __init__(self, parent=None, dnd=False): - """ - Constructor - - @param parent reference to the parent widget (QWidget) - @param dnd flag indicating the support for Drag & Drop (boolean) - """ - super().__init__(parent) - - if dnd: - if not hasattr(self, 'setMovable'): - self.__tabBar = EricDnDTabBar(self) - self.__tabBar.tabMoveRequested.connect(self.moveTab) - self.setTabBar(self.__tabBar) - else: - self.__tabBar = EricWheelTabBar(self) - self.setTabBar(self.__tabBar) - self.setMovable(True) - else: - self.__tabBar = EricWheelTabBar(self) - self.setTabBar(self.__tabBar) - - self.__lastCurrentIndex = -1 - self.__currentIndex = -1 - self.currentChanged.connect(self.__currentChanged) - - def setCustomTabBar(self, dnd, tabBar): - """ - Public method to set a custom tab bar. - - @param dnd flag indicating the support for Drag & Drop (boolean) - @param tabBar reference to the tab bar to set (QTabBar) - """ - self.__tabBar = tabBar - self.setTabBar(self.__tabBar) - if dnd: - if isinstance(tabBar, EricDnDTabBar): - self.__tabBar.tabMoveRequested.connect(self.moveTab) - else: - self.setMovable(True) - - def __currentChanged(self, index): - """ - Private slot to handle the currentChanged signal. - - @param index index of the current tab - """ - if index == -1: - self.__lastCurrentIndex = -1 - else: - self.__lastCurrentIndex = self.__currentIndex - self.__currentIndex = index - - def switchTab(self): - """ - Public slot used to switch between the current and the previous - current tab. - """ - if self.__lastCurrentIndex == -1 or self.__currentIndex == -1: - return - - self.setCurrentIndex(self.__lastCurrentIndex) - self.currentWidget().setFocus() - - def nextTab(self): - """ - Public slot used to show the next tab. - """ - ind = self.currentIndex() + 1 - if ind == self.count(): - ind = 0 - - self.setCurrentIndex(ind) - self.currentWidget().setFocus() - - def prevTab(self): - """ - Public slot used to show the previous tab. - """ - ind = self.currentIndex() - 1 - if ind == -1: - ind = self.count() - 1 - - self.setCurrentIndex(ind) - self.currentWidget().setFocus() - - def setTabContextMenuPolicy(self, policy): - """ - Public method to set the context menu policy of the tab. - - @param policy context menu policy to set (Qt.ContextMenuPolicy) - """ - self.tabBar().setContextMenuPolicy(policy) - if policy == Qt.ContextMenuPolicy.CustomContextMenu: - self.tabBar().customContextMenuRequested.connect( - self.__handleTabCustomContextMenuRequested) - else: - self.tabBar().customContextMenuRequested.disconnect( - self.__handleTabCustomContextMenuRequested) - - def __handleTabCustomContextMenuRequested(self, point): - """ - Private slot to handle the context menu request for the tabbar. - - @param point point the context menu was requested (QPoint) - """ - _tabbar = self.tabBar() - for index in range(_tabbar.count()): - rect = _tabbar.tabRect(index) - if rect.contains(point): - self.customTabContextMenuRequested.emit( - _tabbar.mapToParent(point), index) - return - - self.customTabContextMenuRequested.emit(_tabbar.mapToParent(point), -1) - - def selectTab(self, pos): - """ - Public method to get the index of a tab given a position. - - @param pos position determining the tab index (QPoint) - @return index of the tab (integer) - """ - _tabbar = self.tabBar() - for index in range(_tabbar.count()): - rect = _tabbar.tabRect(index) - if rect.contains(pos): - return index - - return -1 - - def moveTab(self, curIndex, newIndex): - """ - Public method to move a tab to a new index. - - @param curIndex index of tab to be moved (integer) - @param newIndex index the tab should be moved to (integer) - """ - # step 1: save the tab data of tab to be moved - toolTip = self.tabToolTip(curIndex) - text = self.tabText(curIndex) - icon = self.tabIcon(curIndex) - whatsThis = self.tabWhatsThis(curIndex) - widget = self.widget(curIndex) - curWidget = self.currentWidget() - - # step 2: move the tab - self.removeTab(curIndex) - self.insertTab(newIndex, widget, icon, text) - - # step 3: set the tab data again - self.setTabToolTip(newIndex, toolTip) - self.setTabWhatsThis(newIndex, whatsThis) - - # step 4: set current widget - self.setCurrentWidget(curWidget) - - def __freeSide(self): - """ - Private method to determine the free side of a tab. - - @return free side (QTabBar.ButtonPosition) - """ - side = self.__tabBar.style().styleHint( - QStyle.StyleHint.SH_TabBar_CloseButtonPosition, - None, None, None) - side = ( - QTabBar.ButtonPosition.RightSide - if side == QTabBar.ButtonPosition.LeftSide else - QTabBar.ButtonPosition.LeftSide - ) - return side - - def animationLabel(self, index, animationFile, interval=100): - """ - Public slot to set an animated icon. - - @param index tab index - @type int - @param animationFile name of the file containing the animation - @type str - @param interval interval in milliseconds between animation frames - @type int - @return reference to the created label - @rtype EricAnimatedLabel - """ - if index == -1: - return None - - if hasattr(self.__tabBar, 'setTabButton'): - side = self.__freeSide() - animation = EricAnimatedLabel( - self, animationFile=animationFile, interval=interval) - self.__tabBar.setTabButton(index, side, None) - self.__tabBar.setTabButton(index, side, animation) - animation.start() - return animation - else: - return None - - def resetAnimation(self, index): - """ - Public slot to reset an animated icon. - - @param index tab index (integer) - """ - if index == -1: - return - - if hasattr(self.__tabBar, 'tabButton'): - side = self.__freeSide() - animation = self.__tabBar.tabButton(index, side) - if animation is not None: - animation.stop() - self.__tabBar.setTabButton(index, side, None) - del animation