Skip to content

Commit

Permalink
WIP TimeSheet history view
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexandrePTJ committed Jun 24, 2024
1 parent 9413b7b commit c455164
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 17 deletions.
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ set(SRCS
gui/mainWindow.cpp
gui/settingsDialog.cpp
gui/taskWidget.cpp
gui/timeSheetItemDelegate.cpp
gui/timeSheetListWidgetItem.cpp
gui/timeSheetParamsItemDelegate.cpp
main.cpp
Expand Down Expand Up @@ -55,6 +56,7 @@ set(HDRS
gui/mainWindow.h
gui/settingsDialog.h
gui/taskWidget.h
gui/timeSheetItemDelegate.h
gui/timeSheetListWidgetItem.h
gui/timeSheetParamsItemDelegate.h
misc/customFmt.h
Expand Down
5 changes: 5 additions & 0 deletions src/gui/activityWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "misc/customFmt.h"
#include "misc/helpers.h"
#include "timeSheetItemDelegate.h"
#include "timeSheetListWidgetItem.h"
#include "timeSheetParamsItemDelegate.h"

Expand All @@ -21,6 +22,10 @@ ActivityWidget::ActivityWidget(QWidget* parent) : QWidget(parent), mUi(std::make
mUi->cbTimeSheetParams->setItemDelegate(new TimeSheetParamsItemDelegate);

mUi->lwHistory->setModel(&mTimeSheetModel);
mUi->lwHistory->setItemDelegate(new TimeSheetItemDelegate);

auto scrollBarWidth = qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent);
mUi->lwHistoryOutsideLayout->setContentsMargins(scrollBarWidth, 0, 0, 0);

connect(mUi->btStartStop, &QPushButton::clicked, this, &ActivityWidget::onBtStartStopClicked);
connect(mUi->cbTimeSheetParams, &QComboBox::currentIndexChanged, this, &ActivityWidget::onTimeSheetParamsIndexChanged);
Expand Down
28 changes: 27 additions & 1 deletion src/gui/activityWidget.ui
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,33 @@
</widget>
</item>
<item>
<widget class="QListView" name="lwHistory"/>
<layout class="QHBoxLayout" name="lwHistoryOutsideLayout">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QListView" name="lwHistory">
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOn</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOff</enum>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="spacing">
<number>2</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
Expand Down
113 changes: 113 additions & 0 deletions src/gui/timeSheetItemDelegate.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#include "timeSheetItemDelegate.h"

#include <QApplication>
#include <QPainter>
#include <QPalette>
#include <QStyle>

#include "models/timeSheetModel.h"

using namespace kemai;

static const auto gIndicatorWidth = 2;
static const auto gTextSpacing = 2;
static const auto gTextLeftOffset = gIndicatorWidth + gTextSpacing;
static const auto gPadding = 4;
static const auto gButtonWidth = 64;

// gIndicatorWidth + gPadding (1 x blue rect + 1 x empty)
// | |
// || < gPadding >
// || Customer name
// || < gTextSpacing >
// || Project name (on gProjectMaxWidth) < gTextSpacing > Activity name
// || < gPadding >

void TimeSheetItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
auto isHover = (option.state & QStyle::State_MouseOver) != 0;
auto timeSheet = reinterpret_cast<const TimeSheet*>(index.constInternalPointer());

/*
* Background frame
*/
QStyleOptionFrame frameOption;
frameOption.frameShape = QFrame::StyledPanel;
frameOption.rect = option.rect;
if (isHover)
{
frameOption.palette.setColor(QPalette::Base, option.palette.color(QPalette::Base).lighter(120));
}
qApp->style()->drawPrimitive(QStyle::PE_Frame, &frameOption, painter);

/*
* Text
*/
const auto textX = option.rect.left() + gTextLeftOffset;
const auto textH = option.fontMetrics.height();
const auto widthThird = option.rect.width() / 3;
const auto secondLineY = option.rect.top() + gPadding + option.fontMetrics.height() + gTextSpacing;

// Customer
auto customerRect = QRect(textX, option.rect.top() + gPadding, option.rect.width(), textH);
auto customerPalette = option.palette;
if (!timeSheet->project.customer.color.isEmpty())
{
customerPalette.setColor(QPalette::WindowText, timeSheet->project.customer.color);
}
qApp->style()->drawItemText(painter, customerRect, Qt::AlignLeft, customerPalette, isHover, timeSheet->project.customer.name, QPalette::WindowText);

// Project
auto projectRect = QRect(textX, secondLineY, widthThird, textH);
auto projectPalette = option.palette;
if (!timeSheet->project.color.isEmpty())
{
projectPalette.setColor(QPalette::WindowText, timeSheet->project.color);
}
qApp->style()->drawItemText(painter, projectRect, Qt::AlignLeft, projectPalette, isHover, timeSheet->project.name, QPalette::WindowText);

// Activity
auto activityRect = QRect(textX + widthThird, secondLineY, 2 * widthThird, textH);
qApp->style()->drawItemText(painter, activityRect, Qt::AlignLeft, option.palette, isHover, timeSheet->activity.name);

/*
* Buttons
*/
const auto buttonY = option.rect.top() + gPadding;
const auto buttonH = option.rect.height() - 2 * gPadding;

// Reload button
QStyleOptionButton reloadButtonOption;
reloadButtonOption.features = QStyleOptionButton::Flat;
reloadButtonOption.text = "Reload";
reloadButtonOption.state = QStyle::State_Sunken;
reloadButtonOption.rect = QRect(option.rect.right() - 3 * gPadding - 2 * gButtonWidth, buttonY, gButtonWidth, buttonH);

qApp->style()->drawControl(QStyle::CE_PushButton, &reloadButtonOption, painter);

// Restart button
QStyleOptionButton restartButtonOption;
// buttonOption.features = QStyleOptionButton::DefaultButton;
restartButtonOption.text = "Restart";
restartButtonOption.state = QStyle::State_Sunken;
restartButtonOption.rect = QRect(option.rect.right() - gPadding - gButtonWidth, buttonY, gButtonWidth, buttonH);

qApp->style()->drawControl(QStyle::CE_PushButton, &restartButtonOption, painter);
}

QSize TimeSheetItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
{
return {option.rect.width(), option.fontMetrics.height() * 2 + gTextSpacing + 2 * gPadding};
}

bool TimeSheetItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index)
{
switch (event->type())
{
case QEvent::MouseButtonPress:
return true;

default:
return false;
}
}
15 changes: 15 additions & 0 deletions src/gui/timeSheetItemDelegate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

