[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [kdevplatform] shell: Fix up assistant popup and port to QtQuick 2
From:       Olivier JG <olivier.jg () gmail ! com>
Date:       2015-10-09 8:45:51
Message-ID: E1ZkTJH-0006Za-Ur () scm ! kde ! org
[Download RAW message or body]

Git commit 0f641b67c3fb7ca0e197e7c646be58ddfc3c4877 by Olivier JG.
Committed on 09/10/2015 at 08:47.
Pushed by olivierjg into branch 'master'.

Fix up assistant popup and port to QtQuick 2

M  +1    -1    shell/AssistantButton.qml
M  +2    -1    shell/CMakeLists.txt
M  +37   -71   shell/assistantpopup.cpp
M  +9    -12   shell/assistantpopup.h
M  +32   -28   shell/assistantpopup.qml

http://commits.kde.org/kdevplatform/0f641b67c3fb7ca0e197e7c646be58ddfc3c4877

diff --git a/shell/AssistantButton.qml b/shell/AssistantButton.qml
index 586c1aa..9500a9e 100644
--- a/shell/AssistantButton.qml
+++ b/shell/AssistantButton.qml
@@ -17,7 +17,7 @@
    Boston, MA 02110-1301, USA.
 */
 
-import QtQuick 1.0
+import QtQuick 2.2
 
 // Component which provides a single button for the assistant widget.
 
diff --git a/shell/CMakeLists.txt b/shell/CMakeLists.txt
index d9257da..2ac7691 100644
--- a/shell/CMakeLists.txt
+++ b/shell/CMakeLists.txt
@@ -116,7 +116,8 @@ LINK_PUBLIC
     KDev::Debugger
     KDev::Interfaces
 LINK_PRIVATE
-    Qt5::Declarative
+    Qt5::Quick
+    Qt5::QuickWidgets
 
     KF5::GuiAddons
     KF5::IconThemes
diff --git a/shell/assistantpopup.cpp b/shell/assistantpopup.cpp
index 4349e95..8aea1e6 100644
--- a/shell/assistantpopup.cpp
+++ b/shell/assistantpopup.cpp
@@ -24,12 +24,14 @@
 #include "util/kdevstringhandler.h"
 
 #include <QAction>
-#include <QDeclarativeContext>
+#include <QApplication>
 #include <QKeyEvent>
 #include <QDebug>
 #include <QEvent>
 #include <QTimer>
 #include <QStandardPaths>
+#include <QQmlContext>
+#include <QQuickItem>
 
 #include <KLocalizedString>
 #include <KParts/MainWindow>
@@ -61,7 +63,7 @@ const int ASSISTANT_MOD_KEY =
 #ifdef Q_OS_MAC
 Qt::Key_Control;
 #else
-Qt::Key_Meta;
+Qt::Key_Alt;
 #endif
 
 QWidget* findByClassname(const KTextEditor::View* view, const QString& klass)
@@ -101,7 +103,6 @@ QRect textWidgetGeometry(const KTextEditor::View *view)
 AssistantPopupConfig::AssistantPopupConfig(QObject *parent)
     : QObject(parent)
     , m_active(false)
-    , m_useVerticalLayout(false)
 {
 }
 
@@ -140,14 +141,12 @@ void AssistantPopupConfig::setActive(bool active)
     emit activeChanged(m_active);
 }
 
-void AssistantPopupConfig::setUseVerticalLayout(bool vertical)
+void AssistantPopupConfig::setViewSize(const QSize& size)
 {
-    if (m_useVerticalLayout == vertical) {
-        return;
+    if (size != m_viewSize) {
+        m_viewSize = size;
+        emit viewSizeChanged(size);
     }
-
-    m_useVerticalLayout = vertical;
-    emit useVerticalLayoutChanged(m_useVerticalLayout);
 }
 
 void AssistantPopupConfig::setTitle(const QString& title)
@@ -173,18 +172,10 @@ void AssistantPopupConfig::setModel(const QList<QObject*>& \
model)  
 AssistantPopup::AssistantPopup()
 // main window as parent to use maximal space available in worst case
-    : QDeclarativeView(ICore::self()->uiController()->activeMainWindow())
+    : QQuickWidget(ICore::self()->uiController()->activeMainWindow())
     , m_config(new AssistantPopupConfig(this))
-    , m_shownAtBottom(false)
-    , m_reopening(false)
-    , m_updateTimer(new QTimer(this))
+    , m_firstLayoutCompleted(false)
 {
-    QPalette p = palette();
-    p.setColor(QPalette::Window, Qt::transparent);
-    setPalette(p);
-    setBackgroundRole(QPalette::Window);
-    setBackgroundBrush(QBrush(QColor(0, 0, 0, 0)));
-    setResizeMode(QDeclarativeView::SizeViewToRootObject);
     setAttribute(Qt::WA_ShowWithoutActivating);
 
     rootContext()->setContextProperty("config", m_config);
@@ -192,25 +183,24 @@ AssistantPopup::AssistantPopup()
     setSource(QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, \
"kdevelop/assistantpopup.qml")));  if (!rootObject()) {
         qWarning() << "Failed to load assistant markup! The assistant will not \
work."; +    } else {
+        connect(rootObject(), &QQuickItem::widthChanged, this, \
&AssistantPopup::updateLayout); +        connect(rootObject(), \
&QQuickItem::heightChanged, this, &AssistantPopup::updateLayout);  }
 
