250 lines
9.0 KiB
C++
250 lines
9.0 KiB
C++
/*
|
|
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
|
* running old operating systems and software designed for IBM
|
|
* PC systems and compatibles from 1981 through fairly recent
|
|
* system designs based on the PCI bus.
|
|
*
|
|
* This file is part of the 86Box distribution.
|
|
*
|
|
* 86Box VM manager list view delegate module
|
|
*
|
|
*
|
|
*
|
|
* Authors: cold-brewed
|
|
*
|
|
* Copyright 2024 cold-brewed
|
|
*/
|
|
|
|
|
|
#include <QApplication>
|
|
|
|
#include "qt_vmmanager_listviewdelegate.hpp"
|
|
#include "qt_vmmanager_model.hpp"
|
|
|
|
#ifdef Q_OS_WINDOWS
|
|
extern bool windows_is_light_theme();
|
|
#endif
|
|
|
|
|
|
// Thanks to scopchanov https://github.com/scopchanov/SO-MessageLog
|
|
// from https://stackoverflow.com/questions/53105343/is-it-possible-to-add-a-custom-widget-into-a-qlistview
|
|
|
|
|
|
VMManagerListViewDelegate::VMManagerListViewDelegate(QObject *parent)
|
|
: QStyledItemDelegate(parent),
|
|
m_ptr(new VMManagerListViewDelegateStyle)
|
|
{
|
|
}
|
|
|
|
VMManagerListViewDelegate::~VMManagerListViewDelegate()
|
|
= default;
|
|
|
|
void VMManagerListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
|
|
const QModelIndex &index) const {
|
|
bool windows_light_mode = true;
|
|
#ifdef Q_OS_WINDOWS
|
|
windows_light_mode = windows_is_light_theme();
|
|
#endif
|
|
QStyleOptionViewItem opt(option);
|
|
initStyleOption(&opt, index);
|
|
const QPalette &palette(opt.palette);
|
|
// opt.rect = opt.rect.adjusted(0, 0, 0, 20);
|
|
const QRect &rect(opt.rect);
|
|
const QRect &contentRect(rect.adjusted(m_ptr->margins.left(),
|
|
m_ptr->margins.top(),
|
|
-m_ptr->margins.right(),
|
|
-m_ptr->margins.bottom()));
|
|
|
|
// The status icon represents the current state of the vm. Initially set to a default state.
|
|
QIcon status_icon = QApplication::style()->standardIcon(QStyle::SP_MediaStop);
|
|
auto process_variant = index.data(VMManagerModel::Roles::ProcessStatus);
|
|
auto process_status = process_variant.value<VMManagerSystem::ProcessStatus>();
|
|
// The main icon, configurable. Falls back to default if it cannot be loaded.
|
|
auto customIcom = index.data(VMManagerModel::Roles::Icon).toString();
|
|
opt.icon = QIcon(":/settings/qt/icons/86Box-gray.ico");
|
|
if(!customIcom.isEmpty()) {
|
|
if (const auto customPixmap = QPixmap(customIcom); !customPixmap.isNull()) {
|
|
opt.icon = customPixmap;
|
|
}
|
|
}
|
|
// opt.icon = QIcon(":/settings/qt/icons/86Box-gray.ico");
|
|
|
|
// Set the status icon based on the process status
|
|
switch(process_status) {
|
|
case VMManagerSystem::ProcessStatus::Running:
|
|
status_icon = QIcon(":/menuicons/qt/icons/run.ico");
|
|
break;
|
|
case VMManagerSystem::ProcessStatus::Stopped:
|
|
status_icon = QIcon(":/menuicons/qt/icons/acpi_shutdown.ico");
|
|
break;
|
|
case VMManagerSystem::ProcessStatus::PausedWaiting:
|
|
case VMManagerSystem::ProcessStatus::RunningWaiting:
|
|
case VMManagerSystem::ProcessStatus::Paused:
|
|
status_icon = QIcon(":/menuicons/qt/icons/pause.ico");
|
|
break;
|
|
default:
|
|
status_icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxQuestion);
|
|
}
|
|
|
|
|
|
// Used to determine if the horizontal separator should be drawn
|
|
const bool lastIndex = (index.model()->rowCount() - 1) == index.row();
|
|
const bool hasIcon = !opt.icon.isNull();
|
|
const int bottomEdge = rect.bottom();
|
|
QFont f(opt.font);
|
|
|
|
f.setPointSizeF(m_ptr->statusFontPointSize(opt.font));
|
|
|
|
painter->save();
|
|
painter->setClipping(true);
|
|
painter->setClipRect(rect);
|
|
painter->setFont(opt.font);
|
|
|
|
// Draw the background
|
|
if (opt.state & QStyle::State_Selected) {
|
|
// When selected, only draw the highlighted part until the horizontal separator
|
|
int offset = 2;
|
|
auto highlightRect = rect.adjusted(0, 0, 0, -offset);
|
|
painter->fillRect(highlightRect, windows_light_mode ? palette.highlight().color() : QColor("#616161"));
|
|
// Then fill the remainder with the normal color
|
|
auto regularRect = rect.adjusted(0, rect.height()-offset, 0, 0);
|
|
painter->fillRect(regularRect, windows_light_mode ? palette.light().color() : QColor("#272727"));
|
|
} else {
|
|
// Otherwise just draw the background color as usual
|
|
painter->fillRect(rect, windows_light_mode ? palette.light().color() : QColor("#272727"));
|
|
}
|
|
|
|
// Draw bottom line. Last line gets a different color
|
|
painter->setPen(lastIndex ? palette.dark().color()
|
|
: palette.mid().color());
|
|
painter->drawLine(lastIndex ? rect.left() : m_ptr->margins.left(),
|
|
bottomEdge, rect.right(), bottomEdge);
|
|
|
|
// Draw system icon
|
|
if (hasIcon) {
|
|
painter->drawPixmap(contentRect.left(), contentRect.top(),
|
|
opt.icon.pixmap(m_ptr->iconSize));
|
|
}
|
|
|
|
// System name
|
|
QRect systemNameRect(m_ptr->systemNameBox(opt, index));
|
|
|
|
systemNameRect.moveTo(m_ptr->margins.left() + m_ptr->iconSize.width()
|
|
+ m_ptr->spacingHorizontal, contentRect.top());
|
|
// If desired, font can be changed here
|
|
// painter->setFont(f);
|
|
painter->setFont(opt.font);
|
|
painter->setPen(palette.text().color());
|
|
painter->drawText(systemNameRect, Qt::TextSingleLine, opt.text);
|
|
|
|
// Draw status icon
|
|
painter->drawPixmap(systemNameRect.left(), systemNameRect.bottom()
|
|
+ m_ptr->spacingVertical,
|
|
status_icon.pixmap(m_ptr->smallIconSize));
|
|
|
|
// This rectangle is around the status icon
|
|
// auto point = QPoint(systemNameRect.left(), systemNameRect.bottom()
|
|
// + m_ptr->spacingVertical);
|
|
// auto point2 = QPoint(point.x() + m_ptr->smallIconSize.width(), point.y() + m_ptr->smallIconSize.height());
|
|
// auto arect = QRect(point, point2);
|
|
// painter->drawRect(arect);
|
|
|
|
// Draw status text
|
|
QRect statusRect(m_ptr->statusBox(opt, index));
|
|
int extraaa = 2;
|
|
statusRect.moveTo(systemNameRect.left() + m_ptr->margins.left() + m_ptr->smallIconSize.width(),
|
|
systemNameRect.bottom() + m_ptr->spacingVertical + extraaa + (m_ptr->smallIconSize.height() - systemNameRect.height() ));
|
|
|
|
// painter->setFont(opt.font);
|
|
painter->setFont(f);
|
|
painter->setPen(palette.windowText().color());
|
|
painter->drawText(statusRect, Qt::TextSingleLine,
|
|
index.data(VMManagerModel::Roles::ProcessStatusString).toString());
|
|
|
|
painter->restore();
|
|
|
|
}
|
|
|
|
QMargins VMManagerListViewDelegate::contentsMargins() const
|
|
{
|
|
return m_ptr->margins;
|
|
}
|
|
|
|
void VMManagerListViewDelegate::setContentsMargins(const int left, const int top, const int right, const int bottom) const
|
|
{
|
|
m_ptr->margins = QMargins(left, top, right, bottom);
|
|
}
|
|
|
|
int VMManagerListViewDelegate::horizontalSpacing() const
|
|
{
|
|
return m_ptr->spacingHorizontal;
|
|
}
|
|
|
|
void VMManagerListViewDelegate::setHorizontalSpacing(const int spacing) const
|
|
{
|
|
m_ptr->spacingHorizontal = spacing;
|
|
}
|
|
|
|
int VMManagerListViewDelegate::verticalSpacing() const
|
|
{
|
|
return m_ptr->spacingVertical;
|
|
}
|
|
|
|
void VMManagerListViewDelegate::setVerticalSpacing(const int spacing) const
|
|
{
|
|
m_ptr->spacingVertical = spacing;
|
|
}
|
|
|
|
|
|
QSize VMManagerListViewDelegate::sizeHint(const QStyleOptionViewItem &option,
|
|
const QModelIndex &index) const
|
|
{
|
|
QStyleOptionViewItem opt(option);
|
|
initStyleOption(&opt, index);
|
|
|
|
const int textHeight = m_ptr->systemNameBox(opt, index).height()
|
|
+ m_ptr->spacingVertical + m_ptr->statusBox(opt, index).height();
|
|
const int iconHeight = m_ptr->iconSize.height();
|
|
const int h = textHeight > iconHeight ? textHeight : iconHeight;
|
|
|
|
// return the same width
|
|
// for height, add margins on top and bottom *plus* either the text or icon height, whichever is greater
|
|
// Note: text height is the combined value of the system name and the status just below the name
|
|
return {opt.rect.width(), m_ptr->margins.top() + h
|
|
+ m_ptr->margins.bottom()};
|
|
}
|
|
|
|
VMManagerListViewDelegateStyle::VMManagerListViewDelegateStyle() :
|
|
iconSize(32, 32),
|
|
smallIconSize(16, 16),
|
|
// bottom gets a little more than the top because of the custom separator
|
|
margins(4, 10, 8, 12),
|
|
// Spacing between icon and text
|
|
spacingHorizontal(8),
|
|
spacingVertical(4)
|
|
{
|
|
|
|
}
|
|
|
|
QRect VMManagerListViewDelegateStyle::statusBox(const QStyleOptionViewItem &option,
|
|
const QModelIndex &index) const
|
|
{
|
|
QFont f(option.font);
|
|
|
|
f.setPointSizeF(statusFontPointSize(option.font));
|
|
|
|
return QFontMetrics(f).boundingRect(index.data(VMManagerModel::Roles::ProcessStatusString).toString())
|
|
.adjusted(0, 0, 1, 1);
|
|
}
|
|
|
|
qreal VMManagerListViewDelegateStyle::statusFontPointSize(const QFont &f) const
|
|
{
|
|
return 0.75*f.pointSize();
|
|
// return 1*f.pointSize();
|
|
}
|
|
|
|
QRect VMManagerListViewDelegateStyle::systemNameBox(const QStyleOptionViewItem &option, const QModelIndex &index) const
|
|
{
|
|
return option.fontMetrics.boundingRect(option.text).adjusted(0, 0, 1, 1);
|
|
}
|