#include <QStyledItemDelegate>

namespace kemai {

class TimeSheetItemDelegate : public QStyledItemDelegate
{
public:
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) override;
};

} // namespace kemai
22 changes: 7 additions & 15 deletions src/models/timeSheetModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,14 @@ void TimeSheetModel::setFilterTerm(const QString& term)
resetModel();
}

QVariant TimeSheetModel::data(const QModelIndex& index, int role) const
QModelIndex TimeSheetModel::index(int row, int column, const QModelIndex& parent) const
{
if (!index.isValid() || (index.row() > mTimeSheets.size()))
{
return {};
}

const auto& timeSheet = mTimeSheets.at(index.row());
switch (role)
{
case Qt::DisplayRole:
return QString("%1 / %2 / %3").arg(timeSheet.project.customer.name, timeSheet.project.name, timeSheet.activity.name);
return hasIndex(row, column, parent) ? createIndex(row, column, mTimeSheets.at(row).get()) : QModelIndex();
}

default:
return {};
}
QVariant TimeSheetModel::data(const QModelIndex& index, int role) const
{
return {};
}

int TimeSheetModel::rowCount(const QModelIndex& /*parent*/) const
Expand All @@ -55,7 +47,7 @@ void TimeSheetModel::fetchMore(const QModelIndex& /*parent*/)
auto timeSheets = timeSheetsResult->takeResult();

beginInsertRows({}, static_cast<int>(mTimeSheets.size()), static_cast<int>(mTimeSheets.size() + timeSheets.size() - 1));
mTimeSheets.insert(mTimeSheets.end(), timeSheets.begin(), timeSheets.end());
std::ranges::transform(timeSheets, std::back_inserter(mTimeSheets), [](const auto& ts) { return std::make_unique<TimeSheet>(ts); });
endInsertRows();

++mLastPageFetched;
Expand Down
3 changes: 2 additions & 1 deletion src/models/timeSheetModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class TimeSheetModel : public QAbstractListModel
void setKemaiSession(const std::shared_ptr<KemaiSession>& kemaiSession);
void setFilterTerm(const QString& term);

QModelIndex index(int row, int column, const QModelIndex& parent = {}) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
void fetchMore(const QModelIndex& parent) override;
Expand All @@ -21,7 +22,7 @@ class TimeSheetModel : public QAbstractListModel
void resetModel();

std::shared_ptr<KemaiSession> mSession;
std::vector<TimeSheet> mTimeSheets;
std::vector<std::unique_ptr<TimeSheet>> mTimeSheets;
bool mEndReached = false;
bool mIsFetching = false;
int mLastPageFetched = 0;
Expand Down

0 comments on commit c455164

Please sign in to comment.