forked from qt-creator/qt-creator
ProjectExplorer: Use Widget as project delegate in ProjectWelcomePage
Less manual pixel calculation, more Layouting. Change-Id: I33e0ce82a83d7561c73e9b6a92e349354d8f758b Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -20,6 +20,7 @@
|
|||||||
#include <coreplugin/welcomepagehelper.h>
|
#include <coreplugin/welcomepagehelper.h>
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
|
#include <utils/elidinglabel.h>
|
||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
#include <utils/icon.h>
|
#include <utils/icon.h>
|
||||||
#include <utils/layoutbuilder.h>
|
#include <utils/layoutbuilder.h>
|
||||||
@@ -630,104 +631,128 @@ private:
|
|||||||
mutable QRect m_activeActionRects[3];
|
mutable QRect m_activeActionRects[3];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ProjectItemWidget final : public QWidget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ProjectItemWidget(QWidget *parent = nullptr)
|
||||||
|
: QWidget(parent)
|
||||||
|
{
|
||||||
|
m_shortcut = new QLabel;
|
||||||
|
applyTf(m_shortcut, shortcutNumberTF, false);
|
||||||
|
const int shortcutWidth = HPaddingXs + shortcutNumberWidth + HGapXs;
|
||||||
|
m_shortcut->setMinimumWidth(shortcutWidth);
|
||||||
|
|
||||||
|
const QPixmap icon = pixmap("project", Theme::Token_Text_Muted);
|
||||||
|
const QSize iconS = icon.deviceIndependentSize().toSize();
|
||||||
|
auto iconLabel = new QLabel;
|
||||||
|
iconLabel->setFixedSize(iconS);
|
||||||
|
iconLabel->setPixmap(icon);
|
||||||
|
|
||||||
|
m_projectName = new ElidingLabel;
|
||||||
|
applyTf(m_projectName, projectNameTF);
|
||||||
|
m_projectName->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
|
||||||
|
|
||||||
|
m_projectPath = new ElidingLabel;
|
||||||
|
applyTf(m_projectPath, projectPathTF);
|
||||||
|
m_projectPath->setElideMode(Qt::ElideMiddle);
|
||||||
|
m_projectPath->setSizePolicy(m_projectName->sizePolicy());
|
||||||
|
|
||||||
|
using namespace Layouting;
|
||||||
|
Row {
|
||||||
|
m_shortcut,
|
||||||
|
iconLabel,
|
||||||
|
Space(HGapXs),
|
||||||
|
Column {
|
||||||
|
m_projectName,
|
||||||
|
m_projectPath,
|
||||||
|
customMargins(0, VPaddingXs, 0, VPaddingXs),
|
||||||
|
spacing(VGapXs),
|
||||||
|
},
|
||||||
|
customMargins(0, 0, HPaddingXs, 0),
|
||||||
|
spacing(0),
|
||||||
|
}.attachTo(this);
|
||||||
|
|
||||||
|
setAutoFillBackground(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setData(const QModelIndex &index)
|
||||||
|
{
|
||||||
|
const int row = index.row() + 1;
|
||||||
|
m_shortcut->setText(row <= 9 ? QString::number(row) : QString());
|
||||||
|
|
||||||
|
const QString projectName = index.data(Qt::DisplayRole).toString();
|
||||||
|
m_projectName->setText(projectName);
|
||||||
|
|
||||||
|
const FilePath projectPath =
|
||||||
|
FilePath::fromVariant(index.data(ProjectModel::FilePathRole));
|
||||||
|
const QString displayPath =
|
||||||
|
projectPath.osType() == OsTypeWindows ? projectPath.displayName()
|
||||||
|
: projectPath.withTildeHomePath();
|
||||||
|
m_projectPath->setText(displayPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index)
|
||||||
|
{
|
||||||
|
const bool hovered = option.widget->isActiveWindow()
|
||||||
|
&& option.state & QStyle::State_MouseOver;
|
||||||
|
|
||||||
|
const QRect bgRGlobal = option.rect.adjusted(0, 0, -s(HPaddingXs), -itemSpacing());
|
||||||
|
const QRect bgR = bgRGlobal.translated(-option.rect.topLeft());
|
||||||
|
|
||||||
|
QFont projectNameFont = m_projectName->font();
|
||||||
|
projectNameFont.setUnderline(hovered);
|
||||||
|
m_projectName->setFont(projectNameFont);
|
||||||
|
setFixedWidth(bgR.width());
|
||||||
|
|
||||||
|
painter->save();
|
||||||
|
painter->setRenderHint(QPainter::Antialiasing);
|
||||||
|
painter->translate(bgRGlobal.topLeft());
|
||||||
|
|
||||||
|
drawBackgroundRect(painter, bgR, hovered);
|
||||||
|
render(painter, bgR.topLeft(), {}, QWidget::DrawChildren);
|
||||||
|
|
||||||
|
painter->restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QLabel *m_shortcut;
|
||||||
|
ElidingLabel *m_projectName;
|
||||||
|
ElidingLabel *m_projectPath;
|
||||||
|
};
|
||||||
|
|
||||||
class ProjectDelegate : public BaseDelegate
|
class ProjectDelegate : public BaseDelegate
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
explicit ProjectDelegate()
|
||||||
|
: BaseDelegate()
|
||||||
|
{}
|
||||||
|
|
||||||
|
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index)
|
||||||
|
const override
|
||||||
|
{
|
||||||
|
m_itemWidget.setData(index);
|
||||||
|
m_itemWidget.paint(painter, option, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize sizeHint([[maybe_unused]] const QStyleOptionViewItem &option,
|
||||||
|
const QModelIndex &index) const override
|
||||||
|
{
|
||||||
|
return {-1, m_itemWidget.minimumSizeHint().height() + itemSpacing()};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
QString entryType() override
|
QString entryType() override
|
||||||
{
|
{
|
||||||
return Tr::tr("project", "Appears in \"Open project <name>\"");
|
return Tr::tr("project", "Appears in \"Open project <name>\"");
|
||||||
}
|
}
|
||||||
int shortcutRole() const override { return ProjectModel::ShortcutRole; }
|
|
||||||
|
|
||||||
public:
|
int shortcutRole() const override
|
||||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &idx) const final
|
|
||||||
{
|
{
|
||||||
// visible on with Icon() Extra margin right of project item
|
return ProjectModel::ShortcutRole;
|
||||||
// | |
|
|
||||||
// +--------+-------+ +------+-----+
|
|
||||||
// | | | |
|
|
||||||
//
|
|
||||||
// +------------+--------+--------+------+---------+-------------+------------+------------+
|
|
||||||
// | | | | | | (VPaddingXs)| | |
|
|
||||||
// | | | | | +-------------+ | |
|
|
||||||
// | | | | | |<projectName>| | |
|
|
||||||
// | | | | | +-------------+ | |
|
|
||||||
// |(HPaddingXs)|<number>|(HGapXs)|<icon>|(HGapXxs)| (VGapXs) |(HPaddingXs)|(HPaddingXs)|
|
|
||||||
// | |(w:6) | | | +-------------+ | |
|
|
||||||
// | | | | | |<projectPath>| | |
|
|
||||||
// | | | | | +-------------+ | |
|
|
||||||
// | | | | | | (VPaddingXs)| | |
|
|
||||||
// +------------+--------+--------+------+---------+-------------+------------+------------+ --+
|
|
||||||
// | (VGapL) | +-- Gap between project items
|
|
||||||
// +---------------------------------------------------------------------------------------+ --+
|
|
||||||
|
|
||||||
const bool hovered = option.widget->isActiveWindow()
|
|
||||||
&& option.state & QStyle::State_MouseOver;
|
|
||||||
|
|
||||||
const QRect bgR = option.rect.adjusted(0, 0, -s(HPaddingXs), -itemSpacing());
|
|
||||||
|
|
||||||
static const QPixmap icon = pixmap("project", Theme::Token_Text_Muted);
|
|
||||||
const QSize iconS = icon.deviceIndependentSize().toSize();
|
|
||||||
|
|
||||||
const int x = bgR.x();
|
|
||||||
const int numberX = x + s(HPaddingXs);
|
|
||||||
const int iconX = numberX + shortcutNumberWidth + s(HGapXs);
|
|
||||||
const int iconWidth = iconS.width();
|
|
||||||
const int textX = withIcon() ? iconX + iconWidth + s(HGapXs) : iconX;
|
|
||||||
const int textWidth = bgR.width() - s(HPaddingXs) - textX;
|
|
||||||
|
|
||||||
const int y = bgR.y();
|
|
||||||
const int iconHeight = iconS.height();
|
|
||||||
const int iconY = y + (bgR.height() - iconHeight) / 2;
|
|
||||||
const int projectNameY = y + s(VPaddingXs);
|
|
||||||
const QRect projectNameR(textX, projectNameY, textWidth, projectNameTF.lineHeight());
|
|
||||||
const int projectPathY = projectNameY + projectNameR.height() + s(VGapXs);
|
|
||||||
const QRect projectPathR(textX, projectPathY, textWidth, projectPathTF.lineHeight());
|
|
||||||
|
|
||||||
QTC_CHECK(option.rect.bottom() == projectPathR.bottom() + s(VPaddingXs) + itemSpacing());
|
|
||||||
|
|
||||||
{
|
|
||||||
drawBackgroundRect(painter, bgR, hovered);
|
|
||||||
}
|
|
||||||
if (idx.row() < 9) {
|
|
||||||
painter->setPen(shortcutNumberTF.color());
|
|
||||||
painter->setFont(shortcutNumberTF.font());
|
|
||||||
const QRect numberR(numberX, y, shortcutNumberWidth, bgR.height());
|
|
||||||
const QString numberString = QString::number(idx.row() + 1);
|
|
||||||
painter->drawText(numberR, shortcutNumberTF.drawTextFlags, numberString);
|
|
||||||
}
|
|
||||||
if (withIcon()) {
|
|
||||||
painter->drawPixmap(iconX, iconY, icon);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
painter->setPen(projectNameTF.color());
|
|
||||||
painter->setFont(projectNameTF.font(hovered));
|
|
||||||
const QString projectName = idx.data(Qt::DisplayRole).toString();
|
|
||||||
const QString projectNameElided =
|
|
||||||
painter->fontMetrics().elidedText(projectName, Qt::ElideRight, textWidth);
|
|
||||||
painter->drawText(projectNameR, projectNameTF.drawTextFlags, projectNameElided);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
painter->setPen(projectPathTF.color());
|
|
||||||
painter->setFont(projectPathTF.font());
|
|
||||||
const FilePath projectPath =
|
|
||||||
FilePath::fromVariant(idx.data(ProjectModel::FilePathRole));
|
|
||||||
const QString displayPath =
|
|
||||||
projectPath.osType() == OsTypeWindows ? projectPath.displayName()
|
|
||||||
: projectPath.withTildeHomePath();
|
|
||||||
const QString displayPathElided =
|
|
||||||
painter->fontMetrics().elidedText(displayPath, Qt::ElideMiddle, textWidth);
|
|
||||||
painter->drawText(projectPathR, projectPathTF.drawTextFlags, displayPathElided);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QSize sizeHint([[maybe_unused]] const QStyleOptionViewItem &option,
|
|
||||||
[[maybe_unused]] const QModelIndex &idx) const override
|
|
||||||
{
|
|
||||||
return QSize(-1, itemHeight() + itemSpacing());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool editorEvent(QEvent *ev, QAbstractItemModel *model,
|
bool editorEvent(QEvent *ev, QAbstractItemModel *model,
|
||||||
const QStyleOptionViewItem &, const QModelIndex &idx) final
|
const QStyleOptionViewItem &, const QModelIndex &idx) final
|
||||||
{
|
{
|
||||||
if (ev->type() == QEvent::MouseButtonRelease) {
|
if (ev->type() == QEvent::MouseButtonRelease) {
|
||||||
const QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(ev);
|
const QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(ev);
|
||||||
@@ -762,16 +787,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static int itemHeight()
|
mutable ProjectItemWidget m_itemWidget;
|
||||||
{
|
|
||||||
const int height =
|
|
||||||
s(VPaddingXs)
|
|
||||||
+ projectNameTF.lineHeight()
|
|
||||||
+ s(VGapXs)
|
|
||||||
+ projectPathTF.lineHeight()
|
|
||||||
+ s(VPaddingXs);
|
|
||||||
return height;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class TreeView : public QTreeView
|
class TreeView : public QTreeView
|
||||||
|
Reference in New Issue
Block a user