generate ui core flows
This commit is contained in:
+15
-1
@@ -20,7 +20,21 @@ qt_add_executable(adhd
|
||||
service.cpp
|
||||
service.h
|
||||
mainwindow.ui
|
||||
sessionslistmodel.h sessionslistmodel.cpp
|
||||
sessionslistmodel.h
|
||||
sessionslistmodel.cpp
|
||||
sessionitemdelegate.h
|
||||
sessionitemdelegate.cpp
|
||||
createsessiondialog.h
|
||||
createsessiondialog.cpp
|
||||
createsessiondialog.ui
|
||||
focusoverlay.h
|
||||
focusoverlay.cpp
|
||||
focusoverlay.ui
|
||||
endsessiondialog.h
|
||||
endsessiondialog.cpp
|
||||
endsessiondialog.ui
|
||||
namegenerator.h
|
||||
namegenerator.cpp
|
||||
)
|
||||
|
||||
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "createsessiondialog.h"
|
||||
#include "ui_createsessiondialog.h"
|
||||
#include <QPushButton>
|
||||
|
||||
CreateSessionDialog::CreateSessionDialog(QWidget *parent)
|
||||
: QDialog(parent),
|
||||
ui(new Ui::CreateSessionDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
setWindowTitle("Create New Session");
|
||||
|
||||
// Disable OK button initially
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
|
||||
// Connect oneliner field to validation
|
||||
connect(ui->onelinerEdit, &QLineEdit::textChanged,
|
||||
this, &CreateSessionDialog::onOnelinerChanged);
|
||||
}
|
||||
|
||||
CreateSessionDialog::~CreateSessionDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
QString CreateSessionDialog::getOneliner() const
|
||||
{
|
||||
return ui->onelinerEdit->text().trimmed();
|
||||
}
|
||||
|
||||
QString CreateSessionDialog::getContent() const
|
||||
{
|
||||
return ui->contentEdit->toPlainText().trimmed();
|
||||
}
|
||||
|
||||
void CreateSessionDialog::onOnelinerChanged(const QString &text)
|
||||
{
|
||||
bool isValid = !text.trimmed().isEmpty();
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(isValid);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef CREATESESSIONDIALOG_H
|
||||
#define CREATESESSIONDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class CreateSessionDialog;
|
||||
}
|
||||
|
||||
class CreateSessionDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CreateSessionDialog(QWidget *parent = nullptr);
|
||||
~CreateSessionDialog();
|
||||
|
||||
QString getOneliner() const;
|
||||
QString getContent() const;
|
||||
|
||||
private slots:
|
||||
void onOnelinerChanged(const QString &text);
|
||||
|
||||
private:
|
||||
Ui::CreateSessionDialog *ui;
|
||||
};
|
||||
|
||||
#endif // CREATESESSIONDIALOG_H
|
||||
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>CreateSessionDialog</class>
|
||||
<widget class="QDialog" name="CreateSessionDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>500</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Create Session</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="onelinerLabel">
|
||||
<property name="text">
|
||||
<string>Oneliner:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="onelinerEdit">
|
||||
<property name="placeholderText">
|
||||
<string>What will you focus on?</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="contentLabel">
|
||||
<property name="text">
|
||||
<string>Initial Content:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QTextEdit" name="contentEdit">
|
||||
<property name="placeholderText">
|
||||
<string>Optional: Add initial notes or context...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>CreateSessionDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>CreateSessionDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
@@ -0,0 +1,68 @@
|
||||
#include "endsessiondialog.h"
|
||||
#include "ui_endsessiondialog.h"
|
||||
|
||||
EndSessionDialog::EndSessionDialog(const QString &oneliner, int elapsedMs, QWidget *parent)
|
||||
: QDialog(parent),
|
||||
ui(new Ui::EndSessionDialog),
|
||||
m_selectedResult(Session::UNSPECIFIED)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
setWindowTitle("End Session");
|
||||
|
||||
// Set oneliner and elapsed time
|
||||
ui->onelinerLabel->setText(oneliner);
|
||||
|
||||
int seconds = elapsedMs / 1000;
|
||||
int hours = seconds / 3600;
|
||||
int minutes = (seconds % 3600) / 60;
|
||||
int secs = seconds % 60;
|
||||
|
||||
QString timeStr = QString("%1:%2:%3")
|
||||
.arg(hours, 2, 10, QChar('0'))
|
||||
.arg(minutes, 2, 10, QChar('0'))
|
||||
.arg(secs, 2, 10, QChar('0'));
|
||||
|
||||
ui->elapsedLabel->setText("Duration: " + timeStr);
|
||||
|
||||
// Connect buttons
|
||||
connect(ui->winButton, &QPushButton::clicked,
|
||||
this, &EndSessionDialog::onWinClicked);
|
||||
connect(ui->lossButton, &QPushButton::clicked,
|
||||
this, &EndSessionDialog::onLossClicked);
|
||||
connect(ui->neutralButton, &QPushButton::clicked,
|
||||
this, &EndSessionDialog::onNeutralClicked);
|
||||
}
|
||||
|
||||
EndSessionDialog::~EndSessionDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
Session::Result EndSessionDialog::getResult() const
|
||||
{
|
||||
return m_selectedResult;
|
||||
}
|
||||
|
||||
QString EndSessionDialog::getAdditionalNotes() const
|
||||
{
|
||||
return ui->notesEdit->toPlainText().trimmed();
|
||||
}
|
||||
|
||||
void EndSessionDialog::onWinClicked()
|
||||
{
|
||||
m_selectedResult = Session::WIN;
|
||||
accept();
|
||||
}
|
||||
|
||||
void EndSessionDialog::onLossClicked()
|
||||
{
|
||||
m_selectedResult = Session::LOSS;
|
||||
accept();
|
||||
}
|
||||
|
||||
void EndSessionDialog::onNeutralClicked()
|
||||
{
|
||||
m_selectedResult = Session::NEUTRAL;
|
||||
accept();
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef ENDSESSIONDIALOG_H
|
||||
#define ENDSESSIONDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "service.h"
|
||||
|
||||
namespace Ui {
|
||||
class EndSessionDialog;
|
||||
}
|
||||
|
||||
class EndSessionDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit EndSessionDialog(const QString &oneliner, int elapsedMs, QWidget *parent = nullptr);
|
||||
~EndSessionDialog();
|
||||
|
||||
Session::Result getResult() const;
|
||||
QString getAdditionalNotes() const;
|
||||
|
||||
private slots:
|
||||
void onWinClicked();
|
||||
void onLossClicked();
|
||||
void onNeutralClicked();
|
||||
|
||||
private:
|
||||
Ui::EndSessionDialog *ui;
|
||||
Session::Result m_selectedResult;
|
||||
};
|
||||
|
||||
#endif // ENDSESSIONDIALOG_H
|
||||
@@ -0,0 +1,221 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>EndSessionDialog</class>
|
||||
<widget class="QDialog" name="EndSessionDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>500</width>
|
||||
<height>400</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>End Session</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>16</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>How did your session go?</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="onelinerLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>14</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Session Oneliner</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="elapsedLabel">
|
||||
<property name="text">
|
||||
<string>Duration: 00:00:00</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="winButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>14</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton {
|
||||
background-color: #2ecc71;
|
||||
color: white;
|
||||
border-radius: 5px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #27ae60;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #229954;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>WIN</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="lossButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>14</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton {
|
||||
background-color: #e74c3c;
|
||||
color: white;
|
||||
border-radius: 5px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #c0392b;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #a93226;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>LOSS</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="neutralButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>14</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton {
|
||||
background-color: #95a5a6;
|
||||
color: white;
|
||||
border-radius: 5px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #7f8c8d;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #707b7c;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>NEUTRAL</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Optional: Add final notes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="notesEdit">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Any final thoughts or notes about this session...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,71 @@
|
||||
#include "focusoverlay.h"
|
||||
#include "ui_focusoverlay.h"
|
||||
#include <QScreen>
|
||||
#include <QGuiApplication>
|
||||
|
||||
FocusOverlay::FocusOverlay(QWidget *parent)
|
||||
: QWidget(parent),
|
||||
ui(new Ui::FocusOverlay)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
// Set window flags for always-on-top frameless window
|
||||
setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::Tool);
|
||||
setAttribute(Qt::WA_TranslucentBackground);
|
||||
|
||||
// Connect buttons
|
||||
connect(ui->resetButton, &QPushButton::clicked,
|
||||
this, &FocusOverlay::onResetButtonClicked);
|
||||
connect(ui->endButton, &QPushButton::clicked,
|
||||
this, &FocusOverlay::onEndButtonClicked);
|
||||
|
||||
// Position window
|
||||
positionAtBottomCenter();
|
||||
}
|
||||
|
||||
FocusOverlay::~FocusOverlay()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void FocusOverlay::setOneliner(const QString &oneliner)
|
||||
{
|
||||
ui->onelinerLabel->setText(oneliner);
|
||||
}
|
||||
|
||||
void FocusOverlay::setElapsedTime(int elapsedMs)
|
||||
{
|
||||
int seconds = elapsedMs / 1000;
|
||||
int hours = seconds / 3600;
|
||||
int minutes = (seconds % 3600) / 60;
|
||||
int secs = seconds % 60;
|
||||
|
||||
QString timeStr = QString("%1:%2:%3")
|
||||
.arg(hours, 2, 10, QChar('0'))
|
||||
.arg(minutes, 2, 10, QChar('0'))
|
||||
.arg(secs, 2, 10, QChar('0'));
|
||||
|
||||
ui->elapsedLabel->setText("Elapsed: " + timeStr);
|
||||
}
|
||||
|
||||
void FocusOverlay::onResetButtonClicked()
|
||||
{
|
||||
emit resetClicked();
|
||||
}
|
||||
|
||||
void FocusOverlay::onEndButtonClicked()
|
||||
{
|
||||
emit endSessionClicked();
|
||||
}
|
||||
|
||||
void FocusOverlay::positionAtBottomCenter()
|
||||
{
|
||||
QScreen *screen = QGuiApplication::primaryScreen();
|
||||
if (!screen) return;
|
||||
|
||||
QRect screenGeometry = screen->geometry();
|
||||
int x = (screenGeometry.width() - width()) / 2;
|
||||
int y = screenGeometry.height() - height() - 300; // 300px from bottom
|
||||
|
||||
move(x, y);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef FOCUSOVERLAY_H
|
||||
#define FOCUSOVERLAY_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
namespace Ui {
|
||||
class FocusOverlay;
|
||||
}
|
||||
|
||||
class FocusOverlay : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FocusOverlay(QWidget *parent = nullptr);
|
||||
~FocusOverlay();
|
||||
|
||||
void setOneliner(const QString &oneliner);
|
||||
void setElapsedTime(int elapsedMs);
|
||||
|
||||
signals:
|
||||
void resetClicked();
|
||||
void endSessionClicked();
|
||||
|
||||
private slots:
|
||||
void onResetButtonClicked();
|
||||
void onEndButtonClicked();
|
||||
|
||||
private:
|
||||
Ui::FocusOverlay *ui;
|
||||
void positionAtBottomCenter();
|
||||
};
|
||||
|
||||
#endif // FOCUSOVERLAY_H
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>FocusOverlay</class>
|
||||
<widget class="QWidget" name="FocusOverlay">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>120</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Focus Session</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QWidget#FocusOverlay {
|
||||
background-color: rgba(50, 50, 50, 200);
|
||||
border-radius: 10px;
|
||||
border: 2px solid rgba(100, 100, 100, 255);
|
||||
}
|
||||
|
||||
QLabel {
|
||||
color: white;
|
||||
}
|
||||
|
||||
QPushButton {
|
||||
background-color: rgba(80, 80, 80, 255);
|
||||
color: white;
|
||||
border: 1px solid rgba(120, 120, 120, 255);
|
||||
border-radius: 5px;
|
||||
padding: 5px 10px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
QPushButton:hover {
|
||||
background-color: rgba(100, 100, 100, 255);
|
||||
}
|
||||
|
||||
QPushButton:pressed {
|
||||
background-color: rgba(60, 60, 60, 255);
|
||||
}</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>15</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>15</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>15</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>15</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="onelinerLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>14</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Session Oneliner</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="elapsedLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>12</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Elapsed: 00:00:00</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="resetButton">
|
||||
<property name="text">
|
||||
<string>Reset</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="endButton">
|
||||
<property name="text">
|
||||
<string>End Session</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
+218
-2
@@ -1,19 +1,51 @@
|
||||
#include "mainwindow.h"
|
||||
#include "service.h"
|
||||
#include "sessionslistmodel.h"
|
||||
#include "sessionitemdelegate.h"
|
||||
#include "createsessiondialog.h"
|
||||
#include "focusoverlay.h"
|
||||
#include "endsessiondialog.h"
|
||||
#include <QFileDialog>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
#include <QTimer>
|
||||
#include <QMessageBox>
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent)
|
||||
: QMainWindow(parent), m_service(new Service(this))
|
||||
: QMainWindow(parent),
|
||||
m_service(new Service(this)),
|
||||
m_focusOverlay(nullptr),
|
||||
m_updatingActiveFields(false)
|
||||
{
|
||||
ui = new Ui::MainWindow;
|
||||
ui->setupUi(this);
|
||||
|
||||
m_listModel = new SessionsListModel(m_service, this);
|
||||
ui->listView->setModel(m_listModel);
|
||||
ui->listView->setItemDelegate(new SessionItemDelegate(this));
|
||||
|
||||
// Connect Service signals
|
||||
connect(m_service, &Service::activeSessionChanged,
|
||||
this, &MainWindow::onActiveSessionChanged);
|
||||
connect(m_service, &Service::sessionTimerTicked,
|
||||
this, &MainWindow::onSessionTimerTicked);
|
||||
|
||||
// Connect Active tab field changes
|
||||
connect(ui->oneLineEdit, &QLineEdit::editingFinished,
|
||||
this, &MainWindow::onActiveOnelinerChanged);
|
||||
|
||||
// Debounce content text changes
|
||||
QTimer *contentDebounce = new QTimer(this);
|
||||
contentDebounce->setSingleShot(true);
|
||||
contentDebounce->setInterval(500);
|
||||
connect(ui->contentTextEdit, &QTextEdit::textChanged,
|
||||
[contentDebounce]() { contentDebounce->start(); });
|
||||
connect(contentDebounce, &QTimer::timeout,
|
||||
this, &MainWindow::onActiveContentChanged);
|
||||
|
||||
// Set initial state: no active session
|
||||
ui->activeStackedWidget->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow() {}
|
||||
@@ -27,6 +59,190 @@ void MainWindow::on_selectVaultButton_clicked()
|
||||
if (!dir.isEmpty()) {
|
||||
m_service->setVaultDirectory(dir);
|
||||
ui->tabWidget->setCurrentIndex(1);
|
||||
ui->vaultLabel->setText(m_service->vaultDirectory());
|
||||
ui->vaultLabel->setText(dir);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_newSessionButton_clicked()
|
||||
{
|
||||
CreateSessionDialog dialog(this);
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
QString oneliner = dialog.getOneliner();
|
||||
QString content = dialog.getContent();
|
||||
|
||||
Session session = m_service->createSession(oneliner, content);
|
||||
m_service->startSession(session);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_startSessionButton_clicked()
|
||||
{
|
||||
QModelIndex index = ui->listView->currentIndex();
|
||||
if (!index.isValid()) {
|
||||
QMessageBox::warning(this, "No Selection", "Please select a session to start.");
|
||||
return;
|
||||
}
|
||||
|
||||
Session session = m_listModel->getSessionByIndex(index);
|
||||
m_service->startSession(session);
|
||||
}
|
||||
|
||||
void MainWindow::on_endSessionButton_clicked()
|
||||
{
|
||||
auto activeSession = m_service->activeSession();
|
||||
if (!activeSession) {
|
||||
return;
|
||||
}
|
||||
|
||||
EndSessionDialog dialog(activeSession->oneliner, m_service->getElapsedMs(), this);
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
Session::Result result = dialog.getResult();
|
||||
QString notes = dialog.getAdditionalNotes();
|
||||
|
||||
// Append notes to content if provided
|
||||
if (!notes.isEmpty()) {
|
||||
Session updated = *activeSession;
|
||||
if (!updated.content.isEmpty()) {
|
||||
updated.content += "\n\n";
|
||||
}
|
||||
updated.content += notes;
|
||||
m_service->updateSession(updated);
|
||||
}
|
||||
|
||||
m_service->endSession(result);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::onActiveSessionChanged()
|
||||
{
|
||||
auto activeSession = m_service->activeSession();
|
||||
|
||||
if (activeSession) {
|
||||
// Session started
|
||||
ui->activeStackedWidget->setCurrentIndex(1);
|
||||
updateActiveTab();
|
||||
|
||||
// Create and show overlay
|
||||
if (!m_focusOverlay) {
|
||||
m_focusOverlay = new FocusOverlay();
|
||||
connect(m_focusOverlay, &FocusOverlay::resetClicked,
|
||||
this, &MainWindow::onOverlayResetClicked);
|
||||
connect(m_focusOverlay, &FocusOverlay::endSessionClicked,
|
||||
this, &MainWindow::onOverlayEndSessionClicked);
|
||||
}
|
||||
|
||||
m_focusOverlay->setOneliner(activeSession->oneliner);
|
||||
m_focusOverlay->setElapsedTime(0);
|
||||
m_focusOverlay->show();
|
||||
|
||||
// Switch to Active tab
|
||||
ui->tabWidget->setCurrentIndex(2);
|
||||
} else {
|
||||
// Session ended
|
||||
ui->activeStackedWidget->setCurrentIndex(0);
|
||||
|
||||
// Hide and delete overlay
|
||||
if (m_focusOverlay) {
|
||||
m_focusOverlay->hide();
|
||||
m_focusOverlay->deleteLater();
|
||||
m_focusOverlay = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::onSessionTimerTicked(int elapsedMs)
|
||||
{
|
||||
ui->elapsedLabel->setText(formatDuration(elapsedMs));
|
||||
|
||||
if (m_focusOverlay) {
|
||||
m_focusOverlay->setElapsedTime(elapsedMs);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::onActiveOnelinerChanged()
|
||||
{
|
||||
if (m_updatingActiveFields) return;
|
||||
|
||||
auto activeSession = m_service->activeSession();
|
||||
if (!activeSession) return;
|
||||
|
||||
Session updated = *activeSession;
|
||||
updated.oneliner = ui->oneLineEdit->text();
|
||||
updated.lastModifiedAt = QDateTime::currentDateTime();
|
||||
|
||||
m_updatingActiveFields = true;
|
||||
m_service->updateSession(updated);
|
||||
m_service->updateActiveSession(updated);
|
||||
|
||||
// Update overlay
|
||||
if (m_focusOverlay) {
|
||||
m_focusOverlay->setOneliner(updated.oneliner);
|
||||
}
|
||||
|
||||
m_updatingActiveFields = false;
|
||||
}
|
||||
|
||||
void MainWindow::onActiveContentChanged()
|
||||
{
|
||||
if (m_updatingActiveFields) return;
|
||||
|
||||
auto activeSession = m_service->activeSession();
|
||||
if (!activeSession) return;
|
||||
|
||||
Session updated = *activeSession;
|
||||
updated.content = ui->contentTextEdit->toPlainText();
|
||||
updated.lastModifiedAt = QDateTime::currentDateTime();
|
||||
|
||||
m_updatingActiveFields = true;
|
||||
m_service->updateSession(updated);
|
||||
m_service->updateActiveSession(updated);
|
||||
m_updatingActiveFields = false;
|
||||
}
|
||||
|
||||
void MainWindow::onOverlayResetClicked()
|
||||
{
|
||||
QMessageBox::StandardButton reply = QMessageBox::question(
|
||||
this,
|
||||
"Reset Timer",
|
||||
"Are you sure you want to reset the timer to 00:00:00?",
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
|
||||
if (reply == QMessageBox::Yes) {
|
||||
m_service->resetSessionTimer();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::onOverlayEndSessionClicked()
|
||||
{
|
||||
on_endSessionButton_clicked();
|
||||
}
|
||||
|
||||
void MainWindow::updateActiveTab()
|
||||
{
|
||||
auto activeSession = m_service->activeSession();
|
||||
if (!activeSession) return;
|
||||
|
||||
m_updatingActiveFields = true;
|
||||
|
||||
ui->oneLineEdit->setText(activeSession->oneliner);
|
||||
ui->elapsedLabel->setText(formatDuration(m_service->getElapsedMs()));
|
||||
|
||||
ui->createdLabel->setText(activeSession->createdAt.toString("yyyy-MM-dd hh:mm:ss"));
|
||||
ui->modifiedLabel->setText(activeSession->lastModifiedAt.toString("yyyy-MM-dd hh:mm:ss"));
|
||||
ui->contentTextEdit->setPlainText(activeSession->content);
|
||||
|
||||
m_updatingActiveFields = false;
|
||||
}
|
||||
|
||||
QString MainWindow::formatDuration(int ms) const
|
||||
{
|
||||
int seconds = ms / 1000;
|
||||
int hours = seconds / 3600;
|
||||
int minutes = (seconds % 3600) / 60;
|
||||
int secs = seconds % 60;
|
||||
|
||||
return QString("%1:%2:%3")
|
||||
.arg(hours, 2, 10, QChar('0'))
|
||||
.arg(minutes, 2, 10, QChar('0'))
|
||||
.arg(secs, 2, 10, QChar('0'));
|
||||
}
|
||||
|
||||
+25
-2
@@ -6,6 +6,8 @@
|
||||
|
||||
class Service;
|
||||
class SessionsListModel;
|
||||
class FocusOverlay;
|
||||
class QTimer;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
QT_END_NAMESPACE
|
||||
@@ -21,13 +23,34 @@ public:
|
||||
MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow();
|
||||
|
||||
private slots:
|
||||
private slots:
|
||||
void on_selectVaultButton_clicked();
|
||||
void on_newSessionButton_clicked();
|
||||
void on_startSessionButton_clicked();
|
||||
void on_endSessionButton_clicked();
|
||||
|
||||
private:
|
||||
// Service signal handlers
|
||||
void onActiveSessionChanged();
|
||||
void onSessionTimerTicked(int elapsedMs);
|
||||
|
||||
// Active tab field editing
|
||||
void onActiveOnelinerChanged();
|
||||
void onActiveContentChanged();
|
||||
|
||||
// Overlay handlers
|
||||
void onOverlayResetClicked();
|
||||
void onOverlayEndSessionClicked();
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
Service *m_service;
|
||||
SessionsListModel *m_listModel;
|
||||
FocusOverlay *m_focusOverlay;
|
||||
|
||||
bool m_updatingActiveFields;
|
||||
|
||||
void updateActiveTab();
|
||||
QString formatDuration(int ms) const;
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
||||
|
||||
+193
@@ -65,6 +65,37 @@
|
||||
<string>Sessions</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="newSessionButton">
|
||||
<property name="text">
|
||||
<string>New Session</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="startSessionButton">
|
||||
<property name="text">
|
||||
<string>Start Selected</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListView" name="listView"/>
|
||||
</item>
|
||||
@@ -74,6 +105,168 @@
|
||||
<attribute name="title">
|
||||
<string>Active</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="activeStackedWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="noSessionPage">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="noSessionLabel">
|
||||
<property name="text">
|
||||
<string>No session running. Start a session to begin.</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="activeSessionPage">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>358</width>
|
||||
<height>457</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Oneliner:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="oneLineEdit"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Elapsed Time:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="elapsedLabel">
|
||||
<property name="text">
|
||||
<string>00:00:00</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Created:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="createdLabel">
|
||||
<property name="text">
|
||||
<string>-</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Modified:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="modifiedLabel">
|
||||
<property name="text">
|
||||
<string>-</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Content:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QTextEdit" name="contentTextEdit">
|
||||
<property name="placeholderText">
|
||||
<string>Add notes, context, or anything related to this session...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="endSessionButton">
|
||||
<property name="text">
|
||||
<string>End Session</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
+2
-6
@@ -1,9 +1,5 @@
|
||||
#include "namegenerator.cpp"
|
||||
|
||||
namespace {
|
||||
constexpr char data[]
|
||||
}
|
||||
#include "namegenerator.h"
|
||||
|
||||
QString generate() {
|
||||
return;
|
||||
return QString("generated-name-placeholder");
|
||||
}
|
||||
|
||||
+81
-2
@@ -8,7 +8,12 @@ bool Session::isComplete() const {
|
||||
return result != UNSPECIFIED;
|
||||
}
|
||||
|
||||
Service::Service(QObject *parent) : QObject(parent) {
|
||||
Service::Service(QObject *parent)
|
||||
: QObject(parent),
|
||||
m_tickTimer(new QTimer(this))
|
||||
{
|
||||
m_tickTimer->setInterval(1000); // Fire every second
|
||||
connect(m_tickTimer, &QTimer::timeout, this, &Service::onTickTimerFired);
|
||||
}
|
||||
|
||||
Service::~Service() {}
|
||||
@@ -161,7 +166,7 @@ std::optional<Session> Service::parseFile(const QFileInfo &fileInfo) {
|
||||
}
|
||||
|
||||
Session session = {};
|
||||
session.id = file.fileName();
|
||||
session.id = fileInfo.baseName(); // Get filename without extension
|
||||
session.content = "";
|
||||
session.oneliner = "";
|
||||
session.durationMs = 0;
|
||||
@@ -268,3 +273,77 @@ void Service::overwriteSessionFile(const Session &session)
|
||||
out << session.content << "\n";
|
||||
file.close();
|
||||
}
|
||||
|
||||
// Session lifecycle methods
|
||||
|
||||
void Service::startSession(const Session &session) {
|
||||
if (m_activeSession.has_value()) {
|
||||
qWarning() << "A session is already active. End it before starting a new one.";
|
||||
return;
|
||||
}
|
||||
|
||||
m_activeSession = session;
|
||||
m_sessionTimer.start();
|
||||
m_tickTimer->start();
|
||||
|
||||
emit activeSessionChanged();
|
||||
emit sessionTimerTicked(0);
|
||||
}
|
||||
|
||||
void Service::updateActiveSession(const Session &session) {
|
||||
if (!m_activeSession.has_value()) {
|
||||
qWarning() << "No active session to update.";
|
||||
return;
|
||||
}
|
||||
|
||||
m_activeSession = session;
|
||||
// Don't emit activeSessionChanged - just update the session data
|
||||
// Timer keeps running
|
||||
}
|
||||
|
||||
int Service::getElapsedMs() const {
|
||||
if (!m_activeSession) {
|
||||
return 0;
|
||||
}
|
||||
return m_sessionTimer.elapsed();
|
||||
}
|
||||
|
||||
void Service::resetSessionTimer() {
|
||||
if (!m_activeSession) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_sessionTimer.restart();
|
||||
emit sessionTimerTicked(0);
|
||||
}
|
||||
|
||||
void Service::endSession(Session::Result result) {
|
||||
if (!m_activeSession) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update session with final values
|
||||
Session updated = *m_activeSession;
|
||||
updated.durationMs = getElapsedMs();
|
||||
updated.result = result;
|
||||
updated.lastModifiedAt = QDateTime::currentDateTime();
|
||||
|
||||
// Save to file and update in list
|
||||
updateSession(updated);
|
||||
|
||||
// Clear active state
|
||||
m_activeSession.reset();
|
||||
m_tickTimer->stop();
|
||||
|
||||
emit activeSessionChanged();
|
||||
}
|
||||
|
||||
std::optional<Session> Service::activeSession() const {
|
||||
return m_activeSession;
|
||||
}
|
||||
|
||||
void Service::onTickTimerFired() {
|
||||
if (m_activeSession) {
|
||||
emit sessionTimerTicked(getElapsedMs());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <QDateTime>
|
||||
#include <QString>
|
||||
#include <QFileInfo>
|
||||
#include <QElapsedTimer>
|
||||
#include <QTimer>
|
||||
#include <optional>
|
||||
|
||||
struct Session
|
||||
@@ -65,18 +67,38 @@ public slots:
|
||||
|
||||
void deleteSession(const QString &sessionId);
|
||||
|
||||
// Session lifecycle methods
|
||||
void startSession(const Session &session);
|
||||
void updateActiveSession(const Session &session); // Update without restarting timer
|
||||
int getElapsedMs() const;
|
||||
void resetSessionTimer();
|
||||
void endSession(Session::Result result);
|
||||
std::optional<Session> activeSession() const;
|
||||
|
||||
signals:
|
||||
void sessionsChanged();
|
||||
void activeSessionChanged();
|
||||
void sessionTimerTicked(int elapsedMs);
|
||||
|
||||
private:
|
||||
QString m_vaultDirectory;
|
||||
QList<Session> m_sessions;
|
||||
|
||||
// Active session tracking
|
||||
std::optional<Session> m_activeSession;
|
||||
QElapsedTimer m_sessionTimer;
|
||||
QTimer *m_tickTimer;
|
||||
|
||||
/// @returns null Session if not a valid session markdown document
|
||||
std::optional<Session> parseFile(const QFileInfo &fileInfo);
|
||||
|
||||
Session::Result parseResult(const QString &resultStr);
|
||||
void overwriteSessionFile(const Session &session);
|
||||
|
||||
private slots:
|
||||
void onTickTimerFired();
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(Session)
|
||||
|
||||
#endif //SERVICE_H
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
#include "sessionitemdelegate.h"
|
||||
#include "service.h"
|
||||
#include <QPainter>
|
||||
#include <QDateTime>
|
||||
|
||||
SessionItemDelegate::SessionItemDelegate(QObject *parent)
|
||||
: QStyledItemDelegate(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void SessionItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid()) {
|
||||
QStyledItemDelegate::paint(painter, option, index);
|
||||
return;
|
||||
}
|
||||
|
||||
painter->save();
|
||||
|
||||
// Get session data
|
||||
Session session = index.data(Qt::UserRole).value<Session>();
|
||||
QString oneliner = session.oneliner;
|
||||
|
||||
// Format duration
|
||||
int seconds = session.durationMs / 1000;
|
||||
int hours = seconds / 3600;
|
||||
int minutes = (seconds % 3600) / 60;
|
||||
int secs = seconds % 60;
|
||||
QString durationStr = QString("%1:%2:%3")
|
||||
.arg(hours, 2, 10, QChar('0'))
|
||||
.arg(minutes, 2, 10, QChar('0'))
|
||||
.arg(secs, 2, 10, QChar('0'));
|
||||
|
||||
// Format result
|
||||
QString resultStr;
|
||||
switch (session.result) {
|
||||
case Session::WIN:
|
||||
resultStr = "WIN";
|
||||
break;
|
||||
case Session::LOSS:
|
||||
resultStr = "LOSS";
|
||||
break;
|
||||
case Session::NEUTRAL:
|
||||
resultStr = "NEUTRAL";
|
||||
break;
|
||||
default:
|
||||
resultStr = "UNSPECIFIED";
|
||||
break;
|
||||
}
|
||||
|
||||
// Format relative time
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
qint64 secsSinceModified = session.lastModifiedAt.secsTo(now);
|
||||
QString relativeTime;
|
||||
if (secsSinceModified < 60) {
|
||||
relativeTime = "just now";
|
||||
} else if (secsSinceModified < 3600) {
|
||||
relativeTime = QString("%1 min ago").arg(secsSinceModified / 60);
|
||||
} else if (secsSinceModified < 86400) {
|
||||
relativeTime = QString("%1 hr ago").arg(secsSinceModified / 3600);
|
||||
} else {
|
||||
relativeTime = QString("%1 days ago").arg(secsSinceModified / 86400);
|
||||
}
|
||||
|
||||
QString metadata = QString("Duration: %1 | Result: %2 | Modified: %3")
|
||||
.arg(durationStr)
|
||||
.arg(resultStr)
|
||||
.arg(relativeTime);
|
||||
|
||||
// Draw background
|
||||
if (option.state & QStyle::State_Selected) {
|
||||
painter->fillRect(option.rect, option.palette.highlight());
|
||||
} else {
|
||||
painter->fillRect(option.rect, option.palette.base());
|
||||
}
|
||||
|
||||
// Draw result indicator bar on the left
|
||||
QColor indicatorColor;
|
||||
switch (session.result) {
|
||||
case Session::WIN:
|
||||
indicatorColor = QColor(46, 204, 113); // Green
|
||||
break;
|
||||
case Session::LOSS:
|
||||
indicatorColor = QColor(231, 76, 60); // Red
|
||||
break;
|
||||
case Session::NEUTRAL:
|
||||
indicatorColor = QColor(149, 165, 166); // Gray
|
||||
break;
|
||||
default:
|
||||
indicatorColor = QColor(0, 0, 0, 0); // Transparent
|
||||
break;
|
||||
}
|
||||
|
||||
if (session.result != Session::UNSPECIFIED) {
|
||||
QRect indicatorRect(option.rect.left(), option.rect.top(), 4, option.rect.height());
|
||||
painter->fillRect(indicatorRect, indicatorColor);
|
||||
}
|
||||
|
||||
// Set up text drawing
|
||||
QRect textRect = option.rect.adjusted(10, 5, -5, -5);
|
||||
QColor textColor = (option.state & QStyle::State_Selected)
|
||||
? option.palette.highlightedText().color()
|
||||
: option.palette.text().color();
|
||||
|
||||
// Draw oneliner (bold, larger font)
|
||||
painter->setPen(textColor);
|
||||
QFont onelineFont = option.font;
|
||||
onelineFont.setPointSize(14);
|
||||
onelineFont.setBold(true);
|
||||
painter->setFont(onelineFont);
|
||||
|
||||
QRect onelineRect = textRect;
|
||||
onelineRect.setHeight(24);
|
||||
QString elidedOneliner = painter->fontMetrics().elidedText(oneliner, Qt::ElideRight, onelineRect.width());
|
||||
painter->drawText(onelineRect, Qt::AlignLeft | Qt::AlignVCenter, elidedOneliner);
|
||||
|
||||
// Draw metadata (smaller font, lighter color)
|
||||
QFont metadataFont = option.font;
|
||||
metadataFont.setPointSize(11);
|
||||
painter->setFont(metadataFont);
|
||||
|
||||
QColor metadataColor = textColor;
|
||||
metadataColor.setAlpha(180); // Make it slightly transparent
|
||||
painter->setPen(metadataColor);
|
||||
|
||||
QRect metadataRect = textRect;
|
||||
metadataRect.setTop(onelineRect.bottom() + 2);
|
||||
QString elidedMetadata = painter->fontMetrics().elidedText(metadata, Qt::ElideRight, metadataRect.width());
|
||||
painter->drawText(metadataRect, Qt::AlignLeft | Qt::AlignVCenter, elidedMetadata);
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
QSize SessionItemDelegate::sizeHint(const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
return QSize(option.rect.width(), 65);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef SESSIONITEMDELEGATE_H
|
||||
#define SESSIONITEMDELEGATE_H
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
class SessionItemDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SessionItemDelegate(QObject *parent = nullptr);
|
||||
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const override;
|
||||
|
||||
QSize sizeHint(const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const override;
|
||||
};
|
||||
|
||||
#endif // SESSIONITEMDELEGATE_H
|
||||
@@ -27,8 +27,19 @@ QVariant SessionsListModel::data(const QModelIndex &index, int role) const {
|
||||
const Session session = m_service->sessions().at(index.row());
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
// Simple display for fallback - delegate will handle rich rendering
|
||||
return session.oneliner;
|
||||
case Qt::UserRole:
|
||||
// Store full session object for delegate access
|
||||
return QVariant::fromValue(session);
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
Session SessionsListModel::getSessionByIndex(const QModelIndex &index) const {
|
||||
if (!index.isValid() || index.row() < 0 || index.row() >= rowCount()) {
|
||||
return Session(); // Return default-constructed session
|
||||
}
|
||||
return m_service->sessions().at(index.row());
|
||||
}
|
||||
|
||||
+3
-1
@@ -13,7 +13,9 @@ public:
|
||||
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
|
||||
Session getSessionByIndex(const QModelIndex &index) const;
|
||||
|
||||
private:
|
||||
Service *m_service;
|
||||
|
||||
Reference in New Issue
Block a user