diff --git a/interfaces/iuicontroller.h b/interfaces/iuicontroller.h index 41950cfe5f..9d5fc19bee 100644 --- a/interfaces/iuicontroller.h +++ b/interfaces/iuicontroller.h @@ -1,184 +1,179 @@ /*************************************************************************** * Copyright 2007 Alexander Dymo * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef KDEVPLATFORM_IUICONTROLLER_H #define KDEVPLATFORM_IUICONTROLLER_H #include "interfacesexport.h" #include #include class QAction; template class QExplicitlySharedDataPointer; namespace KParts { class MainWindow; } namespace Sublime{ class Controller; class View; class Area; } namespace KDevelop { class IDocument; class IAssistant; class KDEVPLATFORMINTERFACES_EXPORT IToolViewFactory { public: virtual ~IToolViewFactory() {} /** * called to create a new widget for this toolview * @param parent the parent to use as parent for the widget * @returns the new widget for the toolview */ virtual QWidget* create(QWidget *parent = 0) = 0; /** * @returns the identifier of this toolview. The identifier * is used to remember which areas the tool view should appear * in, and must never change. */ virtual QString id() const = 0; /** * @returns the default position where this toolview should appear */ virtual Qt::DockWidgetArea defaultPosition() = 0; /** * Fetch a list of actions to add to the toolbar of the toolview @p view * @param view the view to which the actions should be added * @returns a list of actions to be added to the toolbar */ virtual QList toolBarActions( QWidget* viewWidget ) const { return viewWidget->actions(); } /** * Fetch a list of actions to be shown in the context menu of the toolview @p view. * The default implementation will return all actions of @p viewWidget. * * @param view the view for which the context menu should be shown * @returns a list of actions to be shown in the context menu */ virtual QList contextMenuActions( QWidget* viewWidget ) const { return viewWidget->actions(); } /** * called when a new view is created from this template * @param view the new sublime view that is being shown */ virtual void viewCreated(Sublime::View* view); /** * @returns if multiple tool views can by created by this factory in the same area. */ virtual bool allowMultiple() const { return false; } }; /** * * Allows to access various parts of the user-interface, like the toolviews or the mainwindow */ class KDEVPLATFORMINTERFACES_EXPORT IUiController { public: virtual ~IUiController(); enum SwitchMode { ThisWindow /**< indicates that the area switch should be in the this window */, NewWindow /**< indicates that the area switch should be using a new window */ }; virtual void switchToArea(const QString &areaName, SwitchMode switchMode) = 0; virtual void addToolView(const QString &name, IToolViewFactory *factory) = 0; virtual void removeToolView(IToolViewFactory *factory) = 0; enum FindFlags { None = 0, Create = 1, ///The tool-view is created if it doesn't exist in the current area yet Raise = 2, ///The tool-view is raised if it was found/created CreateAndRaise = Create | Raise ///The tool view is created and raised }; /** Makes sure that this tool-view exists in the current area, raises it, and returns the contained widget * Returns zero on failure */ virtual QWidget* findToolView(const QString& name, IToolViewFactory *factory, FindFlags flags = CreateAndRaise) = 0; /** * Makes sure that the toolview that contains the widget @p toolViewWidget is visible to the user. */ virtual void raiseToolView(QWidget* toolViewWidget) = 0; /** @return active mainwindow or 0 if no such mainwindow is active.*/ virtual KParts::MainWindow *activeMainWindow() = 0; /*! @p status must implement KDevelop::IStatus */ virtual void registerStatus(QObject* status) = 0; /** - * Shows an assistant popup at bottom within the current central widget - * @p assistant the assistant that will be shown in a popup */ - virtual void popUpAssistant(const QExplicitlySharedDataPointer& assistant) = 0; - - /** - * Hides the assistant if it is currently being shown + * Show the assistant specified by @p assistant */ - virtual void hideAssistant() = 0; - + virtual void showAssistant(const QExplicitlySharedDataPointer& assistant) = 0; + /** * This is meant to be used by IDocument subclasses to initialize the * Sublime::Document. */ virtual Sublime::Controller* controller() = 0; /** Shows an error message in the status bar. * * Unlike all other functions in this class, this function is thread-safe. * You can call it from the background. * * @p message The message * @p timeout The timeout in seconds how long to show the message */ virtual void showErrorMessage(const QString& message, int timeout = 1) = 0; /** @return area for currently active sublime mainwindow or 0 if no sublime mainwindow is active.*/ virtual Sublime::Area *activeArea() = 0; /** * Widget which is currently responsible for consuming special events in the UI * (such as shortcuts) * * @sa IToolViewActionListener * @return QWidget implementing the IToolViewActionListener interface */ virtual QWidget* activeToolViewActionListener() const = 0; /** * @returns all areas in the shell * * @note there will be one per mainwindow, of each type, plus the default ones. */ virtual QList allAreas() const = 0; protected: IUiController(); }; } #endif diff --git a/language/assistant/staticassistantsmanager.cpp b/language/assistant/staticassistantsmanager.cpp index 4e86fdd818..3aa60bff0f 100644 --- a/language/assistant/staticassistantsmanager.cpp +++ b/language/assistant/staticassistantsmanager.cpp @@ -1,317 +1,317 @@ /* Copyright 2009 David Nolden Copyright 2014 Kevin Funk This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "staticassistantsmanager.h" #include "util/debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; using namespace KTextEditor; struct StaticAssistantsManager::Private { Private(StaticAssistantsManager* qq) : q(qq) { connect(KDevelop::ICore::self()->languageController()->backgroundParser(), &BackgroundParser::parseJobFinished, q, [this] (ParseJob* job) { parseJobFinished(job); }); } void eventuallyStartAssistant(); void startAssistant(KDevelop::IAssistant::Ptr assistant); void checkAssistantForProblems(KDevelop::TopDUContext* top); void documentLoaded(KDevelop::IDocument*); void textInserted(Document* document, const Cursor& cursor, const QString& text); void textRemoved(Document* document, const Range& cursor, const QString& removedText); void parseJobFinished(KDevelop::ParseJob*); void documentActivated(KDevelop::IDocument*); void cursorPositionChanged(KTextEditor::View*, const KTextEditor::Cursor&); void timeout(); StaticAssistantsManager* q; QPointer m_currentView; KTextEditor::Cursor m_assistantStartedAt; KDevelop::IndexedString m_currentDocument; QExplicitlySharedDataPointer m_activeAssistant; QList m_registeredAssistants; bool m_activeProblemAssistant = false; QTimer* m_timer; SafeDocumentPointer m_eventualDocument; KTextEditor::Range m_eventualRange; QString m_eventualRemovedText; QMetaObject::Connection m_cursorPositionChangeConnection; }; StaticAssistantsManager::StaticAssistantsManager(QObject* parent) : QObject(parent) , d(new Private(this)) { d->m_timer = new QTimer(this); d->m_timer->setSingleShot(true); d->m_timer->setInterval(400); connect(d->m_timer, &QTimer::timeout, this, [&] { d->timeout(); }); connect(KDevelop::ICore::self()->documentController(), &IDocumentController::documentLoaded, this, [&] (IDocument* document) { d->documentLoaded(document); }); connect(KDevelop::ICore::self()->documentController(), &IDocumentController::documentActivated, this, [&] (IDocument* doc) { d->documentActivated(doc); }); foreach (IDocument* document, ICore::self()->documentController()->openDocuments()) { d->documentLoaded(document); } } StaticAssistantsManager::~StaticAssistantsManager() { } QExplicitlySharedDataPointer StaticAssistantsManager::activeAssistant() { return d->m_activeAssistant; } void StaticAssistantsManager::registerAssistant(const StaticAssistant::Ptr assistant) { if (d->m_registeredAssistants.contains(assistant)) return; d->m_registeredAssistants << assistant; } void StaticAssistantsManager::unregisterAssistant(const StaticAssistant::Ptr assistant) { d->m_registeredAssistants.removeOne(assistant); } QList StaticAssistantsManager::registeredAssistants() const { return d->m_registeredAssistants; } void StaticAssistantsManager::Private::documentLoaded(IDocument* document) { if (document->textDocument()) { connect(document->textDocument(), &Document::textInserted, q, [&] (Document* document, const Cursor& cursor, const QString& text) { textInserted(document, cursor, text); }); connect(document->textDocument(), &Document::textRemoved, q, [&] (Document* document, const Range& range, const QString& removedText) { textRemoved(document, range, removedText); }); } } void StaticAssistantsManager::hideAssistant() { d->m_activeAssistant = QExplicitlySharedDataPointer(); d->m_activeProblemAssistant = false; emit activeAssistantChanged(); } void StaticAssistantsManager::Private::textInserted(Document* document, const Cursor& cursor, const QString& text) { m_eventualDocument = document; m_eventualRange = Range(cursor, text.size()); m_eventualRemovedText.clear(); QMetaObject::invokeMethod(q, "eventuallyStartAssistant", Qt::QueuedConnection); } void StaticAssistantsManager::Private::textRemoved(Document* document, const Range& range, const QString& removedText) { m_eventualDocument = document; m_eventualRange = range; m_eventualRemovedText = removedText; QMetaObject::invokeMethod(q, "eventuallyStartAssistant", Qt::QueuedConnection); } void StaticAssistantsManager::Private::eventuallyStartAssistant() { if (!m_eventualDocument) { return; } View* view = ICore::self()->documentController()->activeTextDocumentView(); if (!view) { return; } if (view->document() != m_eventualDocument) { qWarning(LANGUAGE) << "Active view does not belong to document of last observed change!"; return; } auto language = ICore::self()->languageController()->languagesForUrl(m_eventualDocument.data()->url()).value(0); if (!language) { return; } qCDebug(LANGUAGE) << "Trying to find assistants for language" << language->name(); foreach (const auto& assistant, m_registeredAssistants) { if (assistant->supportedLanguage() != language) continue; // notify assistant about editor changes assistant->textChanged(view, m_eventualRange, m_eventualRemovedText); if (assistant->isUseful()) { startAssistant(IAssistant::Ptr(assistant.data())); break; } } // optimize, esp. for setText() calls as done in e.g. reformat source // only start the assitant once for multiple textRemoved/textInserted signals m_eventualDocument.clear(); m_eventualRange = Range::invalid(); m_eventualRemovedText.clear(); } void StaticAssistantsManager::Private::startAssistant(IAssistant::Ptr assistant) { if (assistant == m_activeAssistant) { return; } if (m_activeAssistant) { m_activeAssistant->doHide(); } if (!m_currentView) return; m_activeAssistant = assistant; if (m_activeAssistant) { connect(m_activeAssistant.data(), &IAssistant::hide, q, &StaticAssistantsManager::hideAssistant, Qt::UniqueConnection); - ICore::self()->uiController()->popUpAssistant(IAssistant::Ptr(m_activeAssistant.data())); + ICore::self()->uiController()->showAssistant(IAssistant::Ptr(m_activeAssistant.data())); m_assistantStartedAt = m_currentView.data()->cursorPosition(); } emit q->activeAssistantChanged(); } void StaticAssistantsManager::Private::parseJobFinished(ParseJob* job) { if (job->document() != m_currentDocument) { return; } if (m_activeAssistant) { if (m_activeProblemAssistant) { m_activeAssistant->doHide(); //Hide the assistant, as we will create a new one if the problem is still there } else { return; } } DUChainReadLocker lock(DUChain::lock(), 300); if (!lock.locked()) { return; } if (job->duChain()) { checkAssistantForProblems(job->duChain()); } } void StaticAssistantsManager::Private::cursorPositionChanged(View*, const Cursor& pos) { if (m_activeAssistant && m_assistantStartedAt.isValid() && abs(m_assistantStartedAt.line() - pos.line()) >= 1) { m_activeAssistant->doHide(); } m_timer->start(); } void StaticAssistantsManager::Private::documentActivated(IDocument* doc) { if (doc) { m_currentDocument = IndexedString(doc->url()); } if (m_currentView) { QObject::disconnect(m_cursorPositionChangeConnection); m_currentView.clear(); } m_currentView = ICore::self()->documentController()->activeTextDocumentView(); if (m_currentView) { m_cursorPositionChangeConnection = connect(m_currentView.data(), &View::cursorPositionChanged, q, [&] (View* v, const Cursor& pos) { cursorPositionChanged(v, pos); }); } } void StaticAssistantsManager::Private::checkAssistantForProblems(TopDUContext* top) { foreach (ProblemPointer problem, top->problems()) { if (m_currentView && m_currentView.data()->cursorPosition().line() == problem->range().start.line) { IAssistant::Ptr solution = problem->solutionAssistant(); if(solution) { startAssistant(solution); m_activeProblemAssistant = true; break; } } } } void StaticAssistantsManager::Private::timeout() { if (!m_activeAssistant && m_currentView) { DUChainReadLocker lock(DUChain::lock(), 300); if (!lock.locked()) { return; } TopDUContext* top = DUChainUtils::standardContextForUrl(m_currentDocument.toUrl()); if (top) { checkAssistantForProblems(top); } } } #include "moc_staticassistantsmanager.cpp" diff --git a/shell/AssistantButton.qml b/shell/AssistantButton.qml deleted file mode 100644 index 2d07c57041..0000000000 --- a/shell/AssistantButton.qml +++ /dev/null @@ -1,101 +0,0 @@ -/* - Copyright 2014 Sven Brauch - Copyright 2014 Kevin Funk - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License version 2 as published by the Free Software Foundation. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -import QtQuick 2.2 - -// Component which provides a single button for the assistant widget. - -Rectangle { - id: root - - property color foreground - property color background - property color highlight - - property bool highlighted: false - // text on the button - property string text - // text in the shortcut field - property int button - // emitted when the button is clicked with the mouse - signal triggered() - - y: -1 - width: text.width + 4 - height: number.height + 4 - - color: Qt.lighter(root.background, 1.5) - border.color: Qt.lighter(root.foreground, 1.5) - - Behavior on opacity { - NumberAnimation { duration: 150; } - } - MouseArea { - id: mouseArea - - anchors.fill: parent - onClicked: root.triggered() - - hoverEnabled: true - - Row { - // row containing the separators, shortcut text, and button text - z: 3 - id: text - anchors.centerIn: parent - spacing: 0 - Rectangle { width: 2; height: 1; color: Qt.rgba(0, 0, 0, 0) } // padding - Text { - // shortcut key - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: 1 - id: number - color: root.foreground - text: button - z: 2 - } - Rectangle { width: 3; height: 1; color: Qt.rgba(0, 0, 0, 0) } // padding - Rectangle { y: 1; width: 1; color: root.foreground; height: root.height - 1; opacity: 0.3 } // line - Rectangle { width: 4; height: 1; color: Qt.rgba(0, 0, 0, 0) } // padding - Text { - // actual button text - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: 1 - color: root.foreground - text: root.text - textFormat: Text.PlainText - } - Rectangle { width: 2; height: 1; color: Qt.rgba(0, 0, 0, 0) } // padding - } - Rectangle { - // the background color for the shortcut key box, invisible by default. - id: highlightArea - Behavior on opacity { - NumberAnimation { duration: 200 } - } - opacity: (root.highlighted || mouseArea.containsMouse) ? 0.5 : 0.0 - x: 1 - y: 1 - z: 1 - height: text.height - width: number.width + 6 - color: root.highlight - } - } -} diff --git a/shell/CMakeLists.txt b/shell/CMakeLists.txt index 2ac76912f0..82b0690658 100644 --- a/shell/CMakeLists.txt +++ b/shell/CMakeLists.txt @@ -1,178 +1,174 @@ add_definitions(-DTRANSLATION_DOMAIN=\"kdevplatform\") add_subdirectory(tests) set(KDevPlatformShell_LIB_SRCS workingsetcontroller.cpp workingsets/workingset.cpp workingsets/workingsetfilelabel.cpp workingsets/workingsettoolbutton.cpp workingsets/workingsettooltipwidget.cpp workingsets/workingsetwidget.cpp workingsets/closedworkingsetswidget.cpp workingsets/workingsethelpers.cpp - assistantpopup.cpp mainwindow.cpp mainwindow_p.cpp plugincontroller.cpp ktexteditorpluginintegration.cpp shellextension.cpp core.cpp uicontroller.cpp projectcontroller.cpp project.cpp partcontroller.cpp #document.cpp partdocument.cpp textdocument.cpp documentcontroller.cpp languagecontroller.cpp statusbar.cpp runcontroller.cpp sessioncontroller.cpp session.cpp sessionlock.cpp sessionchooserdialog.cpp savedialog.cpp sessiondialog.cpp sourceformattercontroller.cpp completionsettings.cpp openprojectpage.cpp openprojectdialog.cpp projectinfopage.cpp selectioncontroller.cpp documentationcontroller.cpp debugcontroller.cpp launchconfiguration.cpp launchconfigurationdialog.cpp loadedpluginsdialog.cpp testcontroller.cpp projectsourcepage.cpp debug.cpp configdialog.cpp editorconfigpage.cpp environmentconfigurebutton.cpp checkerstatus.cpp problem.cpp problemmodelset.cpp problemmodel.cpp problemstore.cpp watcheddocumentset.cpp filteredproblemstore.cpp progresswidget/progressmanager.cpp progresswidget/statusbarprogresswidget.cpp progresswidget/overlaywidget.cpp progresswidget/progressdialog.cpp areadisplay.cpp settings/uipreferences.cpp settings/pluginpreferences.cpp settings/sourceformattersettings.cpp settings/editstyledialog.cpp settings/projectpreferences.cpp settings/environmentwidget.cpp settings/environmentgroupmodel.cpp settings/environmentpreferences.cpp settings/languagepreferences.cpp settings/bgpreferences.cpp settings/templateconfig.cpp settings/templatepage.cpp ) kconfig_add_kcfg_files(KDevPlatformShell_LIB_SRCS settings/uiconfig.kcfgc settings/projectconfig.kcfgc settings/languageconfig.kcfgc settings/bgconfig.kcfgc ) ki18n_wrap_ui(KDevPlatformShell_LIB_SRCS sessiondialog.ui projectinfopage.ui launchconfigurationdialog.ui projectsourcepage.ui settings/uiconfig.ui settings/editstyledialog.ui settings/sourceformattersettings.ui settings/projectpreferences.ui settings/environmentwidget.ui settings/languagepreferences.ui settings/bgpreferences.ui settings/templateconfig.ui settings/templatepage.ui ) kdevplatform_add_library(KDevPlatformShell SOURCES ${KDevPlatformShell_LIB_SRCS}) target_link_libraries(KDevPlatformShell LINK_PUBLIC KF5::XmlGui KDev::Sublime KDev::OutputView KDev::Debugger KDev::Interfaces LINK_PRIVATE - Qt5::Quick - Qt5::QuickWidgets - KF5::GuiAddons KF5::IconThemes KF5::KIOFileWidgets KF5::KIOWidgets KF5::Parts KF5::Notifications KF5::NotifyConfig KF5::TextEditor KF5::ThreadWeaver KF5::JobWidgets KF5::ItemViews KF5::WindowSystem KF5::KCMUtils #for KPluginSelector, not sure why it is in kcmutils KF5::NewStuff # template config page KF5::Archive # template config page KDev::Project KDev::Vcs KDev::Language KDev::Util KDev::Documentation ) install(FILES mainwindow.h plugincontroller.h shellextension.h core.h uicontroller.h projectcontroller.h project.h partcontroller.h partdocument.h textdocument.h documentcontroller.h languagecontroller.h session.h sessioncontroller.h sessionlock.h sourceformattercontroller.h selectioncontroller.h runcontroller.h launchconfiguration.h environmentconfigurebutton.h checkerstatus.h problem.h problemmodel.h problemmodelset.h problemconstants.h filteredproblemstore.h DESTINATION ${KDE_INSTALL_INCLUDEDIR}/kdevplatform/shell COMPONENT Devel ) install( FILES debugger/kdevdebuggershellui.rc DESTINATION ${KDE_INSTALL_KXMLGUI5DIR}/kdevdebugger ) install( FILES kdevsessionui.rc DESTINATION ${KDE_INSTALL_KXMLGUI5DIR}/kdevsession ) install( FILES kdevsourceformatter.rc DESTINATION ${KDE_INSTALL_KXMLGUI5DIR}/kdevsourceformatter ) -install( FILES AssistantButton.qml assistantpopup.qml DESTINATION ${KDE_INSTALL_DATADIR}/kdevelop ) + diff --git a/shell/assistantpopup.cpp b/shell/assistantpopup.cpp deleted file mode 100644 index 8aea1e6bec..0000000000 --- a/shell/assistantpopup.cpp +++ /dev/null @@ -1,382 +0,0 @@ -/* - Copyright 2009 David Nolden - Copyright 2012 Milian Wolff - Copyright 2014 Sven Brauch - Copyright 2014 Kevin Funk - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License version 2 as published by the Free Software Foundation. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#include "assistantpopup.h" -#include "sublime/holdupdates.h" -#include "util/kdevstringhandler.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -using namespace KDevelop; - -namespace { - -/// Interval after which the state of the popup is re-evaluated -/// Used to avoid flickering caused when user is quickly inserting code -const int UPDATE_STATE_INTERVAL = 300; // ms - -const int ASSISTANT_MODIFIER = -#ifdef Q_OS_MAC -Qt::CTRL; -#else -Qt::ALT; -#endif - -const int ASSISTANT_MOD_KEY = -#ifdef Q_OS_MAC -Qt::Key_Control; -#else -Qt::Key_Alt; -#endif - -QWidget* findByClassname(const KTextEditor::View* view, const QString& klass) -{ - auto children = view->findChildren(); - for ( auto child: children ) { - if ( child->metaObject()->className() == klass ) { - return child; - } - } - return nullptr; -}; - -/** - * @brief Get the geometry of the inner part (with the text) of the KTextEditor::View being used. - */ -QRect textWidgetGeometry(const KTextEditor::View *view) -{ - // Subtract the width of the right scrollbar - int scrollbarWidth = 0; - if ( auto scrollbar = findByClassname(view, "KateScrollBar") ) { - scrollbarWidth = scrollbar->width(); - } - // Subtract the width of the bottom scrollbar - int bottomScrollbarWidth = 0; - if ( auto bottom = findByClassname(view, "QScrollBar") ) { - bottomScrollbarWidth = bottom->height(); - } - auto geom = view->geometry(); - - geom.adjust(0, 0, -scrollbarWidth, -bottomScrollbarWidth); - return geom; -} - -} - -AssistantPopupConfig::AssistantPopupConfig(QObject *parent) - : QObject(parent) - , m_active(false) -{ -} - -void AssistantPopupConfig::setColorsFromView(QObject *view) -{ - auto iface = dynamic_cast(view); - Q_ASSERT(iface); - m_foreground = iface->configValue("line-number-color").value(); - m_background = iface->configValue("icon-border-color").value(); - m_highlight = iface->configValue("folding-marker-color").value(); - if ( KColorUtils::luma(m_background) < 0.3 ) { - m_foreground = KColorUtils::lighten(m_foreground, 0.7); - } - const float lumaDiff = KColorUtils::luma(m_highlight) - KColorUtils::luma(m_background); - if ( qAbs(lumaDiff) < 0.5 ) { - m_highlight = QColor::fromHsv(m_highlight.hue(), - qMin(255, m_highlight.saturation() + 80), - lumaDiff > 0 ? qMin(255, m_highlight.value() + 120) - : qMax(80, m_highlight.value() - 40)); - } - emit colorsChanged(); -} - -bool AssistantPopupConfig::isActive() const -{ - return m_active; -} - -void AssistantPopupConfig::setActive(bool active) -{ - if (m_active == active) { - return; - } - - m_active = active; - emit activeChanged(m_active); -} - -void AssistantPopupConfig::setViewSize(const QSize& size) -{ - if (size != m_viewSize) { - m_viewSize = size; - emit viewSizeChanged(size); - } -} - -void AssistantPopupConfig::setTitle(const QString& title) -{ - if (m_title == title) { - return; - } - - m_title = title; - emit titleChanged(m_title); -} - -void AssistantPopupConfig::setModel(const QList& model) -{ - if (m_model == model) { - return; - } - - qDeleteAll( m_model ); - m_model = model; - emit modelChanged(model); -} - -AssistantPopup::AssistantPopup() -// main window as parent to use maximal space available in worst case - : QQuickWidget(ICore::self()->uiController()->activeMainWindow()) - , m_config(new AssistantPopupConfig(this)) - , m_firstLayoutCompleted(false) -{ - setAttribute(Qt::WA_ShowWithoutActivating); - - rootContext()->setContextProperty("config", m_config); - - 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); - } - - 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); - updateState(); -} - -void AssistantPopup::setView(KTextEditor::View* view) -{ - if (m_view == view) { - return; - } - - setActive(false); - - if (m_view) { - m_view->removeEventFilter(this); - disconnect(m_view.data(), &KTextEditor::View::verticalScrollPositionChanged, - 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, - this, &AssistantPopup::updatePosition); - } -} - -void AssistantPopup::setAssistant(const IAssistant::Ptr& assistant) -{ - if (m_assistant == assistant) { - return; - } - - if (m_assistant) { - disconnect(m_assistant.data(), &IAssistant::hide, this, &AssistantPopup::hideAssistant); - } - m_assistant = assistant; - if (m_assistant) { - connect(m_assistant.data(), &IAssistant::hide, this, &AssistantPopup::hideAssistant); - } else { - hide(); - } -} - -void AssistantPopup::setActive(bool active) -{ - m_config->setActive(active); - for (auto shortcut : m_shortcuts) { - shortcut->setEnabled(active); - } -} - -bool AssistantPopup::eventFilter(QObject* object, QEvent* event) -{ - Q_UNUSED(object); - - if (!m_view || (object != m_view.data())) - return false; - - if (event->type() == QEvent::Resize) { - updateLayout(); - } else if (event->type() == QEvent::Hide) { - executeHideAction(); - } else if (event->type() == QEvent::KeyPress) { - auto keyEvent = static_cast(event); - if (keyEvent->modifiers() == ASSISTANT_MODIFIER) { - setActive(true); - } - if (keyEvent->key() == Qt::Key_Escape) { - executeHideAction(); - } - } else if (event->type() == QEvent::KeyRelease) { - auto keyEvent = static_cast(event); - if (keyEvent->modifiers() == ASSISTANT_MODIFIER || keyEvent->key() == ASSISTANT_MOD_KEY) { - setActive(false); - } - } - return false; -} - -void AssistantPopup::updatePosition(KTextEditor::View* view, const KTextEditor::Cursor& newPos) -{ - static const int MARGIN = 12; - - if (newPos.isValid() && newPos.line() == 0) { - // the position is not going to change; don't waste time - return; - } - - auto editorGeometry = textWidgetGeometry(view); - const auto startCursorCoordinate = view->cursorToCoordinate(KTextEditor::Cursor(0, 0)); - - // algorithm for popup positioning: - // if we are scrolled to the top: show at bottom - // else: - // if: current cursor position is in upper half => show at bottom - // else: show at top - const bool showAtBottom = startCursorCoordinate.y() == 0 ? true : - view->cursorPositionCoordinates().y() < view->height()/2; - const QPoint targetLocation = showAtBottom ? - parentWidget()->mapFromGlobal(view->mapToGlobal(editorGeometry.bottomRight() - + QPoint(-width() - MARGIN, -MARGIN - height()))) : - parentWidget()->mapFromGlobal(view->mapToGlobal(editorGeometry.topRight() - + QPoint(-width() - MARGIN, MARGIN))); - if (pos() == targetLocation) { - return; - } - - Sublime::HoldUpdates hold(ICore::self()->uiController()->activeMainWindow()); - move(targetLocation); -} - -IAssistant::Ptr AssistantPopup::assistant() const -{ - return m_assistant; -} - -void AssistantPopup::executeHideAction() -{ - if ( isVisible() ) { - m_assistant->doHide(); - } -} - -void AssistantPopup::hideAssistant() -{ - reset(nullptr, {}); // indirectly calls hide() -} - -void AssistantPopup::updateLayout() -{ - if ( !m_view ) { - return; - } - - 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; - } -} - -void AssistantPopup::updateState() -{ - if (!m_assistant || m_assistant->actions().isEmpty() || !m_view) { - hide(); - return; - } - - auto curShortcut = m_shortcuts.constBegin(); - auto hideAction = new QAction(i18n("Hide"), this); - connect(*curShortcut, &QShortcut::activated, hideAction, &QAction::trigger); - connect(hideAction, &QAction::triggered, this, &AssistantPopup::executeHideAction); - - QList items; - foreach (IAssistantAction::Ptr action, m_assistant->actions()) { - QAction* asQAction = action->toKAction(); - items << asQAction; - asQAction->setParent(this); - //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(action.data(), SIGNAL(executed(IAssistantAction*)), hideAction, SLOT(trigger())); - } - items << hideAction; - - auto view = ICore::self()->documentController()->activeTextDocumentView(); - m_config->setColorsFromView(view); - m_config->setModel(items); - m_config->setTitle(m_assistant->title()); - setActive(false); - - show(); -} - diff --git a/shell/assistantpopup.h b/shell/assistantpopup.h deleted file mode 100644 index 44cd167e75..0000000000 --- a/shell/assistantpopup.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - Copyright 2009 David Nolden - Copyright 2012 Milian Wolff - Copyright 2014 Sven Brauch - Copyright 2014 Kevin Funk - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License version 2 as published by the Free Software Foundation. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#ifndef KDEVPLATFORM_ASSISTANTPOPUP_H -#define KDEVPLATFORM_ASSISTANTPOPUP_H - -#include -#include -#include - -namespace KTextEditor -{ -class View; -class Cursor; -} - -class AssistantPopupConfig : public QObject -{ - Q_OBJECT - Q_PROPERTY(QColor foreground READ foreground NOTIFY colorsChanged) - Q_PROPERTY(QColor background READ background NOTIFY colorsChanged) - Q_PROPERTY(QColor highlight READ highlight NOTIFY colorsChanged) - - Q_PROPERTY(QString title READ title NOTIFY titleChanged) - Q_PROPERTY(QList model READ model NOTIFY modelChanged) - Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged) - Q_PROPERTY(QSize viewSize READ viewSize WRITE setViewSize NOTIFY viewSizeChanged) - -public: - explicit AssistantPopupConfig(QObject *parent = 0); - - QColor foreground() const { return m_foreground; } - QColor background() const { return m_background; } - QColor highlight() const { return m_highlight; } - - QSize viewSize() const { return m_viewSize; }; - void setViewSize(const QSize &size); - - QString title() const { return m_title; } - void setTitle(const QString& title); - QList model() const { return m_model; } - void setModel(const QList& model); - - void setColorsFromView(QObject *view); - - bool isActive() const; - void setActive(bool active); - -signals: - void colorsChanged(); - - void titleChanged(const QString& title); - void modelChanged(const QList& model); - void activeChanged(bool active); - void viewSizeChanged(const QSize& size); - -private: - QColor m_foreground; - QColor m_background; - QColor m_highlight; - - QString m_title; - QList m_model; - bool m_active; - QSize m_viewSize; -}; - -Q_DECLARE_METATYPE(AssistantPopupConfig*) - -class AssistantPopup : public QQuickWidget -{ - Q_OBJECT - -public: - typedef QExplicitlySharedDataPointer Ptr; - - /** - * The current main window will be used as parent widget for the popup. - * This is to make use of the maximal space available and prevent any lines - * in e.g. the editor to be hidden by the popup. - */ - AssistantPopup(); - - /** - * Reset this popup for view @p view and show assistant @p assistant - * - * @p view The widget below which the assistant should be shown. - */ - void reset(KTextEditor::View *view, const KDevelop::IAssistant::Ptr &assistant); - - KDevelop::IAssistant::Ptr assistant() const; - -private slots: - void updatePosition(KTextEditor::View* view, const KTextEditor::Cursor& newPos); - void updateState(); - void updateLayout(); - - void executeHideAction(); - void hideAssistant(); - -protected: - virtual bool eventFilter(QObject* object, QEvent* event) override; - -private: - void setView(KTextEditor::View* view); - void setAssistant(const KDevelop::IAssistant::Ptr& assistant); - void setActive( bool active ); - - KDevelop::IAssistant::Ptr m_assistant; - QPointer m_view; - AssistantPopupConfig* m_config; - QList m_shortcuts; - bool m_firstLayoutCompleted; -}; - -#endif // KDEVPLATFORM_ASSISTANTPOPUP_H diff --git a/shell/assistantpopup.qml b/shell/assistantpopup.qml deleted file mode 100644 index 63c00e8756..0000000000 --- a/shell/assistantpopup.qml +++ /dev/null @@ -1,89 +0,0 @@ -/* - Copyright 2014 Sven Brauch - Copyright 2014 Kevin Funk - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License version 2 as published by the Free Software Foundation. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -// This file provides the whole assistant, including title and buttons. - -import QtQuick 2.2 - -Rectangle { - id: root - - 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) - gradient: Gradient { - GradientStop { position: 0.0; color: Qt.lighter(config.background) } - GradientStop { position: 1.0; color: config.background } - } - - Flow { - id: mainFlow - - anchors { - centerIn: parent - } - - flow: root.useVerticalLayout ? Flow.TopToBottom : Flow.LeftToRight - spacing: root.useVerticalLayout ? root.vSpacing : root.hSpacing - - Text { - id: title - - color: config.foreground - font.bold: true - text: config.title - } - - - Repeater { - id: items - objectName: "items" - - 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 - - onTriggered: { modelData.trigger() } - } - } - } -} diff --git a/shell/uicontroller.cpp b/shell/uicontroller.cpp index ceb631e7d8..1df7a96346 100644 --- a/shell/uicontroller.cpp +++ b/shell/uicontroller.cpp @@ -1,778 +1,764 @@ /*************************************************************************** * Copyright 2007 Alexander Dymo * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "uicontroller.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core.h" #include "configpage.h" #include "configdialog.h" #include "debug.h" #include "editorconfigpage.h" #include "shellextension.h" #include "partcontroller.h" #include "plugincontroller.h" #include "mainwindow.h" #include "partdocument.h" #include "textdocument.h" #include "documentcontroller.h" -#include "assistantpopup.h" #include #include "workingsetcontroller.h" #include "workingsets/workingset.h" #include "settings/bgpreferences.h" #include "settings/languagepreferences.h" #include "settings/environmentpreferences.h" #include "settings/pluginpreferences.h" #include "settings/projectpreferences.h" #include "settings/sourceformattersettings.h" #include "settings/uipreferences.h" #include "settings/templateconfig.h" namespace KDevelop { class UiControllerPrivate { public: UiControllerPrivate(UiController *controller) : areasRestored(false), m_controller(controller) { if (Core::self()->workingSetControllerInternal()) Core::self()->workingSetControllerInternal()->initializeController(m_controller); m_controller->connect(m_controller, &Sublime::Controller::mainWindowAdded, m_controller, &UiController::mainWindowAdded); QMap desired; desired["org.kdevelop.ClassBrowserView"] = Sublime::Left; desired["org.kdevelop.DocumentsView"] = Sublime::Left; desired["org.kdevelop.ProjectsView"] = Sublime::Left; desired["org.kdevelop.FileManagerView"] = Sublime::Left; desired["org.kdevelop.ProblemReporterView"] = Sublime::Bottom; desired["org.kdevelop.OutputView"] = Sublime::Bottom; desired["org.kdevelop.ContextBrowser"] = Sublime::Bottom; desired["org.kdevelop.KonsoleView"] = Sublime::Bottom; desired["org.kdevelop.SnippetView"] = Sublime::Right; desired["org.kdevelop.ExternalScriptView"] = Sublime::Right; Sublime::Area* a = new Sublime::Area(m_controller, "code", i18n("Code")); a->setDesiredToolViews(desired); a->setIconName("document-edit"); m_controller->addDefaultArea(a); desired.clear(); desired["org.kdevelop.debugger.VariablesView"] = Sublime::Left; desired["org.kdevelop.debugger.BreakpointsView"] = Sublime::Bottom; desired["org.kdevelop.debugger.StackView"] = Sublime::Bottom; desired["org.kdevelop.debugger.ConsoleView"] = Sublime::Bottom; desired["org.kdevelop.KonsoleView"] = Sublime::Bottom; a = new Sublime::Area(m_controller, "debug", i18n("Debug")); a->setDesiredToolViews(desired); a->setIconName("tools-report-bug"); m_controller->addDefaultArea(a); desired.clear(); desired["org.kdevelop.ProjectsView"] = Sublime::Left; desired["org.kdevelop.PatchReview"] = Sublime::Bottom; a = new Sublime::Area(m_controller, "review", i18n("Review")); a->setDesiredToolViews(desired); a->setIconName("applications-engineering"); m_controller->addDefaultArea(a); if(!(Core::self()->setupFlags() & Core::NoUi)) { defaultMainWindow = new MainWindow(m_controller); m_controller->addMainWindow(defaultMainWindow); activeSublimeWindow = defaultMainWindow; } else { activeSublimeWindow = defaultMainWindow = 0; } m_assistantTimer.setSingleShot(true); m_assistantTimer.setInterval(100); } void widgetChanged(QWidget*, QWidget* now) { if (now) { Sublime::MainWindow* win = qobject_cast(now->window()); if( win ) { activeSublimeWindow = win; } } } Core *core; QPointer defaultMainWindow; QMap factoryDocuments; QPointer activeSublimeWindow; bool areasRestored; - /// Currently shown assistant popup. - QPointer currentShownAssistant; /// QWidget implementing IToolViewActionListener interface, or null QPointer activeActionListener; QTimer m_assistantTimer; private: UiController *m_controller; }; class UiToolViewFactory: public Sublime::ToolFactory { public: UiToolViewFactory(IToolViewFactory *factory): m_factory(factory) {} ~UiToolViewFactory() { delete m_factory; } virtual QWidget* create(Sublime::ToolDocument *doc, QWidget *parent = 0) override { Q_UNUSED( doc ); return m_factory->create(parent); } virtual QList< QAction* > contextMenuActions(QWidget* viewWidget) const override { return m_factory->contextMenuActions( viewWidget ); } QList toolBarActions( QWidget* viewWidget ) const override { return m_factory->toolBarActions( viewWidget ); } QString id() const override { return m_factory->id(); } private: IToolViewFactory *m_factory; }; class ViewSelectorItem: public QListWidgetItem { public: ViewSelectorItem(const QString &text, QListWidget *parent = 0, int type = Type) :QListWidgetItem(text, parent, type) {} IToolViewFactory *factory; }; class NewToolViewListWidget: public QListWidget { Q_OBJECT public: NewToolViewListWidget(MainWindow *mw, QWidget* parent = 0) :QListWidget(parent), m_mw(mw) { connect(this, &NewToolViewListWidget::doubleClicked, this, &NewToolViewListWidget::addNewToolViewByDoubleClick); } Q_SIGNALS: void addNewToolView(MainWindow *mw, QListWidgetItem *item); private Q_SLOTS: void addNewToolViewByDoubleClick(QModelIndex index) { QListWidgetItem *item = itemFromIndex(index); // Disable item so that the toolview can not be added again. item->setFlags(item->flags() & ~Qt::ItemIsEnabled); emit addNewToolView(m_mw, item); } private: MainWindow *m_mw; }; UiController::UiController(Core *core) :Sublime::Controller(0), IUiController(), d(new UiControllerPrivate(this)) { setObjectName("UiController"); d->core = core; if (!defaultMainWindow() || (Core::self()->setupFlags() & Core::NoUi)) return; connect(qApp, &QApplication::focusChanged, this, [&] (QWidget* old, QWidget* now) { d->widgetChanged(old, now); } ); setupActions(); } UiController::~UiController() { delete d; } void UiController::setupActions() { } void UiController::mainWindowAdded(Sublime::MainWindow* mainWindow) { connect(mainWindow, &MainWindow::activeToolViewChanged, this, &UiController::slotActiveToolViewChanged); connect(mainWindow, &MainWindow::areaChanged, this, &UiController::slotAreaChanged); // also check after area reconstruction } // FIXME: currently, this always create new window. Probably, // should just rename it. void UiController::switchToArea(const QString &areaName, SwitchMode switchMode) { if (switchMode == ThisWindow) { showArea(areaName, activeSublimeWindow()); return; } MainWindow *main = new MainWindow(this); addMainWindow(main); showArea(areaName, main); main->initialize(); // WTF? First, enabling this code causes crashes since we // try to disconnect some already-deleted action, or something. // Second, this code will disconnection the clients from guiFactory // of the previous main window. Ick! #if 0 //we need to add all existing guiclients to the new mainwindow //@todo adymo: add only ones that belong to the area (when the area code is there) foreach (KXMLGUIClient *client, oldMain->guiFactory()->clients()) main->guiFactory()->addClient(client); #endif main->show(); } QWidget* UiController::findToolView(const QString& name, IToolViewFactory *factory, FindFlags flags) { if(!d->areasRestored || !activeArea()) return 0; QList< Sublime::View* > views = activeArea()->toolViews(); foreach(Sublime::View* view, views) { Sublime::ToolDocument *doc = dynamic_cast(view->document()); if(doc && doc->title() == name && view->widget()) { if(flags & Raise) view->requestRaise(); return view->widget(); } } QWidget* ret = 0; if(flags & Create) { if(!d->factoryDocuments.contains(factory)) d->factoryDocuments[factory] = new Sublime::ToolDocument(name, this, new UiToolViewFactory(factory)); Sublime::ToolDocument *doc = d->factoryDocuments[factory]; Sublime::View* view = addToolViewToArea(factory, doc, activeArea()); if(view) ret = view->widget(); if(flags & Raise) findToolView(name, factory, Raise); } return ret; } void UiController::raiseToolView(QWidget* toolViewWidget) { if(!d->areasRestored) return; QList< Sublime::View* > views = activeArea()->toolViews(); foreach(Sublime::View* view, views) { if(view->widget() == toolViewWidget) { view->requestRaise(); return; } } } void UiController::addToolView(const QString & name, IToolViewFactory *factory) { if (!factory) return; qCDebug(SHELL) ; Sublime::ToolDocument *doc = new Sublime::ToolDocument(name, this, new UiToolViewFactory(factory)); d->factoryDocuments[factory] = doc; /* Until areas are restored, we don't know which views should be really added, and which not, so we just record view availability. */ if (d->areasRestored) { foreach (Sublime::Area* area, allAreas()) { addToolViewToArea(factory, doc, area); } } } void KDevelop::UiController::raiseToolView(Sublime::View * view) { foreach( Sublime::Area* area, allAreas() ) { if( area->toolViews().contains( view ) ) area->raiseToolView( view ); } slotActiveToolViewChanged(view); } void UiController::slotAreaChanged(Sublime::Area*) { // this slot gets call if an area in *any* MainWindow changed // so let's first get the "active area" const auto area = activeSublimeWindow()->area(); if (area) { // walk through shown tool views and maku sure the const auto shownIds = area->shownToolViews(Sublime::AllPositions); foreach (Sublime::View* toolView, area->toolViews()) { if (shownIds.contains(toolView->document()->documentSpecifier())) { slotActiveToolViewChanged(toolView); } } } } void UiController::slotActiveToolViewChanged(Sublime::View* view) { if (!view) { return; } // record the last active tool view action listener if (qobject_cast(view->widget())) { d->activeActionListener = view->widget(); } } void KDevelop::UiController::removeToolView(IToolViewFactory *factory) { if (!factory) return; qCDebug(SHELL) ; //delete the tooldocument Sublime::ToolDocument *doc = d->factoryDocuments[factory]; ///@todo adymo: on document deletion all its views shall be also deleted foreach (Sublime::View *view, doc->views()) { foreach (Sublime::Area *area, allAreas()) if (area->removeToolView(view)) view->deleteLater(); } d->factoryDocuments.remove(factory); delete doc; } Sublime::Area *UiController::activeArea() { Sublime::MainWindow *m = activeSublimeWindow(); if (m) return activeSublimeWindow()->area(); return 0; } Sublime::MainWindow *UiController::activeSublimeWindow() { return d->activeSublimeWindow; } MainWindow *UiController::defaultMainWindow() { return d->defaultMainWindow; } void UiController::initialize() { defaultMainWindow()->initialize(); } void UiController::cleanup() { foreach (Sublime::MainWindow* w, mainWindows()) w->saveSettings(); saveAllAreas(KSharedConfig::openConfig()); } void UiController::selectNewToolViewToAdd(MainWindow *mw) { if (!mw || !mw->area()) return; QDialog *dia = new QDialog(mw); dia->setWindowTitle(i18n("Select Tool View to Add")); auto mainLayout = new QVBoxLayout(dia); NewToolViewListWidget *list = new NewToolViewListWidget(mw, dia); list->setSelectionMode(QAbstractItemView::ExtendedSelection); list->setSortingEnabled(true); for (QMap::const_iterator it = d->factoryDocuments.constBegin(); it != d->factoryDocuments.constEnd(); ++it) { ViewSelectorItem *item = new ViewSelectorItem(it.value()->title(), list); item->factory = it.key(); if (!item->factory->allowMultiple() && toolViewPresent(it.value(), mw->area())) { // Disable item if the toolview is already present. item->setFlags(item->flags() & ~Qt::ItemIsEnabled); } list->addItem(item); } list->setFocus(); connect(list, &NewToolViewListWidget::addNewToolView, this, &UiController::addNewToolView); mainLayout->addWidget(list); auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); auto okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); dia->connect(buttonBox, &QDialogButtonBox::accepted, dia, &QDialog::accept); dia->connect(buttonBox, &QDialogButtonBox::rejected, dia, &QDialog::reject); mainLayout->addWidget(buttonBox); if (dia->exec() == QDialog::Accepted) { foreach (QListWidgetItem* item, list->selectedItems()) { addNewToolView(mw, item); } } delete dia; } void UiController::addNewToolView(MainWindow *mw, QListWidgetItem* item) { ViewSelectorItem *current = static_cast(item); Sublime::ToolDocument *doc = d->factoryDocuments[current->factory]; Sublime::View *view = doc->createView(); mw->area()->addToolView(view, Sublime::dockAreaToPosition(current->factory->defaultPosition())); current->factory->viewCreated(view); } void UiController::showSettingsDialog() { auto editorConfigPage = new EditorConfigPage(activeMainWindow()); auto languageConfigPage = new LanguagePreferences(activeMainWindow()); auto configPages = QVector { new UiPreferences(activeMainWindow()), new PluginPreferences(activeMainWindow()), new SourceFormatterSettings(activeMainWindow()), new ProjectPreferences(activeMainWindow()), new EnvironmentPreferences(QString(), activeMainWindow()), new BGPreferences(activeMainWindow()), new TemplateConfig(activeMainWindow()), editorConfigPage }; ConfigDialog cfgDlg(configPages, activeMainWindow()); auto addPluginPages = [&](IPlugin* plugin) { for (int i = 0, numPages = plugin->configPages(); i < numPages; ++i) { auto page = plugin->configPage(i, &cfgDlg); if(page && page->configPageType() == ConfigPage::LanguageConfigPage){ cfgDlg.addSubConfigPage(languageConfigPage, page); } else { // insert them before the editor config page cfgDlg.addConfigPage(page, editorConfigPage); } } }; cfgDlg.addConfigPage(languageConfigPage, configPages[5]); for (IPlugin* plugin : ICore::self()->pluginController()->loadedPlugins()) { addPluginPages(plugin); } // TODO: only load settings if a UI related page was changed? connect(&cfgDlg, &ConfigDialog::configSaved, activeSublimeWindow(), &Sublime::MainWindow::loadSettings); // make sure that pages get added whenever a new plugin is loaded (probably from the plugin selection dialog) // removal on plugin unload is already handled in ConfigDialog connect(ICore::self()->pluginController(), &IPluginController::pluginLoaded, &cfgDlg, addPluginPages); cfgDlg.exec(); } Sublime::Controller* UiController::controller() { return this; } KParts::MainWindow *UiController::activeMainWindow() { return activeSublimeWindow(); } void UiController::saveArea(Sublime::Area * area, KConfigGroup & group) { area->save(group); if (!area->workingSet().isEmpty()) { WorkingSet* set = Core::self()->workingSetControllerInternal()->getWorkingSet(area->workingSet()); set->saveFromArea(area, area->rootIndex()); } } void UiController::loadArea(Sublime::Area * area, const KConfigGroup & group) { area->load(group); if (!area->workingSet().isEmpty()) { WorkingSet* set = Core::self()->workingSetControllerInternal()->getWorkingSet(area->workingSet()); Q_ASSERT(set->isConnected(area)); Q_UNUSED(set); } } void UiController::saveAllAreas(KSharedConfigPtr config) { KConfigGroup uiConfig(config, "User Interface"); int wc = mainWindows().size(); uiConfig.writeEntry("Main Windows Count", wc); for (int w = 0; w < wc; ++w) { KConfigGroup mainWindowConfig(&uiConfig, QStringLiteral("Main Window %1").arg(w)); foreach (Sublime::Area* defaultArea, defaultAreas()) { // FIXME: using object name seems ugly. QString type = defaultArea->objectName(); Sublime::Area* area = this->area(w, type); KConfigGroup areaConfig(&mainWindowConfig, "Area " + type); areaConfig.deleteGroup(); areaConfig.writeEntry("id", type); saveArea(area, areaConfig); areaConfig.sync(); } } uiConfig.sync(); } void UiController::loadAllAreas(KSharedConfigPtr config) { KConfigGroup uiConfig(config, "User Interface"); int wc = uiConfig.readEntry("Main Windows Count", 1); /* It is expected the main windows are restored before restoring areas. */ if (wc > mainWindows().size()) wc = mainWindows().size(); QList changedAreas; /* Offer all toolviews to the default areas. */ foreach (Sublime::Area *area, defaultAreas()) { QMap::const_iterator i, e; for (i = d->factoryDocuments.constBegin(), e = d->factoryDocuments.constEnd(); i != e; ++i) { addToolViewIfWanted(i.key(), i.value(), area); } } /* Restore per-windows areas. */ for (int w = 0; w < wc; ++w) { KConfigGroup mainWindowConfig(&uiConfig, QStringLiteral("Main Window %1").arg(w)); Sublime::MainWindow *mw = mainWindows()[w]; /* We loop over default areas. This means that if the config file has an area of some type that is not in default set, we'd just ignore it. I think it's fine -- the model were a given mainwindow can has it's own area types not represented in the default set is way too complex. */ foreach (Sublime::Area* defaultArea, defaultAreas()) { QString type = defaultArea->objectName(); Sublime::Area* area = this->area(w, type); KConfigGroup areaConfig(&mainWindowConfig, "Area " + type); qCDebug(SHELL) << "Trying to restore area " << type; /* This is just an easy check that a group exists, to avoid "restoring" area from empty config group, wiping away programmatically installed defaults. */ if (areaConfig.readEntry("id", "") == type) { qCDebug(SHELL) << "Restoring area " << type; loadArea(area, areaConfig); } // At this point we know which toolviews the area wants. // Tender all tool views we have. QMap::const_iterator i, e; for (i = d->factoryDocuments.constBegin(), e = d->factoryDocuments.constEnd(); i != e; ++i) { addToolViewIfWanted(i.key(), i.value(), area); } } // Force reload of the changes. showAreaInternal(mw->area(), mw); mw->enableAreaSettingsSave(); } d->areasRestored = true; } void UiController::addToolViewToDockArea(IToolViewFactory* factory, Qt::DockWidgetArea area) { addToolViewToArea(factory, d->factoryDocuments[factory], activeArea(), Sublime::dockAreaToPosition(area)); } bool UiController::toolViewPresent(Sublime::ToolDocument* doc, Sublime::Area* area) { foreach (Sublime::View *view, doc->views()) { if( area->toolViews().contains( view ) ) return true; } return false; } void UiController::addToolViewIfWanted(IToolViewFactory* factory, Sublime::ToolDocument* doc, Sublime::Area* area) { if (area->wantToolView(factory->id())) { addToolViewToArea(factory, doc, area); } } Sublime::View* UiController::addToolViewToArea(IToolViewFactory* factory, Sublime::ToolDocument* doc, Sublime::Area* area, Sublime::Position p) { Sublime::View* view = doc->createView(); area->addToolView( view, p == Sublime::AllPositions ? Sublime::dockAreaToPosition(factory->defaultPosition()) : p); connect(view, &Sublime::View::raise, this, static_cast(&UiController::raiseToolView)); factory->viewCreated(view); return view; } void UiController::registerStatus(QObject* status) { Sublime::MainWindow* w = activeSublimeWindow(); if (!w) return; MainWindow* mw = qobject_cast(w); if (!mw) return; mw->registerStatus(status); } void UiController::showErrorMessage(const QString& message, int timeout) { Sublime::MainWindow* w = activeSublimeWindow(); if (!w) return; MainWindow* mw = qobject_cast(w); if (!mw) return; QMetaObject::invokeMethod(mw, "showErrorMessage", Q_ARG(QString, message), Q_ARG(int, timeout)); } -void UiController::hideAssistant() -{ - if (d->currentShownAssistant) { - d->currentShownAssistant->hide(); - } -} - -void UiController::popUpAssistant(const KDevelop::IAssistant::Ptr& assistant) +void UiController::showAssistant(const KDevelop::IAssistant::Ptr& assistant) { if(!assistant) return; Sublime::View* view = d->activeSublimeWindow->activeView(); if( !view ) { qCDebug(SHELL) << "no active view in mainwindow"; return; } auto editorView = qobject_cast(view->widget()); Q_ASSERT(editorView); if (editorView) { - if ( !d->currentShownAssistant ) { - d->currentShownAssistant = new AssistantPopup; - } - d->currentShownAssistant->reset(editorView, assistant); // TODO: See if we can use CodeCompletionInterface::startCompletion for this // See if we can split the assistant actions into their own model and use the model param as well // That way we don't get unwanted completions when the assistant completions are shown if (!assistant->actions().isEmpty()) { // HACK: Internal, unexposed API -- do NOT git blame this line QMetaObject::invokeMethod(editorView, "userInvokedCompletion"); } } } const QMap< IToolViewFactory*, Sublime::ToolDocument* >& UiController::factoryDocuments() const { return d->factoryDocuments; } QWidget* UiController::activeToolViewActionListener() const { return d->activeActionListener; } QList UiController::allAreas() const { return Sublime::Controller::allAreas(); } } #include "uicontroller.moc" #include "moc_uicontroller.cpp" diff --git a/shell/uicontroller.h b/shell/uicontroller.h index 3d2e8dc9f8..06b4104617 100644 --- a/shell/uicontroller.h +++ b/shell/uicontroller.h @@ -1,133 +1,131 @@ /*************************************************************************** * Copyright 2007 Alexander Dymo * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef KDEVPLATFORM_UICONTROLLER_H #define KDEVPLATFORM_UICONTROLLER_H #include "shellexport.h" #include #include #include #include class QListWidgetItem; namespace Sublime { class AreaIndex; class ToolDocument; } namespace KDevelop { class Core; class MainWindow; class KDEVPLATFORMSHELL_EXPORT UiController: public Sublime::Controller, public IUiController { Q_OBJECT public: explicit UiController(Core *core); virtual ~UiController(); /** @return area for currently active sublime mainwindow or 0 if no sublime mainwindow is active.*/ virtual Sublime::Area *activeArea() override; /** @return active sublime mainwindow or 0 if no such mainwindow is active.*/ virtual Sublime::MainWindow *activeSublimeWindow(); /** @return active sublime mainwindow or 0 if no such mainwindow is active.*/ virtual KParts::MainWindow *activeMainWindow() override; /** @return default main window - the main window for default area in the shell. No guarantee is given that it always exists so this method may return 0.*/ MainWindow *defaultMainWindow(); virtual void switchToArea(const QString &areaName, SwitchMode switchMode) override; virtual void addToolView(const QString &name, IToolViewFactory *factory) override; virtual void removeToolView(IToolViewFactory *factory) override; virtual QWidget* findToolView(const QString& name, IToolViewFactory *factory, FindFlags flags) override; virtual void raiseToolView(QWidget* toolViewWidget) override; void selectNewToolViewToAdd(MainWindow *mw); void initialize(); void cleanup(); void showSettingsDialog(); Sublime::Controller* controller() override; void mainWindowAdded(Sublime::MainWindow* mainWindow); void saveAllAreas(KSharedConfigPtr config); void saveArea(Sublime::Area* area, KConfigGroup & group); void loadAllAreas(KSharedConfigPtr config); void loadArea(Sublime::Area* area, const KConfigGroup & group); /*! @p status must implement KDevelop::IStatus */ virtual void registerStatus(QObject* status) override; - virtual void popUpAssistant(const KDevelop::IAssistant::Ptr& assistant) override; + virtual void showAssistant(const KDevelop::IAssistant::Ptr& assistant) override; virtual void showErrorMessage(const QString& message, int timeout) override; /// Returns list of available view factories together with their ToolDocuments. /// @see addToolView(), removeToolView(), findToolView() const QMap& factoryDocuments() const; /// Adds a tool view in the active area to the dock area @p area. /// @see activeArea() void addToolViewToDockArea(KDevelop::IToolViewFactory* factory, Qt::DockWidgetArea area); bool toolViewPresent(Sublime::ToolDocument* doc, Sublime::Area* area); virtual QWidget* activeToolViewActionListener() const override; virtual QList allAreas() const override; public Q_SLOTS: void raiseToolView(Sublime::View * view); private Q_SLOTS: void addNewToolView(MainWindow* mw, QListWidgetItem* item); void slotAreaChanged(Sublime::Area* area); void slotActiveToolViewChanged(Sublime::View* view); - void hideAssistant() override; - private: void addToolViewIfWanted(IToolViewFactory* factory, Sublime::ToolDocument* doc, Sublime::Area* area); Sublime::View* addToolViewToArea(IToolViewFactory* factory, Sublime::ToolDocument* doc, Sublime::Area* area, Sublime::Position p=Sublime::AllPositions); void setupActions(); class UiControllerPrivate* const d; friend class UiControllerPrivate; Q_PRIVATE_SLOT(d, void widgetChanged(QWidget*,QWidget*)) }; } #endif