Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • Orange/OrangeCanvas/application/canvastooldock.py

    r11243 r11495  
    33 
    44""" 
     5import sys 
     6 
    57from PyQt4.QtGui import ( 
    68    QWidget, QSplitter, QVBoxLayout, QTextEdit, QAction, QPalette, 
    7     QSizePolicy 
     9    QSizePolicy, QApplication, QDrag 
    810) 
    911 
    10 from PyQt4.QtCore import Qt, QSize, QObject, QPropertyAnimation, QEvent 
    11 from PyQt4.QtCore import pyqtProperty as Property 
     12from PyQt4.QtCore import ( 
     13    Qt, QSize, QObject, QPropertyAnimation, QEvent, QRect, 
     14    QModelIndex, QPersistentModelIndex, QEventLoop, QMimeData 
     15) 
     16 
     17from PyQt4.QtCore import pyqtProperty as Property, pyqtSignal as Signal 
    1218 
    1319from ..gui.toolgrid import ToolGrid 
    1420from ..gui.toolbar import DynamicResizeToolBar 
    1521from ..gui.quickhelp import QuickHelp 
     22from ..gui.framelesswindow import FramelessWindow 
     23from ..document.quickmenu import MenuPage 
    1624from .widgettoolbox import WidgetToolBox, iter_item 
    1725 
    1826from ..registry.qt import QtWidgetRegistry 
     27from ..utils.qtcompat import toPyObject 
    1928 
    2029 
     
    337346                action = self._gridSlots[index].action 
    338347                self.removeAction(action) 
     348 
     349 
     350class CategoryPopupMenu(FramelessWindow): 
     351    triggered = Signal(QAction) 
     352    hovered = Signal(QAction) 
     353 
     354    def __init__(self, parent=None, **kwargs): 
     355        FramelessWindow.__init__(self, parent, **kwargs) 
     356        self.setWindowFlags(self.windowFlags() | Qt.Popup) 
     357 
     358        layout = QVBoxLayout() 
     359        layout.setContentsMargins(6, 6, 6, 6) 
     360 
     361        self.__menu = MenuPage() 
     362        self.__menu.setActionRole(QtWidgetRegistry.WIDGET_ACTION_ROLE) 
     363 
     364        if sys.platform == "darwin": 
     365            self.__menu.view().setAttribute(Qt.WA_MacShowFocusRect, False) 
     366 
     367        self.__menu.triggered.connect(self.__onTriggered) 
     368        self.__menu.hovered.connect(self.hovered) 
     369 
     370        self.__dragListener = ItemViewDragStartEventListener(self) 
     371        self.__dragListener.dragStarted.connect(self.__onDragStarted) 
     372 
     373        self.__menu.view().viewport().installEventFilter(self.__dragListener) 
     374 
     375        layout.addWidget(self.__menu) 
     376 
     377        self.setLayout(layout) 
     378 
     379        self.__action = None 
     380        self.__loop = None 
     381        self.__item = None 
     382 
     383    def setCategoryItem(self, item): 
     384        """ 
     385        Set the category root item (:class:`QStandardItem`). 
     386        """ 
     387        self.__item = item 
     388        model = item.model() 
     389        self.__menu.setModel(model) 
     390        self.__menu.setRootIndex(item.index()) 
     391 
     392    def popup(self, pos=None): 
     393        if pos is None: 
     394            pos = self.pos() 
     395        geom = widget_popup_geometry(pos, self) 
     396        self.setGeometry(geom) 
     397        self.show() 
     398 
     399    def exec_(self, pos=None): 
     400        self.popup(pos) 
     401        self.__loop = QEventLoop() 
     402 
     403        self.__action = None 
     404        self.__loop.exec_() 
     405        self.__loop = None 
     406 
     407        if self.__action is not None: 
     408            action = self.__action 
     409        else: 
     410            action = None 
     411        return action 
     412 
     413    def hideEvent(self, event): 
     414        if self.__loop is not None: 
     415            self.__loop.exit(0) 
     416 
     417        return FramelessWindow.hideEvent(self, event) 
     418 
     419    def __onTriggered(self, action): 
     420        self.__action = action 
     421        self.triggered.emit(action) 
     422        self.hide() 
     423 
     424        if self.__loop: 
     425            self.__loop.exit(0) 
     426 
     427    def __onDragStarted(self, index): 
     428        desc = toPyObject(index.data(QtWidgetRegistry.WIDGET_DESC_ROLE)) 
     429        icon = toPyObject(index.data(Qt.DecorationRole)) 
     430 
     431        drag_data = QMimeData() 
     432        drag_data.setData( 
     433            "application/vnv.orange-canvas.registry.qualified-name", 
     434            desc.qualified_name 
     435        ) 
     436        drag = QDrag(self) 
     437        drag.setPixmap(icon.pixmap(38)) 
     438        drag.setMimeData(drag_data) 
     439 
     440        # TODO: Should animate (accept) hide. 
     441        self.hide() 
     442 
     443        # When a drag is started and the menu hidden the item's tool tip 
     444        # can still show for a short time UNDER the cursor preventing a 
     445        # drop. 
     446        viewport = self.__menu.view().viewport() 
     447        filter = ToolTipEventFilter() 
     448        viewport.installEventFilter(filter) 
     449 
     450        drag.exec_(Qt.CopyAction) 
     451 
     452        viewport.removeEventFilter(filter) 
     453 
     454 
     455class ItemViewDragStartEventListener(QObject): 
     456    dragStarted = Signal(QModelIndex) 
     457 
     458    def __init__(self, parent=None): 
     459        QObject.__init__(self, parent) 
     460        self._pos = None 
     461        self._index = None 
     462 
     463    def eventFilter(self, viewport, event): 
     464        view = viewport.parent() 
     465 
     466        if event.type() == QEvent.MouseButtonPress and \ 
     467                event.button() == Qt.LeftButton: 
     468 
     469            index = view.indexAt(event.pos()) 
     470 
     471            if index is not None: 
     472                self._pos = event.pos() 
     473                self._index = QPersistentModelIndex(index) 
     474 
     475        elif event.type() == QEvent.MouseMove and self._pos is not None and \ 
     476                ((self._pos - event.pos()).manhattanLength() >= 
     477                 QApplication.startDragDistance()): 
     478 
     479            if self._index.isValid(): 
     480                # Map to a QModelIndex in the model. 
     481                index = self._index 
     482                index = index.model().index(index.row(), index.column(), 
     483                                            index.parent()) 
     484                self._pos = None 
     485                self._index = None 
     486 
     487                self.dragStarted.emit(index) 
     488 
     489        return QObject.eventFilter(self, view, event) 
     490 
     491 
     492class ToolTipEventFilter(QObject): 
     493    def eventFilter(self, receiver, event): 
     494        if event.type() == QEvent.ToolTip: 
     495            return True 
     496 
     497        return QObject.eventFilter(self, receiver, event) 
     498 
     499 
     500def widget_popup_geometry(pos, widget): 
     501    widget.ensurePolished() 
     502 
     503    if widget.testAttribute(Qt.WA_Resized): 
     504        size = widget.size() 
     505    else: 
     506        size = widget.sizeHint() 
     507 
     508    desktop = QApplication.desktop() 
     509    screen_geom = desktop.availableGeometry(pos) 
     510 
     511    # Adjust the size to fit inside the screen. 
     512    if size.height() > screen_geom.height(): 
     513        size.setHeight(screen_geom.height()) 
     514    if size.width() > screen_geom.width(): 
     515        size.setWidth(screen_geom.width()) 
     516 
     517    geom = QRect(pos, size) 
     518 
     519    if geom.top() < screen_geom.top(): 
     520        geom.setTop(screen_geom.top()) 
     521 
     522    if geom.left() < screen_geom.left(): 
     523        geom.setLeft(screen_geom.left()) 
     524 
     525    bottom_margin = screen_geom.bottom() - geom.bottom() 
     526    right_margin = screen_geom.right() - geom.right() 
     527    if bottom_margin < 0: 
     528        # Falls over the bottom of the screen, move it up. 
     529        geom.translate(0, bottom_margin) 
     530 
     531    # TODO: right to left locale 
     532    if right_margin < 0: 
     533        # Falls over the right screen edge, move the menu to the 
     534        # other side of pos. 
     535        geom.translate(-size.width(), 0) 
     536 
     537    return geom 
Note: See TracChangeset for help on using the changeset viewer.