generate ui core flows
This commit is contained in:
+15
-1
@@ -20,7 +20,21 @@ qt_add_executable(adhd
|
|||||||
service.cpp
|
service.cpp
|
||||||
service.h
|
service.h
|
||||||
mainwindow.ui
|
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.
|
# 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 "mainwindow.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
#include "sessionslistmodel.h"
|
#include "sessionslistmodel.h"
|
||||||
|
#include "sessionitemdelegate.h"
|
||||||
|
#include "createsessiondialog.h"
|
||||||
|
#include "focusoverlay.h"
|
||||||
|
#include "endsessiondialog.h"
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QMessageBox>
|
||||||
#include "ui_mainwindow.h"
|
#include "ui_mainwindow.h"
|
||||||
|
|
||||||
MainWindow::MainWindow(QWidget *parent)
|
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 = new Ui::MainWindow;
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
m_listModel = new SessionsListModel(m_service, this);
|
m_listModel = new SessionsListModel(m_service, this);
|
||||||
ui->listView->setModel(m_listModel);
|
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() {}
|
MainWindow::~MainWindow() {}
|
||||||
@@ -27,6 +59,190 @@ void MainWindow::on_selectVaultButton_clicked()
|
|||||||
if (!dir.isEmpty()) {
|
if (!dir.isEmpty()) {
|
||||||
m_service->setVaultDirectory(dir);
|
m_service->setVaultDirectory(dir);
|
||||||
ui->tabWidget->setCurrentIndex(1);
|
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 Service;
|
||||||
class SessionsListModel;
|
class SessionsListModel;
|
||||||
|
class FocusOverlay;
|
||||||
|
class QTimer;
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
@@ -21,13 +23,34 @@ public:
|
|||||||
MainWindow(QWidget *parent = nullptr);
|
MainWindow(QWidget *parent = nullptr);
|
||||||
~MainWindow();
|
~MainWindow();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void on_selectVaultButton_clicked();
|
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;
|
Ui::MainWindow *ui;
|
||||||
Service *m_service;
|
Service *m_service;
|
||||||
SessionsListModel *m_listModel;
|
SessionsListModel *m_listModel;
|
||||||
|
FocusOverlay *m_focusOverlay;
|
||||||
|
|
||||||
|
bool m_updatingActiveFields;
|
||||||
|
|
||||||
|
void updateActiveTab();
|
||||||
|
QString formatDuration(int ms) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MAINWINDOW_H
|
#endif // MAINWINDOW_H
|
||||||
|
|||||||
+193
@@ -65,6 +65,37 @@
|
|||||||
<string>Sessions</string>
|
<string>Sessions</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
<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>
|
<item>
|
||||||
<widget class="QListView" name="listView"/>
|
<widget class="QListView" name="listView"/>
|
||||||
</item>
|
</item>
|
||||||
@@ -74,6 +105,168 @@
|
|||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>Active</string>
|
<string>Active</string>
|
||||||
</attribute>
|
</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>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
+2
-6
@@ -1,9 +1,5 @@
|
|||||||
#include "namegenerator.cpp"
|
#include "namegenerator.h"
|
||||||
|
|
||||||
namespace {
|
|
||||||
constexpr char data[]
|
|
||||||
}
|
|
||||||
|
|
||||||
QString generate() {
|
QString generate() {
|
||||||
return;
|
return QString("generated-name-placeholder");
|
||||||
}
|
}
|
||||||
|
|||||||
+81
-2
@@ -8,7 +8,12 @@ bool Session::isComplete() const {
|
|||||||
return result != UNSPECIFIED;
|
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() {}
|
Service::~Service() {}
|
||||||
@@ -161,7 +166,7 @@ std::optional<Session> Service::parseFile(const QFileInfo &fileInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Session session = {};
|
Session session = {};
|
||||||
session.id = file.fileName();
|
session.id = fileInfo.baseName(); // Get filename without extension
|
||||||
session.content = "";
|
session.content = "";
|
||||||
session.oneliner = "";
|
session.oneliner = "";
|
||||||
session.durationMs = 0;
|
session.durationMs = 0;
|
||||||
@@ -268,3 +273,77 @@ void Service::overwriteSessionFile(const Session &session)
|
|||||||
out << session.content << "\n";
|
out << session.content << "\n";
|
||||||
file.close();
|
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 <QDateTime>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
#include <QElapsedTimer>
|
||||||
|
#include <QTimer>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
struct Session
|
struct Session
|
||||||
@@ -65,18 +67,38 @@ public slots:
|
|||||||
|
|
||||||
void deleteSession(const QString &sessionId);
|
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:
|
signals:
|
||||||
void sessionsChanged();
|
void sessionsChanged();
|
||||||
|
void activeSessionChanged();
|
||||||
|
void sessionTimerTicked(int elapsedMs);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_vaultDirectory;
|
QString m_vaultDirectory;
|
||||||
QList<Session> m_sessions;
|
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
|
/// @returns null Session if not a valid session markdown document
|
||||||
std::optional<Session> parseFile(const QFileInfo &fileInfo);
|
std::optional<Session> parseFile(const QFileInfo &fileInfo);
|
||||||
|
|
||||||
Session::Result parseResult(const QString &resultStr);
|
Session::Result parseResult(const QString &resultStr);
|
||||||
void overwriteSessionFile(const Session &session);
|
void overwriteSessionFile(const Session &session);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onTickTimerFired();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(Session)
|
||||||
|
|
||||||
#endif //SERVICE_H
|
#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());
|
const Session session = m_service->sessions().at(index.row());
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
|
// Simple display for fallback - delegate will handle rich rendering
|
||||||
return session.oneliner;
|
return session.oneliner;
|
||||||
|
case Qt::UserRole:
|
||||||
|
// Store full session object for delegate access
|
||||||
|
return QVariant::fromValue(session);
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
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 rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||||
int columnCount(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:
|
private:
|
||||||
Service *m_service;
|
Service *m_service;
|
||||||
|
|||||||
Reference in New Issue
Block a user