-    m_updateTimer->setInterval(UPDATE_STATE_INTERVAL);
-    m_updateTimer->setSingleShot(true);
-    connect(m_updateTimer, &QTimer::timeout, this, &AssistantPopup::updateState);
-
     for (int i = Qt::Key_0; i <= Qt::Key_9; ++i) {
         m_shortcuts.append(new QShortcut(ASSISTANT_MODIFIER + i, this));
     }
     setActive(false);
+
+    connect(qApp, &QApplication::applicationStateChanged, this, [this]{ \
setActive(false); });  }
 
 void AssistantPopup::reset(KTextEditor::View* view, const IAssistant::Ptr& \
assistant)  {
     setView(view);
     setAssistant(assistant);
-    updateLayoutType();
-
-    m_updateTimer->start();
+    updateState();
 }
 
 void AssistantPopup::setView(KTextEditor::View* view)
@@ -227,6 +217,7 @@ void AssistantPopup::setView(KTextEditor::View* view)
                   this, &AssistantPopup::updatePosition);
     }
     m_view = view;
+    m_config->setViewSize(m_view ? m_view->size() : QSize());
     if (m_view) {
         m_view->installEventFilter(this);
         connect(m_view.data(), &KTextEditor::View::verticalScrollPositionChanged,
@@ -241,13 +232,13 @@ void AssistantPopup::setAssistant(const IAssistant::Ptr& \
assistant)  }
 
     if (m_assistant) {
-        disconnect(m_assistant.data(), &IAssistant::actionsChanged, m_updateTimer, \
                static_cast<void(QTimer::*)()>(&QTimer::start));
         disconnect(m_assistant.data(), &IAssistant::hide, this, \
&AssistantPopup::hideAssistant);  }
     m_assistant = assistant;
     if (m_assistant) {
-        connect(m_assistant.data(), &IAssistant::actionsChanged, m_updateTimer, \
                static_cast<void(QTimer::*)()>(&QTimer::start));
         connect(m_assistant.data(), &IAssistant::hide, this, \
&AssistantPopup::hideAssistant); +    } else {
+        hide();
     }
 }
 
@@ -259,18 +250,6 @@ void AssistantPopup::setActive(bool active)
     }
 }
 
