Example #1
0
    def updateEditorGeometry(self, editor, option, index):
        if editor is None:
            return
        fm = editor.fontMetrics()

        # get the original size of the edit widget
        opt = QStyleOptionViewItem(option)
        self.initStyleOption(opt, index)
        opt.showDecorationSelected = True
        opt.decorationSize = QSize(
            0, 0)  # We want the editor to cover the decoration
        style = QApplication.style()
        initial_geometry = style.subElementRect(
            QStyle.SubElement.SE_ItemViewItemText, opt, None)
        orig_width = initial_geometry.width()

        # Compute the required width: the width that can show all of the current value
        if hasattr(self, 'get_required_width'):
            new_width = self.get_required_width(editor, style, fm)
        else:
            # The line edit box seems to extend by the space consumed by an 'M'.
            # So add that to the text
            text = self.displayText(index.data(Qt.ItemDataRole.DisplayRole),
                                    QLocale()) + 'M'
            srect = style.itemTextRect(fm, editor.geometry(),
                                       Qt.AlignmentFlag.AlignLeft, False, text)
            new_width = srect.width()

        # Now get the size of the combo/spinner arrows and add them to the needed width
        if isinstance(editor, (QComboBox, QDateTimeEdit)):
            r = style.subControlRect(QStyle.ComplexControl.CC_ComboBox,
                                     QStyleOptionComboBox(),
                                     QStyle.SubControl.SC_ComboBoxArrow,
                                     editor)
            new_width += r.width()
        elif isinstance(editor, (QSpinBox, QDoubleSpinBox)):
            r = style.subControlRect(QStyle.ComplexControl.CC_SpinBox,
                                     QStyleOptionSpinBox(),
                                     QStyle.SubControl.SC_SpinBoxUp, editor)
            new_width += r.width()

        # Compute the maximum we can show if we consume the entire viewport
        pin_view = self.table_widget.pin_view
        is_pin_view, p = False, editor.parent()
        while p is not None:
            if p is pin_view:
                is_pin_view = True
                break
            p = p.parent()
        if is_pin_view:
            max_width = pin_view.horizontalScrollBar().geometry().width()
        else:
            view = self.table_widget
            max_width = view.horizontalScrollBar().geometry().width(
            ) - view.verticalHeader().width()
        # What we have to display might not fit. If so, adjust down
        new_width = new_width if new_width < max_width else max_width

        # See if we need to change the editor's geometry
        if new_width <= orig_width:
            delta_x = 0
            delta_width = 0
        else:
            # Compute the space available from the left edge of the widget to
            # the right edge of the displayed table (the viewport) and the left
            # edge of the widget to the left edge of the viewport. These are
            # used to position the edit box
            space_left = initial_geometry.x()
            space_right = max_width - space_left

            if editor.layoutDirection() == Qt.LayoutDirection.RightToLeft:
                # If language is RtL, align to the cell's right edge if possible
                cw = initial_geometry.width()
                consume_on_left = min(space_left, new_width - cw)
                consume_on_right = max(0, new_width - (consume_on_left + cw))
                delta_x = -consume_on_left
                delta_width = consume_on_right
            else:
                # If language is LtR, align to the left if possible
                consume_on_right = min(space_right, new_width)
                consume_on_left = max(0, new_width - consume_on_right)
                delta_x = -consume_on_left
                delta_width = consume_on_right - initial_geometry.width()

        initial_geometry.adjust(delta_x, 0, delta_width, 0)
        editor.setGeometry(initial_geometry)
Example #2
0
    def eventFilter(self, obj, e):
        'Redirect key presses from the popup to the widget'
        widget = self.parent()
        if widget is None or sip.isdeleted(widget):
            return False
        etype = e.type()
        if obj is not self:
            return QObject.eventFilter(self, obj, e)

        # self.debug_event(e)

        if etype == QEvent.Type.KeyPress:
            try:
                key = e.key()
            except AttributeError:
                return QObject.eventFilter(self, obj, e)
            if key == Qt.Key.Key_Escape:
                self.hide()
                e.accept()
                return True
            if key == Qt.Key.Key_F4 and e.modifiers(
            ) & Qt.KeyboardModifier.AltModifier:
                self.hide()
                e.accept()
                return True
            if key in (Qt.Key.Key_Enter, Qt.Key.Key_Return):
                # We handle this explicitly because on OS X activated() is
                # not emitted on pressing Enter.
                idx = self.currentIndex()
                if idx.isValid():
                    self.item_chosen(idx)
                self.hide()
                e.accept()
                return True
            if key == Qt.Key.Key_Tab:
                idx = self.currentIndex()
                if idx.isValid():
                    self.item_chosen(idx)
                    self.hide()
                elif self.model().rowCount() > 0:
                    self.next_match()
                e.accept()
                return True
            if key in (Qt.Key.Key_PageUp, Qt.Key.Key_PageDown):
                # Let the list view handle these keys
                return False
            if key in (Qt.Key.Key_Up, Qt.Key.Key_Down):
                self.next_match(previous=key == Qt.Key.Key_Up)
                e.accept()
                return True
            # Send to widget
            widget.eat_focus_out = False
            widget.keyPressEvent(e)
            widget.eat_focus_out = True
            if not widget.hasFocus():
                # Widget lost focus hide the popup
                self.hide()
            if e.isAccepted():
                return True
        elif ismacos and etype == QEvent.Type.InputMethodQuery and e.queries(
        ) == (Qt.InputMethodQuery.ImHints
              | Qt.InputMethodQuery.ImEnabled) and self.isVisible():
            # In Qt 5 the Esc key causes this event and the line edit does not
            # handle it, which causes the parent dialog to be closed
            # See https://bugreports.qt-project.org/browse/QTBUG-41806
            e.accept()
            return True
        elif etype == QEvent.Type.MouseButtonPress and hasattr(
                e, 'globalPos') and not self.rect().contains(
                    self.mapFromGlobal(e.globalPos())):
            # A click outside the popup, close it
            if isinstance(widget, QComboBox):
                # This workaround is needed to ensure clicking on the drop down
                # arrow of the combobox closes the popup
                opt = QStyleOptionComboBox()
                widget.initStyleOption(opt)
                sc = widget.style().hitTestComplexControl(
                    QStyle.ComplexControl.CC_ComboBox, opt,
                    widget.mapFromGlobal(e.globalPos()), widget)
                if sc == QStyle.SubControl.SC_ComboBoxArrow:
                    QTimer.singleShot(0, self.hide)
                    e.accept()
                    return True
            self.hide()
            e.accept()
            return True
        elif etype in (QEvent.Type.InputMethod, QEvent.Type.ShortcutOverride):
            QApplication.sendEvent(widget, e)
        return False