diff --git a/CMakeLists.txt b/CMakeLists.txt index 8150df8..7883e91 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,15 @@ find_package(Qt6 REQUIRED COMPONENTS qt_standard_project_setup() -qt_add_executable(adhd main.cpp mainwindow.cpp) +qt_add_executable(adhd + main.cpp + mainwindow.cpp + mainwindow.h + service.cpp + service.h + mainwindow.ui + sessionslistmodel.h sessionslistmodel.cpp +) # Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1. # If you are developing for iOS or macOS you should consider setting an diff --git a/CMakeLists.txt.user b/CMakeLists.txt.user new file mode 100644 index 0000000..2b8465e --- /dev/null +++ b/CMakeLists.txt.user @@ -0,0 +1,460 @@ + + + + + + EnvironmentId + {1354daad-1138-41d2-985c-27357b7ee48a} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + true + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 0 + 80 + true + true + 1 + 0 + false + true + false + 2 + true + true + 0 + 8 + true + false + 1 + true + true + true + *.md, *.MD, Makefile + false + true + true + + + + ProjectExplorer.Project.PluginSettings + + + true + false + true + true + true + true + + false + + + 0 + true + + true + true + Builtin.DefaultTidyAndClazy + 5 + true + + + + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop + Qt 6.8.6 for macOS + Qt 6.8.6 for macOS + qt.qt6.686.clang_64_kit + 0 + 0 + 0 + + Debug + 2 + false + + -DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} +-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON +-DCMAKE_BUILD_TYPE:STRING=Debug +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DCMAKE_GENERATOR:STRING=Ninja +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} + 0 + /Users/talksik/Documents/code/adhd/build/Qt_6_8_6_for_macOS-Debug + + + + + all + + false + + true + Build + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + Build + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Debug + CMakeProjectManager.CMakeBuildConfiguration + + + Release + 2 + false + + -DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} +-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON +-DCMAKE_BUILD_TYPE:STRING=Release +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DCMAKE_GENERATOR:STRING=Ninja +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} + /Users/talksik/Documents/code/adhd/build/Qt_6_8_6_for_macOS-Release + + + + + all + + false + + true + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Release + CMakeProjectManager.CMakeBuildConfiguration + + + RelWithDebInfo + 2 + false + + -DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} +-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON +-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DCMAKE_GENERATOR:STRING=Ninja +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} + /Users/talksik/Documents/code/adhd/build/Qt_6_8_6_for_macOS-RelWithDebInfo + + + + + all + + false + + true + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Release with Debug Information + CMakeProjectManager.CMakeBuildConfiguration + + + RelWithDebInfo + 2 + false + + -DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} +-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON +-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DCMAKE_GENERATOR:STRING=Ninja +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} + 0 + /Users/talksik/Documents/code/adhd/build/Qt_6_8_6_for_macOS-Profile + + + + + all + + false + + true + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Profile + CMakeProjectManager.CMakeBuildConfiguration + + + MinSizeRel + 2 + false + + -DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} +-DCMAKE_COLOR_DIAGNOSTICS:BOOL=ON +-DCMAKE_BUILD_TYPE:STRING=MinSizeRel +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DCMAKE_GENERATOR:STRING=Ninja +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} + /Users/talksik/Documents/code/adhd/build/Qt_6_8_6_for_macOS-MinSizeRel + + + + + all + + false + + true + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Minimum Size Release + CMakeProjectManager.CMakeBuildConfiguration + + 5 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + + + + + + + + false + + true + ApplicationManagerPlugin.Deploy.CMakePackageStep + + + install-package --acknowledge + true + Install Application Manager package + ApplicationManagerPlugin.Deploy.InstallPackageStep + + + + + + + + 2 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ApplicationManagerPlugin.Deploy.Configuration + + 2 + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + + ProjectExplorer.CustomExecutableRunConfiguration + + false + true + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..a442b66 --- /dev/null +++ b/TODO.md @@ -0,0 +1,45 @@ +## Todo +- [ ] tab widget: one for settings, one for sessions, one for active session editing +- [ ] active session view would show timer, pause, end, and edit session details +- [ ] create new session button +- [ ] click on a session to set as active + +- [ ] create separate overlay that the main app can listen to and notify of active session changes + +## Ideas +- Show me all sessions and the current session +- Sum up how many sessions I won/lost +- A session can simply be described by simple paragraph of what you'll do, maybe markdown +- Less rigidity in the sense of typing out thoughts and session goals...less due dates. Capture first, do later. +- Later: allow notes within a session, jot things down, all live in the same folder + - It's like a todo.md +- Every session is based on week # - year - session x +- Every session is an individual document that lives on disk +- See which week of the year that I'm in +- See progress bar of how much of the year is done or percentage +- Show stats on how many sessions total I've won/lost +- Display a separate overlay window that shows me my active working item in center bottom in a pill +- Can commit to github for "sync" and even include an input url to auto-upload the data directory to github + +- overlay can show the first line of the session doc (or separate input) +- review of session can be: `Win`, `Neutral`, or `Loss` + +## Flow +- select location to save all your session data in markdown files +- data in the app parses the markdown file and uses it as the database, including the frontmatter + +1. At the start of the day/session, set an intention by creating a session +2. Enter the top things to do this session in plain english (list or paragraph in markdown) +3. Click start session, and clock runs. Can pause, or stop. +4. When done for the session, select "Close session"...it will ask you to grade the session +5. View previous sessions in a list + +## marketing +- local-only +- access files and metadata +- simple video demo OR flowy research-site page that has simple visuals + +## Launch +- [ ] install via brew +- [ ] open source github +- [ ] hackernews "for developers with adhd"...purist site diff --git a/main.cpp b/main.cpp index 1e87fcd..df55bee 100644 --- a/main.cpp +++ b/main.cpp @@ -1,6 +1,7 @@ #include #include #include "mainwindow.h" +#include "service.h" int main(int argc, char *argv[]) { std::cout << "Starting adhd program" << std::endl; diff --git a/mainwindow.cpp b/mainwindow.cpp index bdf4f15..b694bda 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,13 +1,32 @@ #include "mainwindow.h" +#include "service.h" +#include "sessionslistmodel.h" +#include #include +#include +#include "ui_mainwindow.h" -MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent), m_service(new Service(this)) { - QWidget *centralWidget = new QWidget(this); - QLabel *label = new QLabel(centralWidget); - label->setText("Let's get some stuff done!"); + ui = new Ui::MainWindow; + ui->setupUi(this); - setCentralWidget(centralWidget); + m_listModel = new SessionsListModel(m_service, this); + ui->listView->setModel(m_listModel); } MainWindow::~MainWindow() {} + +void MainWindow::on_selectVaultButton_clicked() +{ + QString dir = QFileDialog::getExistingDirectory(this, + "Select Directory", + QDir::homePath(), + QFileDialog::ShowDirsOnly); + if (!dir.isEmpty()) { + m_service->setVaultDirectory(dir); + ui->tabWidget->setCurrentIndex(1); + ui->vaultLabel->setText(m_service->vaultDirectory()); + } +} diff --git a/mainwindow.h b/mainwindow.h index 2869038..e62dba7 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -4,15 +4,30 @@ #include #include +class Service; +class SessionsListModel; + QT_BEGIN_NAMESPACE QT_END_NAMESPACE +namespace Ui { +class MainWindow; +} + class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); + + private slots: + void on_selectVaultButton_clicked(); + + private: + Ui::MainWindow *ui; + Service *m_service; + SessionsListModel *m_listModel; }; #endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui new file mode 100644 index 0000000..0fd782c --- /dev/null +++ b/mainwindow.ui @@ -0,0 +1,96 @@ + + + MainWindow + + + + 0 + 0 + 400 + 600 + + + + MainWindow + + + + + + + 0 + + + + Settings + + + + + 140 + 210 + 88 + 16 + + + + Select a folder + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + 120 + 240 + 121 + 32 + + + + Select vault + + + + + + true + + + + + + Sessions + + + + + + + + + + Active + + + + + + + + + + 0 + 0 + 400 + 30 + + + + + + + + diff --git a/namegenerator.cpp b/namegenerator.cpp new file mode 100644 index 0000000..8e307ad --- /dev/null +++ b/namegenerator.cpp @@ -0,0 +1,9 @@ +#include "namegenerator.cpp" + +namespace { + constexpr char data[] +} + +QString generate() { + return; +} diff --git a/namegenerator.h b/namegenerator.h new file mode 100644 index 0000000..4aaa210 --- /dev/null +++ b/namegenerator.h @@ -0,0 +1,8 @@ +#ifndef NAMEGENERATOR_H +#define NAMEGENERATOR_H + +#include + +QString generate(); + +#endif //NAMEGENERATOR_H diff --git a/service.cpp b/service.cpp new file mode 100644 index 0000000..e3fd559 --- /dev/null +++ b/service.cpp @@ -0,0 +1,270 @@ +#include "service.h" +#include +#include +#include +#include + +bool Session::isComplete() const { + return result != UNSPECIFIED; +} + +Service::Service(QObject *parent) : QObject(parent) { +} + +Service::~Service() {} + +QString Service::vaultDirectory() const { + return m_vaultDirectory; +} + +int Service::sessionCount() const { + return m_sessions.size(); +} + +int Service::completedSessionCount() const { + int count = 0; + for (const Session &session : m_sessions) { + if (session.isComplete()) { + count++; + } + } + return count; +} + +int Service::totalWins() const { + int count = 0; + for (const Session &session : m_sessions) { + if (session.result == Session::WIN) { + count++; + } + } + return count; +} + +int Service::totalLosses() const { + int count = 0; + for (const Session &session : m_sessions) { + if (session.result == Session::LOSS) { + count++; + } + } + return count; +} + +int Service::totalNeutrals() const { + int count = 0; + for (const Session &session : m_sessions) { + if (session.result == Session::NEUTRAL) { + count++; + } + } + return count; +} + +QList Service::sessions() const { + return m_sessions; +} + +void Service::setVaultDirectory(const QString &path) { + m_sessions.clear(); + + qDebug() << "Setting vault directory to: " << path; + + QDir dir = QDir(path); + if (!dir.exists()) { + qWarning() << "Vault directory does not exist"; + return; + } + + m_vaultDirectory = path; + + dir.setFilter(QDir::Files); + dir.setSorting(QDir::Time); + + QFileInfoList list = dir.entryInfoList(); + for (int i = 0; i < list.size(); ++i) { + QFileInfo fileInfo = list.at(i); + + if (fileInfo.completeSuffix() != QStringLiteral("md")) { + qWarning() << "Skipping invalid file format: " << fileInfo.completeSuffix(); + continue; + } + + qDebug() << "Parsing file: " << fileInfo.absoluteFilePath(); + std::optional session = parseFile(fileInfo); + if (session) { + m_sessions.append(*session); + } else { + qWarning() << "Failed to parse session file: " << fileInfo.absoluteFilePath(); + } + } + + emit sessionsChanged(); +} + +Session Service::createSession(const QString &oneliner, const QString &content) { + assert(!m_vaultDirectory.isEmpty() && "Vault directory must be set before creating sessions"); + + QString uuid = QUuid().toString(QUuid::WithoutBraces); + + Session session = {}; + session.id = uuid; + session.oneliner = oneliner; + session.durationMs = 0; + session.content = content; + session.result = Session::UNSPECIFIED; + session.createdAt = QDateTime::currentDateTime(); + session.lastModifiedAt = QDateTime::currentDateTime(); + + overwriteSessionFile(session); + + m_sessions.prepend(session); + emit sessionsChanged(); + + return session; +} + +void Service::updateSession(const Session &session) { + overwriteSessionFile(session); + for (int i = 0; i < m_sessions.size(); ++i) { + if (m_sessions[i].id == session.id) { + m_sessions[i] = session; + break; + } + } + emit sessionsChanged(); +} + +void Service::deleteSession(const QString &sessionId) { + QDir dir = QDir(m_vaultDirectory); + QString filePath = dir.filePath(sessionId + ".md"); + QFile file = QFile(filePath); + if (!file.remove()) { + qWarning() << "Failed to delete session file: " << filePath; + return; + } + + for (int i = 0; i < m_sessions.size(); ++i) { + if (m_sessions[i].id == sessionId) { + m_sessions.removeAt(i); + break; + } + } + emit sessionsChanged(); +} + +std::optional Service::parseFile(const QFileInfo &fileInfo) { + QString path = fileInfo.absoluteFilePath(); + QFile file = QFile(path); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + return std::nullopt; + } + + Session session = {}; + session.id = file.fileName(); + session.content = ""; + session.oneliner = ""; + session.durationMs = 0; + session.result = Session::UNSPECIFIED; + session.createdAt = fileInfo.birthTime(); + session.lastModifiedAt = fileInfo.lastModified(); + + QTextStream in(&file); + int separatorCounts = 0; + while (!in.atEnd()) { + QString line = in.readLine(); + // qDebug() << "Reading line: " << line; + + if (separatorCounts >= 2) { + session.content += line + '\n'; + continue; + } + + switch (line[0].toLatin1()) { + case '-': + separatorCounts++; + continue; + break; + default: + break; + } + // parse each key value pair + QStringList parts = line.split(':'); + if (parts.size() != 2) { + continue; + } + QString key = parts[0].trimmed(); + QString value = parts[1].trimmed(); + if (key == "Oneliner") { + session.oneliner = value; + continue; + } else if (key == "DurationMs") { + session.durationMs = value.toInt(); + continue; + } else if (key == "Result") { + session.result = parseResult(value); + continue; + } + } + + qDebug() << "Parsed oneliner: " << session.oneliner; + qDebug() << "Parsed durationMs: " << session.durationMs; + qDebug() << "Parsed result: " << static_cast(session.result); + qDebug() << "Parsed content: " << session.content; + qDebug() << "Created at: " << session.createdAt; + qDebug() << "Last modified at: " << session.lastModifiedAt; + + file.close(); + + return session; +} + +Session::Result Service::parseResult(const QString &resultStr) { + if (resultStr == "Win") { + return Session::WIN; + } else if (resultStr == "Loss") { + return Session::LOSS; + } else if (resultStr == "Neutral") { + return Session::NEUTRAL; + } else { + return Session::UNSPECIFIED; + } +} + +void Service::overwriteSessionFile(const Session &session) +{ + // rewrites the entire file + QDir dir = QDir(m_vaultDirectory); + QString filePath = dir.filePath(session.id + ".md"); + QFile file = QFile(filePath); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + qWarning() << "Failed to update session file: " << filePath; + return; + } + + QTextStream out(&file); + out << "---\n"; + out << "Oneliner: " << session.oneliner << "\n"; + out << "DurationMs: " << session.durationMs << "\n"; + 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 = ""; + break; + } + if (!resultStr.isEmpty()) { + out << "Result: " << resultStr << "\n"; + } + out << "---\n"; + out << session.content << "\n"; + file.close(); +} diff --git a/service.h b/service.h new file mode 100644 index 0000000..011bbad --- /dev/null +++ b/service.h @@ -0,0 +1,82 @@ +#ifndef SERVICE_H +#define SERVICE_H + +#include +#include +#include +#include +#include +#include + +struct Session +{ + enum Result { + UNSPECIFIED = 0, + WIN, + LOSS, + NEUTRAL + }; + // Becomes the file name + QString id; + // Stored in the frontmatter under 'Oneliner' key + QString oneliner; + // Stored in the frontmatter + int durationMs; + // After the --- onwards, serves as general scratchpad + QString content; + // Stored in the frontmatter under 'Result' key + Result result; + + QDateTime lastModifiedAt; + QDateTime createdAt; + + bool isComplete() const; +}; + +class Service : public QObject { + Q_OBJECT + +public: + explicit Service(QObject *parent = nullptr); + ~Service(); + + QList sessions() const; + QString vaultDirectory() const; + + int sessionCount() const; + int completedSessionCount() const; + int totalWins() const; + int totalLosses() const; + int totalNeutrals() const; + +public slots: + /// @brief sets the directory from which to read/write sessions + /// Fails silently if not a valid path + /// Loads sessions from this path + void setVaultDirectory(const QString &path); + + /// @brief creates the session and stores in sessions list + /// @returns the newly created session + Session createSession(const QString &oneliner, const QString &content); + + /// @brief updates the session + /// Updates session in the sessions list, and the file associated + void updateSession(const Session &session); + + void deleteSession(const QString &sessionId); + +signals: + void sessionsChanged(); + +private: + QString m_vaultDirectory; + QList m_sessions; + + /// @returns null Session if not a valid session markdown document + std::optional parseFile(const QFileInfo &fileInfo); + + Session::Result parseResult(const QString &resultStr); + void overwriteSessionFile(const Session &session); +}; + +#endif //SERVICE_H diff --git a/sessionslistmodel.cpp b/sessionslistmodel.cpp new file mode 100644 index 0000000..77239b8 --- /dev/null +++ b/sessionslistmodel.cpp @@ -0,0 +1,34 @@ +#include "sessionslistmodel.h" + +SessionsListModel::SessionsListModel(Service *service, QObject *parent) + : QAbstractListModel{parent}, m_service(service) +{ + connect(m_service, &Service::sessionsChanged, this, [this]() { + beginResetModel(); + endResetModel(); + }); +} + +int SessionsListModel::rowCount(const QModelIndex &parent) const { + return m_service->sessions().size(); +} + +int SessionsListModel::columnCount(const QModelIndex &parent) const { + return 1; +} + +QVariant SessionsListModel::data(const QModelIndex &index, int role) const { + if (index.row() < 0 || index.row() >= rowCount()) { + return QVariant(); + } else if (!index.isValid()) { + return QVariant(); + } + + const Session session = m_service->sessions().at(index.row()); + switch (role) { + case Qt::DisplayRole: + return session.oneliner; + default: + return QVariant(); + } +} diff --git a/sessionslistmodel.h b/sessionslistmodel.h new file mode 100644 index 0000000..1419bf4 --- /dev/null +++ b/sessionslistmodel.h @@ -0,0 +1,22 @@ +#ifndef SESSIONSLISTMODEL_H +#define SESSIONSLISTMODEL_H + +#include +#include +#include "service.h" + +class SessionsListModel : public QAbstractListModel +{ + Q_OBJECT +public: + explicit SessionsListModel(Service *service, QObject *parent = nullptr); + + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + int columnCount(const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role) const; + +private: + Service *m_service; +}; + +#endif // SESSIONSLISTMODEL_H