-bool AssistantPopup::viewportEvent(QEvent *event)
-{
-    // For some reason, QGraphicsView posts a WindowActivate event
-    // when it is shown, even if disabled through setting the WA_ShowWithoutActivate
-    // attribute. This causes all focus-driven popups (QuickOpen, tooltips, ...)
-    // to hide when the assistant opens. Thus, prevent it from processing the Show \
                event here.
-    if ( event->type() == QEvent::Show ) {
-        return true;
-    }
-    return QGraphicsView::viewportEvent(event);
-}
-
 bool AssistantPopup::eventFilter(QObject* object, QEvent* event)
 {
     Q_UNUSED(object);
@@ -279,8 +258,7 @@ bool AssistantPopup::eventFilter(QObject* object, QEvent* event)
         return false;
 
     if (event->type() == QEvent::Resize) {
-        updateLayoutType();
-        updatePosition(m_view.data(), KTextEditor::Cursor::invalid());
+        updateLayout();
     } else if (event->type() == QEvent::Hide) {
         executeHideAction();
     } else if (event->type() == QEvent::KeyPress) {
@@ -304,7 +282,7 @@ void AssistantPopup::updatePosition(KTextEditor::View* view, \
const KTextEditor::  {
     static const int MARGIN = 12;
 
-    if (newPos.isValid() && newPos.line() == 0 && !m_shownAtBottom) {
+    if (newPos.isValid() && newPos.line() == 0) {
         // the position is not going to change; don't waste time
         return;
     }
@@ -328,15 +306,8 @@ void AssistantPopup::updatePosition(KTextEditor::View* view, \
const KTextEditor::  return;
     }
 
-    if ( m_reopening ) {
-        // When the assistant is already visible, close to no flickering will occur \
                anyways,
-        // so we can avoid the full repaint of the window.
-        move(targetLocation);
-    }
-    else {
-        Sublime::HoldUpdates \
                hold(ICore::self()->uiController()->activeMainWindow());
-        move(targetLocation);
-    }
+    Sublime::HoldUpdates hold(ICore::self()->uiController()->activeMainWindow());
+    move(targetLocation);
 }
 
 IAssistant::Ptr AssistantPopup::assistant() const
@@ -356,23 +327,23 @@ void AssistantPopup::hideAssistant()
     reset(nullptr, {}); // indirectly calls hide()
 }
 
-void AssistantPopup::updateLayoutType()
+void AssistantPopup::updateLayout()
 {
-    if ( !m_assistant || !m_view ) {
+    if ( !m_view ) {
         return;
     }
-    // Make a rough estimate of the width the assistant will need
-    // and decide on whether to use vertical layout or not.
-    const auto& metrics = fontMetrics();
-    auto textWidth = 0;
-
-    textWidth += metrics.boundingRect(KDevelop::htmlToPlainText(assistant()->title())).width();
                
-    for ( const auto& action: assistant()->actions() ) {
-        textWidth += \
                metrics.boundingRect(KDevelop::htmlToPlainText(action->description())).width();
                
-        textWidth += 10;
+
+    m_config->setViewSize(m_view->size());
+    // https://bugreports.qt.io/browse/QTBUG-44876
+    resize(rootObject()->width(), rootObject()->height());
+    updatePosition(m_view, KTextEditor::Cursor::invalid());
+
+    // HACK: QQuickWidget is corrupted due to above resize on the first show
+    if (!m_firstLayoutCompleted) {
+        hide();
+        show();
+        m_firstLayoutCompleted = true;
     }
-    m_config->setUseVerticalLayout(textWidth > \
                textWidgetGeometry(m_view).width()*0.75);
-    updateState();
 }
 
 void AssistantPopup::updateState()
@@ -395,8 +366,8 @@ void AssistantPopup::updateState()
         //For some reason, QAction's setShortcut does nothing, so we manage with \
QShortcut  if (++curShortcut != m_shortcuts.constEnd()) {
             connect(*curShortcut, &QShortcut::activated, asQAction, \
                &QAction::trigger);
-            connect(*curShortcut, &QShortcut::activated, hideAction, \
&QAction::trigger);  }
+        connect(action.data(), SIGNAL(executed(IAssistantAction*)), hideAction, \
SLOT(trigger()));  }
     items << hideAction;
 
@@ -406,11 +377,6 @@ void AssistantPopup::updateState()
     m_config->setTitle(m_assistant->title());
     setActive(false);
 
-    // both changed title or actions may change the appearance of the popup
-    // force recomputing the size hint
-    resize(sizeHint());
-    updatePosition(m_view, KTextEditor::Cursor::invalid());
-
     show();
 }
 
diff --git a/shell/assistantpopup.h b/shell/assistantpopup.h
index bdf3317..44cd167 100644
--- a/shell/assistantpopup.h
+++ b/shell/assistantpopup.h
@@ -22,7 +22,7 @@
 #ifndef KDEVPLATFORM_ASSISTANTPOPUP_H
 #define KDEVPLATFORM_ASSISTANTPOPUP_H
 
-#include <QDeclarativeView>
+#include <QQuickWidget>
 #include <QShortcut>
 #include <interfaces/iassistant.h>
 
@@ -42,7 +42,7 @@ class AssistantPopupConfig : public QObject
     Q_PROPERTY(QString title READ title NOTIFY titleChanged)
     Q_PROPERTY(QList<QObject*> model READ model NOTIFY modelChanged)
     Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged)
-    Q_PROPERTY(bool useVerticalLayout READ useVerticalLayout WRITE \
setUseVerticalLayout NOTIFY useVerticalLayoutChanged) +    Q_PROPERTY(QSize viewSize \
READ viewSize WRITE setViewSize NOTIFY viewSizeChanged)  
 public:
     explicit AssistantPopupConfig(QObject *parent = 0);
@@ -51,8 +51,8 @@ public:
     QColor background() const { return m_background; }
     QColor highlight() const { return m_highlight; }
 
-    bool useVerticalLayout() const { return m_useVerticalLayout; }
-    void setUseVerticalLayout(bool vertical);
+    QSize viewSize() const { return m_viewSize; };
+    void setViewSize(const QSize &size);
 
     QString title() const { return m_title; }
     void setTitle(const QString& title);
@@ -70,7 +70,7 @@ signals:
     void titleChanged(const QString& title);
     void modelChanged(const QList<QObject*>& model);
     void activeChanged(bool active);
-    void useVerticalLayoutChanged(bool useVerticalLayout);
+    void viewSizeChanged(const QSize& size);
 
 private:
     QColor m_foreground;
@@ -80,12 +80,12 @@ private:
     QString m_title;
     QList<QObject*> m_model;
     bool m_active;
-    bool m_useVerticalLayout;
+    QSize m_viewSize;
 };
 
 Q_DECLARE_METATYPE(AssistantPopupConfig*)
 
-class AssistantPopup : public QDeclarativeView
+class AssistantPopup : public QQuickWidget
 {
     Q_OBJECT
 
@@ -111,14 +111,13 @@ public:
 private slots:
     void updatePosition(KTextEditor::View* view, const KTextEditor::Cursor& newPos);
     void updateState();
-    void updateLayoutType();
+    void updateLayout();
 
     void executeHideAction();
     void hideAssistant();
 
 protected:
     virtual bool eventFilter(QObject* object, QEvent* event) override;
-    virtual bool viewportEvent(QEvent *event) override;
 
 private:
     void setView(KTextEditor::View* view);
@@ -128,10 +127,8 @@ private:
     KDevelop::IAssistant::Ptr m_assistant;
     QPointer<KTextEditor::View> m_view;
     AssistantPopupConfig* m_config;
-    bool m_shownAtBottom;
-    bool m_reopening;
-    QTimer* m_updateTimer;
     QList<QShortcut*> m_shortcuts;
+    bool m_firstLayoutCompleted;
 };
 
 #endif // KDEVPLATFORM_ASSISTANTPOPUP_H
diff --git a/shell/assistantpopup.qml b/shell/assistantpopup.qml
index cc40a13..63c00e8 100644
--- a/shell/assistantpopup.qml
+++ b/shell/assistantpopup.qml
@@ -19,13 +19,26 @@
 
 // This file provides the whole assistant, including title and buttons.
 
-import QtQuick 1.0
+import QtQuick 2.2
 
 Rectangle {
     id: root
 
-    width: mainFlow.width + 16
-    height: mainFlow.height + 10
+    readonly property int vSpacing: 4
+    readonly property int hSpacing: 4
+    readonly property real itemsWidth: {
+        var totalWidth = title.width;
+        for (var i = 0; i < items.count; ++i) {
+            totalWidth += items.itemAt(i).width;
+        }
+        return totalWidth + (items.count + 2) * hSpacing;
+    }
+    readonly property bool useVerticalLayout: config.viewSize.width * 0.90 < \
itemsWidth +
+    // QQuickWidget crashes if either of these is zero
+    // Use ceil to ensure the widget always fits the non-integral content size
+    width: Math.ceil(Math.max(hSpacing, mainFlow.width + hSpacing * 2))
+    height: Math.ceil(Math.max(vSpacing, mainFlow.height + vSpacing * 2))
 
     border.width: 1
     border.color: Qt.lighter(config.foreground)
@@ -41,44 +54,35 @@ Rectangle {
             centerIn: parent
         }
 
-        flow: config.useVerticalLayout ? Flow.TopToBottom : Flow.LeftToRight
-        spacing: config.useVerticalLayout ? 4 : 8
+        flow: root.useVerticalLayout ? Flow.TopToBottom : Flow.LeftToRight
+        spacing: root.useVerticalLayout ? root.vSpacing : root.hSpacing
 
         Text {
             id: title
 
-            anchors.verticalCenter: parent.flow == Flow.LeftToRight ? \
                parent.verticalCenter : undefined
-            anchors.verticalCenterOffset: 1
-
             color: config.foreground
             font.bold: true
             text: config.title
         }
 
-        // Layout for the buttons
-        Flow {
-            id: buttonsFlow
-
-            spacing: 8
 
-            Repeater {
-                id: items
-                objectName: "items"
+        Repeater {
+            id: items
+            objectName: "items"
 
-                y: 5
-                model: config.model
+            y: 5
+            model: config.model
 
-                AssistantButton {
-                    text: modelData.text
-                    highlighted: config.active
-                    // what is displayed in the hotkey field of the button
-                    button: index == items.model.length - 1 ? 0 : index + 1
-                    foreground: config.foreground
-                    background: config.background
-                    highlight: config.highlight
+            AssistantButton {
+                text: modelData.text
+                highlighted: config.active
+                // what is displayed in the hotkey field of the button
+                button: index == items.model.length - 1 ? 0 : index + 1
+                foreground: config.foreground
+                background: config.background
+                highlight: config.highlight
 
-                    onTriggered: { modelData.trigger() }
-                }
+                onTriggered: { modelData.trigger() }
             }
         }
     }


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic