Add general UI introduction

Adds a Help > UI Introduction, which highlights basic aspects of the Qt
Creator UI.

The pages to show are defined in a format that can later move into a
text based configuration file like JSON. It specifies an optional object
name of a widget to highlight, a title, a brief description and a longer
description (potentially with tables, lists and other simple HTML).

Fixes: QTCREATORBUG-21585
Change-Id: Idb64c87e1d752bc24437588278093a96be0eeddb
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
Reviewed-by: Tobias Hunger <tobias.hunger@qt.io>
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
This commit is contained in:
Eike Ziller
2018-12-10 17:10:17 +01:00
parent 5330502978
commit 8770d83fc8
16 changed files with 556 additions and 6 deletions

View File

@@ -277,7 +277,7 @@ QMessageBox::StandardButton CheckableMessageBox::dialogButtonBoxToMessageBoxButt
return static_cast<QMessageBox::StandardButton>(int(db));
}
bool askAgain(QSettings *settings, const QString &settingsSubKey)
bool CheckableMessageBox::shouldAskAgain(QSettings *settings, const QString &settingsSubKey)
{
if (QTC_GUARD(settings)) {
settings->beginGroup(QLatin1String(kDoNotAskAgainKey));
@@ -309,7 +309,7 @@ void initDoNotAskAgainMessageBox(CheckableMessageBox &messageBox, const QString
messageBox.setDefaultButton(defaultButton);
}
void doNotAskAgain(QSettings *settings, const QString &settingsSubKey)
void CheckableMessageBox::doNotAskAgain(QSettings *settings, const QString &settingsSubKey)
{
if (!settings)
return;
@@ -337,7 +337,7 @@ CheckableMessageBox::doNotAskAgainQuestion(QWidget *parent, const QString &title
QDialogButtonBox::StandardButton acceptButton)
{
if (!askAgain(settings, settingsSubKey))
if (!shouldAskAgain(settings, settingsSubKey))
return acceptButton;
CheckableMessageBox messageBox(parent);
@@ -365,7 +365,7 @@ CheckableMessageBox::doNotShowAgainInformation(QWidget *parent, const QString &t
QDialogButtonBox::StandardButton defaultButton)
{
if (!askAgain(settings, settingsSubKey))
if (!shouldAskAgain(settings, settingsSubKey))
return defaultButton;
CheckableMessageBox messageBox(parent);

View File

@@ -117,6 +117,10 @@ public:
QAbstractButton *clickedButton() const;
QDialogButtonBox::StandardButton clickedStandardButton() const;
// check and set "ask again" status
static bool shouldAskAgain(QSettings *settings, const QString &settingsSubKey);
static void doNotAskAgain(QSettings *settings, const QString &settingsSubKey);
// Conversion convenience
static QMessageBox::StandardButton dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton);
static void resetAllDoNotAskAgainQuestions(QSettings *settings);

View File

@@ -373,6 +373,8 @@ void FancyActionBar::addProjectSelector(QAction *action)
void FancyActionBar::insertAction(int index, QAction *action)
{
auto *button = new FancyToolButton(action, this);
if (!action->objectName().isEmpty())
button->setObjectName(action->objectName() + ".Button"); // used for UI introduction
button->setIconsOnly(m_iconsOnly);
m_actionsLayout->insertWidget(index, button);
}

View File

@@ -458,6 +458,7 @@ FancyTabWidget::FancyTabWidget(QWidget *parent)
: QWidget(parent)
{
m_tabBar = new FancyTabBar(this);
m_tabBar->setObjectName("ModeSelector"); // used for UI introduction
m_selectionWidget = new QWidget(this);
auto selectionLayout = new QVBoxLayout;
@@ -473,7 +474,8 @@ FancyTabWidget::FancyTabWidget(QWidget *parent)
layout->addWidget(fancyButton);
selectionLayout->addWidget(bar);
selectionLayout->addWidget(m_tabBar, 1);
selectionLayout->addWidget(m_tabBar);
selectionLayout->addStretch(1);
m_selectionWidget->setLayout(selectionLayout);
m_selectionWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
@@ -551,6 +553,7 @@ void FancyTabWidget::paintEvent(QPaintEvent *event)
const QRectF boderRect = QRectF(rect).adjusted(0.5, 0.5, -0.5, -0.5);
if (creatorTheme()->flag(Theme::FlatToolBars)) {
painter.fillRect(event->rect(), StyleHelper::baseColor());
painter.setPen(StyleHelper::toolBarBorderColor());
painter.drawLine(boderRect.topRight(), boderRect.bottomRight());
} else {

View File

@@ -123,6 +123,7 @@ void Locator::initialize()
mtools->addAction(cmd);
auto locatorWidget = LocatorManager::createLocatorInputWidget(ICore::mainWindow());
locatorWidget->setObjectName("LocatorInput"); // used for UI introduction
StatusBarManager::addStatusBarWidget(locatorWidget, StatusBarManager::First,
Context("LocatorWidget"));
connect(ICore::instance(), &ICore::saveSettingsRequested, this, &Locator::saveSettings);

View File

@@ -232,6 +232,7 @@ OutputPaneManager::OutputPaneManager(QWidget *parent) :
setLayout(mainlayout);
m_buttonsWidget = new QWidget;
m_buttonsWidget->setObjectName("OutputPaneButtons"); // used for UI introduction
m_buttonsWidget->setLayout(new QHBoxLayout);
m_buttonsWidget->layout()->setContentsMargins(5,0,0,0);
m_buttonsWidget->layout()->setSpacing(

View File

@@ -300,6 +300,7 @@ void ProgressManagerPrivate::init()
readSettings();
m_statusBarWidget = new QWidget;
m_statusBarWidget->setObjectName("ProgressInfo"); // used for UI introduction
auto layout = new QHBoxLayout(m_statusBarWidget);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);

View File

@@ -1120,6 +1120,7 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments,
m_visibleStartAction.setAttribute(ProxyAction::UpdateIcon);
m_visibleStartAction.setAction(&m_startAction);
m_visibleStartAction.setObjectName("Debug"); // used for UI introduction
ModeManager::addAction(&m_visibleStartAction, Constants::P_ACTION_DEBUG);
m_undisturbableAction.setIcon(interruptIcon(false));

View File

@@ -984,6 +984,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
// Add to mode bar
dd->m_modeBarBuildAction = new Utils::ProxyAction(this);
dd->m_modeBarBuildAction->setObjectName("Build"); // used for UI introduction
dd->m_modeBarBuildAction->initialize(cmd->action());
dd->m_modeBarBuildAction->setAttribute(Utils::ProxyAction::UpdateText);
dd->m_modeBarBuildAction->setAction(cmd->action());
@@ -1027,6 +1028,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+R")));
mbuild->addAction(cmd, Constants::G_BUILD_RUN);
cmd->action()->setObjectName("Run"); // used for UI introduction
ModeManager::addAction(cmd->action(), Constants::P_ACTION_RUN);
// Run without deployment action
@@ -1213,6 +1215,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
// target selector
dd->m_projectSelectorAction = new QAction(this);
dd->m_projectSelectorAction->setObjectName("KitSelector"); // used for UI introduction
dd->m_projectSelectorAction->setCheckable(true);
dd->m_projectSelectorAction->setEnabled(false);
QWidget *mainWindow = ICore::mainWindow();

Binary file not shown.

After

Width:  |  Height:  |  Size: 852 B

View File

@@ -0,0 +1,399 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "introductionwidget.h"
#include <utils/algorithm.h>
#include <utils/checkablemessagebox.h>
#include <utils/qtcassert.h>
#include <utils/stylehelper.h>
#include <QEvent>
#include <QKeyEvent>
#include <QLabel>
#include <QPainter>
#include <QPushButton>
#include <QVBoxLayout>
using namespace Utils;
const char kTakeTourSetting[] = "TakeUITour";
namespace Welcome {
namespace Internal {
void IntroductionWidget::askUserAboutIntroduction(QWidget *parent, QSettings *settings)
{
if (!CheckableMessageBox::shouldAskAgain(settings, kTakeTourSetting))
return;
auto messageBox = new CheckableMessageBox(parent);
messageBox->setWindowTitle(tr("Take a UI Tour"));
messageBox->setIconPixmap(QMessageBox::standardIcon(QMessageBox::Question));
messageBox->setText(
tr("Do you want to take a quick UI tour? This shows where the most important user "
"interface elements are, and how they are used, and will only take a minute. You can "
"also take the tour later by selecting Help > UI Tour."));
messageBox->setCheckBoxVisible(true);
messageBox->setCheckBoxText(CheckableMessageBox::msgDoNotAskAgain());
messageBox->setChecked(true);
messageBox->setStandardButtons(QDialogButtonBox::Cancel);
QPushButton *tourButton = messageBox->addButton(tr("Take UI Tour"), QDialogButtonBox::AcceptRole);
connect(messageBox, &QDialog::finished, parent, [parent, settings, messageBox, tourButton]() {
if (messageBox->isChecked())
CheckableMessageBox::doNotAskAgain(settings, kTakeTourSetting);
if (messageBox->clickedButton() == tourButton) {
auto intro = new IntroductionWidget(parent);
intro->show();
}
messageBox->deleteLater();
});
messageBox->show();
}
IntroductionWidget::IntroductionWidget(QWidget *parent)
: QWidget(parent),
m_borderImage(std::make_unique<QImage>(":/welcome/images/border.png"))
{
setFocusPolicy(Qt::StrongFocus);
setFocus();
parent->installEventFilter(this);
QPalette p = palette();
p.setColor(QPalette::Foreground, QColor(220, 220, 220));
setPalette(p);
m_textWidget = new QWidget(this);
auto layout = new QVBoxLayout;
m_textWidget->setLayout(layout);
m_stepText = new QLabel(this);
m_stepText->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_stepText->setWordWrap(true);
m_stepText->setTextFormat(Qt::RichText);
// why is palette not inherited???
m_stepText->setPalette(palette());
m_stepText->setOpenExternalLinks(true);
m_stepText->installEventFilter(this);
layout->addWidget(m_stepText);
m_continueLabel = new QLabel(this);
m_continueLabel->setAlignment(Qt::AlignCenter);
m_continueLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
m_continueLabel->setWordWrap(true);
auto fnt = font();
fnt.setPointSizeF(fnt.pointSizeF() * 1.5);
m_continueLabel->setFont(fnt);
m_continueLabel->setPalette(palette());
layout->addWidget(m_continueLabel);
m_bodyCss = "font-size: 16px;";
m_items = {
{"ModeSelector",
tr("Mode Selector"),
tr("Select different modes depending on the task at hand."),
tr("<p style=\"margin-top: 30px\"><table>"
"<tr><td style=\"padding-right: 20px\">Welcome:</td><td>Open examples, tutorials, and "
"recent sessions and projects.</td></tr>"
"<tr><td>Edit:</td><td>Work with code and navigate your project.</td></tr>"
"<tr><td>Design:</td><td>Work with UI designs for Qt Widgets or Qt Quick.</td></tr>"
"<tr><td>Debug:</td><td>Analyze your application with a debugger or other "
"analyzer.</td></tr>"
"<tr><td>Projects:</td><td>Manage project settings.</td></tr>"
"<tr><td>Help:</td><td>Browse the help database.</td></tr>"
"</table></p>")},
{"KitSelector.Button",
tr("Kit Selector"),
tr("Select the active project or project configuration."),
""},
{"Run.Button",
tr("Run Button"),
tr("Run the active project. By default this builds the project first."),
""},
{"Debug.Button", tr("Debug Button"), tr("Run the active project in a debugger."), ""},
{"Build.Button", tr("Build Button"), tr("Build the active project."), ""},
{"LocatorInput",
tr("Locator"),
tr("Type here to open a file from any open project."),
tr("Or:"
"<ul>"
"<li>type <code>c&lt;space&gt;&lt;pattern&gt;</code> to jump to a class definition</li>"
"<li>type <code>f&lt;space&gt;&lt;pattern&gt;</code> to open a file from the file "
"system</li>"
"<li>click on the magnifier icon for a complete list of possible options</li>"
"</ul>")},
{"OutputPaneButtons",
tr("Output Panes"),
tr("Find compile and application output here, "
"as well as a list of configuration and build issues, "
"and the panel for global searches."),
""},
{"ProgressInfo",
tr("Progress Indicator"),
tr("Progress information about running tasks is shown here."),
""},
{{},
tr("Escape to Editor"),
tr("Pressing the Escape key brings you back to the editor. Press it "
"multiple times to also hide output panes and context help, giving the editor more "
"space."),
""},
{{},
tr("The End"),
tr("You have now completed the UI tour. To learn more about the highlighted "
"controls, see <a style=\"color: #41CD52\" "
"href=\"qthelp://org.qt-project.qtcreator/doc/creator-quick-tour.html\">User "
"Interface</a>."),
""}};
setStep(0);
resizeToParent();
}
bool IntroductionWidget::event(QEvent *e)
{
if (e->type() == QEvent::ShortcutOverride) {
e->accept();
return true;
}
return QWidget::event(e);
}
bool IntroductionWidget::eventFilter(QObject *obj, QEvent *ev)
{
if (obj == parent() && ev->type() == QEvent::Resize)
resizeToParent();
else if (obj == m_stepText && ev->type() == QEvent::MouseButtonRelease)
step();
return QWidget::eventFilter(obj, ev);
}
const int SPOTLIGHTMARGIN = 18;
const int POINTER_SIZE = 30;
const int POINTER_WIDTH = 3;
static int margin(const QRect &small, const QRect &big, Qt::Alignment side)
{
switch (side) {
case Qt::AlignRight:
return qMax(0, big.right() - small.right());
case Qt::AlignTop:
return qMax(0, small.top() - big.top());
case Qt::AlignBottom:
return qMax(0, big.bottom() - small.bottom());
case Qt::AlignLeft:
return qMax(0, small.x() - big.x());
default:
QTC_ASSERT(false, return 0);
}
}
static int oppositeMargin(const QRect &small, const QRect &big, Qt::Alignment side)
{
switch (side) {
case Qt::AlignRight:
return margin(small, big, Qt::AlignLeft);
case Qt::AlignTop:
return margin(small, big, Qt::AlignBottom);
case Qt::AlignBottom:
return margin(small, big, Qt::AlignTop);
case Qt::AlignLeft:
return margin(small, big, Qt::AlignRight);
default:
QTC_ASSERT(false, return 100000);
}
}
static const QVector<QPolygonF> pointerPolygon(const QRect &anchorRect, const QRect &fullRect)
{
// Put the arrow opposite to the smallest margin,
// with priority right, top, bottom, left.
// Not very sophisticated but sufficient for current use cases.
QVector<Qt::Alignment> alignments{Qt::AlignRight, Qt::AlignTop, Qt::AlignBottom, Qt::AlignLeft};
Utils::sort(alignments, [anchorRect, fullRect](Qt::Alignment a, Qt::Alignment b) {
return oppositeMargin(anchorRect, fullRect, a) < oppositeMargin(anchorRect, fullRect, b);
});
const auto alignIt = std::find_if(alignments.cbegin(),
alignments.cend(),
[anchorRect, fullRect](Qt::Alignment align) {
return margin(anchorRect, fullRect, align) > POINTER_SIZE;
});
if (alignIt == alignments.cend())
return {{}}; // no side with sufficient space found
const qreal arrowHeadWidth = POINTER_SIZE/3.;
if (*alignIt == Qt::AlignRight) {
const qreal middleY = anchorRect.center().y();
const qreal startX = anchorRect.right() + POINTER_SIZE;
const qreal endX = anchorRect.right() + POINTER_WIDTH;
return {{QVector<QPointF>{{startX, middleY}, {endX, middleY}}},
QVector<QPointF>{{endX + arrowHeadWidth, middleY - arrowHeadWidth},
{endX, middleY},
{endX + arrowHeadWidth, middleY + arrowHeadWidth}}};
}
if (*alignIt == Qt::AlignTop) {
const qreal middleX = anchorRect.center().x();
const qreal startY = anchorRect.y() - POINTER_SIZE;
const qreal endY = anchorRect.y() - POINTER_WIDTH;
return {{QVector<QPointF>{{middleX, startY}, {middleX, endY}}},
QVector<QPointF>{{middleX - arrowHeadWidth, endY - arrowHeadWidth},
{middleX, endY},
{middleX + arrowHeadWidth, endY - arrowHeadWidth}}};
}
if (*alignIt == Qt::AlignBottom) {
const qreal middleX = anchorRect.center().x();
const qreal startY = anchorRect.y() + POINTER_WIDTH;
const qreal endY = anchorRect.y() + POINTER_SIZE;
return {{QVector<QPointF>{{middleX, startY}, {middleX, endY}}},
QVector<QPointF>{{middleX - arrowHeadWidth, endY + arrowHeadWidth},
{middleX, endY},
{middleX + arrowHeadWidth, endY + arrowHeadWidth}}};
}
// Qt::AlignLeft
const qreal middleY = anchorRect.center().y();
const qreal startX = anchorRect.x() - POINTER_WIDTH;
const qreal endX = anchorRect.x() - POINTER_SIZE;
return {{QVector<QPointF>{{startX, middleY}, {endX, middleY}}},
QVector<QPointF>{{endX - arrowHeadWidth, middleY - arrowHeadWidth},
{endX, middleY},
{endX - arrowHeadWidth, middleY + arrowHeadWidth}}};
}
void IntroductionWidget::paintEvent(QPaintEvent *)
{
QPainter p(this);
p.setOpacity(.87);
const QColor backgroundColor = Qt::black;
if (m_stepPointerAnchor) {
const QPoint anchorPos = m_stepPointerAnchor->mapTo(parentWidget(), {0, 0});
const QRect anchorRect(anchorPos, m_stepPointerAnchor->size());
const QRect spotlightRect = anchorRect.adjusted(-SPOTLIGHTMARGIN,
-SPOTLIGHTMARGIN,
SPOTLIGHTMARGIN,
SPOTLIGHTMARGIN);
// darken the background to create a spotlighted area
if (spotlightRect.left() > 0) {
p.fillRect(0, 0, spotlightRect.left(), height(), backgroundColor);
}
if (spotlightRect.top() > 0) {
p.fillRect(spotlightRect.left(),
0,
width() - spotlightRect.left(),
spotlightRect.top(),
backgroundColor);
}
if (spotlightRect.right() < width() - 1) {
p.fillRect(spotlightRect.right() + 1,
spotlightRect.top(),
width() - spotlightRect.right() - 1,
height() - spotlightRect.top(),
backgroundColor);
}
if (spotlightRect.bottom() < height() - 1) {
p.fillRect(spotlightRect.left(),
spotlightRect.bottom() + 1,
spotlightRect.width(),
height() - spotlightRect.bottom() - 1,
backgroundColor);
}
// smooth borders of the spotlighted area by gradients
StyleHelper::drawCornerImage(*m_borderImage,
&p,
spotlightRect,
SPOTLIGHTMARGIN,
SPOTLIGHTMARGIN,
SPOTLIGHTMARGIN,
SPOTLIGHTMARGIN);
// draw pointer
const QColor qtGreen(65, 205, 82);
p.setOpacity(1.);
p.setPen(QPen(QBrush(qtGreen),
POINTER_WIDTH,
Qt::SolidLine,
Qt::RoundCap,
Qt::MiterJoin));
p.setRenderHint(QPainter::Antialiasing);
for (const QPolygonF &poly : pointerPolygon(spotlightRect, rect()))
p.drawPolyline(poly);
} else {
p.fillRect(rect(), backgroundColor);
}
}
void IntroductionWidget::keyPressEvent(QKeyEvent *ke)
{
if (ke->key() == Qt::Key_Escape)
finish();
else if (ke->modifiers() == Qt::NoModifier)
step();
}
void IntroductionWidget::mouseReleaseEvent(QMouseEvent *me)
{
me->accept();
step();
}
void IntroductionWidget::finish()
{
hide();
deleteLater();
}
void IntroductionWidget::step()
{
if (m_step >= m_items.size() - 1)
finish();
else
setStep(m_step + 1);
}
void IntroductionWidget::setStep(uint index)
{
QTC_ASSERT(index < m_items.size(), return);
m_step = index;
m_continueLabel->setText(tr("UI Introduction %1/%2 >").arg(m_step + 1).arg(m_items.size()));
const Item &item = m_items.at(m_step);
m_stepText->setText("<html><body style=\"" + m_bodyCss + "\">" + "<h1>" + item.title
+ "</h1><p>" + item.brief + "</p>" + item.description + "</body></html>");
const QString anchorObjectName = m_items.at(m_step).pointerAnchorObjectName;
if (!anchorObjectName.isEmpty()) {
m_stepPointerAnchor = parentWidget()->findChild<QWidget *>(anchorObjectName);
QTC_CHECK(m_stepPointerAnchor);
} else {
m_stepPointerAnchor.clear();
}
update();
}
void IntroductionWidget::resizeToParent()
{
QTC_ASSERT(parentWidget(), return);
setGeometry(QRect(QPoint(0, 0), parentWidget()->size()));
m_textWidget->setGeometry(QRect(width()/4, height()/4, width()/2, height()/2));
}
} // namespace Internal
} // namespace Welcome

View File

@@ -0,0 +1,82 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <QImage>
#include <QPointer>
#include <QWidget>
#include <memory>
QT_BEGIN_NAMESPACE
class QLabel;
class QSettings;
QT_END_NAMESPACE
namespace Welcome {
namespace Internal {
struct Item
{
QString pointerAnchorObjectName;
QString title;
QString brief;
QString description;
};
class IntroductionWidget : public QWidget
{
Q_OBJECT
public:
explicit IntroductionWidget(QWidget *parent = nullptr);
static void askUserAboutIntroduction(QWidget *parent, QSettings *settings);
protected:
bool event(QEvent *e) override;
bool eventFilter(QObject *obj, QEvent *ev) override;
void paintEvent(QPaintEvent *ev) override;
void keyPressEvent(QKeyEvent *ke) override;
void mouseReleaseEvent(QMouseEvent *me) override;
private:
void finish();
void step();
void setStep(uint index);
void resizeToParent();
QWidget *m_textWidget;
QLabel *m_stepText;
QLabel *m_continueLabel;
std::unique_ptr<QImage> m_borderImage;
QString m_bodyCss;
std::vector<Item> m_items;
QPointer<QWidget> m_stepPointerAnchor;
uint m_step = 0;
};
} // namespace Internal
} // namespace Welcome

View File

@@ -1,8 +1,12 @@
include(../../qtcreatorplugin.pri)
SOURCES += welcomeplugin.cpp
SOURCES += welcomeplugin.cpp \
introductionwidget.cpp
DEFINES += WELCOME_LIBRARY
RESOURCES += welcome.qrc
HEADERS += \
introductionwidget.h

View File

@@ -22,5 +22,6 @@
<file>images/new@2x.png</file>
<file>images/expandarrow.png</file>
<file>images/expandarrow@2x.png</file>
<file>images/border.png</file>
</qresource>
</RCC>

View File

@@ -23,11 +23,14 @@
**
****************************************************************************/
#include "introductionwidget.h"
#include <extensionsystem/iplugin.h>
#include <extensionsystem/pluginmanager.h>
#include <app/app_version.h>
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/coreconstants.h>
@@ -139,6 +142,18 @@ public:
{
m_welcomeMode->initPlugins();
ModeManager::activateMode(m_welcomeMode->id());
auto introAction = new QAction(tr("UI Tour"), this);
connect(introAction, &QAction::triggered, this, []() {
auto intro = new IntroductionWidget(ICore::mainWindow());
intro->show();
});
Command *cmd = ActionManager::registerAction(introAction, "Welcome.UITour");
ActionContainer *mhelp = ActionManager::actionContainer(Core::Constants::M_HELP);
if (QTC_GUARD(mhelp))
mhelp->addAction(cmd, Core::Constants::G_HELP_HELP);
connect(ICore::instance(), &ICore::coreOpened, this, []() {
IntroductionWidget::askUserAboutIntroduction(ICore::mainWindow(), ICore::settings());
}, Qt::QueuedConnection);
}
WelcomeMode *m_welcomeMode = nullptr;

View File

@@ -18,6 +18,22 @@
sodipodi:docname="qtcreatoricons.svg">
<defs
id="defs4">
<linearGradient
inkscape:collect="always"
id="linearGradient3970">
<stop
style="stop-color:#000000;stop-opacity:0"
offset="0"
id="stop3966" />
<stop
id="stop3974"
offset="0.35135135"
style="stop-color:#000000;stop-opacity:0" />
<stop
style="stop-color:#000000;stop-opacity:1"
offset="1"
id="stop3968" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient9821">
@@ -527,6 +543,16 @@
y1="109.69"
x2="-191.87"
y2="110.83" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3970"
id="radialGradient3972"
cx="548.5"
cy="91.5"
fx="548.5"
fy="91.5"
r="18.5"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
@@ -741,6 +767,13 @@
d="m 108.63,21.74 h -2 V 9 h 2 v 7.36 l 1.11,-0.11 2.13,-3.51 h 2.19 l -2.59,4.26 2.74,4.78 H 112 l -2.2,-3.78 -1.17,0.12 z m -6.41,0 V 9 h 2 v 12.74 z m -2.57,-6.22 v 4.1 a 0.86,0.86 0 0 0 0.2,0.58 1,1 0 0 0 0.59,0.25 l -0.06,1.49 A 3.8,3.8 0 0 1 98,21.28 6.91,6.91 0 0 1 95.1,21.94 c -1.79,0 -2.68,-1 -2.68,-2.86 a 2.44,2.44 0 0 1 0.73,-2 4,4 0 0 1 2.24,-0.74 l 2.32,-0.2 V 15.5 a 1.33,1.33 0 0 0 -0.31,-1 1.35,1.35 0 0 0 -0.93,-0.29 c -0.77,0 -1.73,0 -2.88,0.14 H 93.01 L 92.93,13 a 15.72,15.72 0 0 1 3.61,-0.46 3.31,3.31 0 0 1 2.38,0.71 3.05,3.05 0 0 1 0.73,2.27 z m -4,2.23 a 1.21,1.21 0 0 0 -1.24,1.35 c 0,0.83 0.37,1.24 1.1,1.24 a 7,7 0 0 0 1.91,-0.29 l 0.32,-0.11 V 17.55 Z M 84,11.2 V 9.41 h 9 v 1.79 h -3.49 v 10.54 h -2 V 11.2 Z m -4.62,3.22 h -2.49 v 4 a 4.12,4.12 0 0 0 0.17,1.46 c 0.1,0.24 0.38,0.36 0.82,0.36 l 1.48,-0.06 0.09,1.57 A 11,11 0 0 1 77.61,21.98 2.55,2.55 0 0 1 75.52,21.28 4.41,4.41 0 0 1 75,18.59 V 14.42 H 73.8 V 12.74 H 75 v -2.61 h 1.94 v 2.61 h 2.49 z m -11.94,7.52 c -1.87,0 -3.17,-0.5 -3.91,-1.51 a 8.16,8.16 0 0 1 -1.11,-4.78 8.43,8.43 0 0 1 1.13,-4.85 c 0.75,-1.06 2,-1.58 3.89,-1.58 1.89,0 3.15,0.52 3.89,1.57 a 8.41,8.41 0 0 1 1.12,4.85 11.08,11.08 0 0 1 -0.45,3.49 4,4 0 0 1 -1.5,2 l 1.5,2.47 -1.86,0.86 -1.6,-2.63 a 3.68,3.68 0 0 1 -1.1,0.11 z m -2.34,-2.8 a 3.13,3.13 0 0 0 4.68,0 7.43,7.43 0 0 0 0.6,-3.5 7.78,7.78 0 0 0 -0.62,-3.58 3,3 0 0 0 -4.64,0 7.56,7.56 0 0 0 -0.63,3.56 7.47,7.47 0 0 0 0.61,3.52 z"
inkscape:connector-curvature="0" />
</g>
<rect
style="fill:url(#radialGradient3972)"
id="src/plugins/welcome/images/border"
width="37"
height="37"
x="530"
y="73" />
</g>
<g
inkscape:groupmode="layer"

Before

Width:  |  Height:  |  Size: 353 KiB

After

Width:  |  Height:  |  Size: 354 KiB