Work on the graphical interface requirements tree
This commit is contained in:
@@ -28,7 +28,7 @@ struct Metadata
|
||||
//--------------------------------------------------------------
|
||||
struct Details
|
||||
{
|
||||
std::string title; // Short name or title of the requirement
|
||||
std::string title; // Short name or title of the requirement
|
||||
std::string description; // Detailed description of the requirement
|
||||
std::string acceptance_criteria; // Criteria that must be met for the requirement to be considered complete
|
||||
bool is_smart; // SMART indicator (Specific, Measurable, Achievable, Relevant, Time-bound)
|
||||
@@ -65,7 +65,7 @@ struct GetRequirementEvent : dBus::api::DefaultData<dBus::makeID("requirement.ge
|
||||
}
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
inline std::expected<Requirement, dBus::ReturnStatus> postGetRequirementRequest(dBus::Bus &bus, const std::string &uuid)
|
||||
inline std::expected<Requirement, dBus::ReturnStatus> postGetRequirementRequest(dBus::Bus &bus, const std::string &uuid = "")
|
||||
{
|
||||
auto eventData = GetRequirementEvent();
|
||||
eventData.uuid = std::move(uuid);
|
||||
|
||||
@@ -10,7 +10,8 @@ using namespace core::project;
|
||||
/* Default constructor */
|
||||
RequirementManager::RequirementManager()
|
||||
{
|
||||
m_rootRequirement = make_shared<RequirementItem>(*this); // Create the root requirement item and set it as the root requirement
|
||||
m_rootRequirement = make_shared<RequirementItem>(*this); // Create the root requirement item and set it as the root requirement
|
||||
m_requirementsByUuid[m_rootRequirement->metadata.uuid] = m_rootRequirement; // Register the root requirement item
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the root requirement */
|
||||
@@ -28,9 +29,13 @@ std::vector<RequirementManager::RequirementItemPtr> RequirementManager::getRootR
|
||||
/* Get a requirement by its UUID */
|
||||
RequirementManager::RequirementItemPtr RequirementManager::getRequirement(const std::string &uuid) const
|
||||
{
|
||||
if (uuid.empty())
|
||||
return m_rootRequirement; // Return the root requirement item pointer if the UUID is empty
|
||||
|
||||
if (m_requirementsByUuid.contains(uuid))
|
||||
return m_requirementsByUuid.at(uuid); // Return the requirement item pointer if found in the map
|
||||
return {}; // Return nullptr if the requirement with the given UUID is not found
|
||||
|
||||
return {}; // Return nullptr if the requirement with the given UUID is not found
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the vector of child requirement items of a requirement by its UUID */
|
||||
|
||||
@@ -14,7 +14,6 @@ RequirementItem::RequirementItem(RequirementManager &manager, const std::shared_
|
||||
m_parent = parentRequirement; // Set the m_parent requirement item
|
||||
|
||||
// Register this requirement item in the manager's internal map
|
||||
m_requirementManager.registerRequirement(shared_from_this());
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Convert this requirement item to an api::requirement::Requirement struct and return it */
|
||||
@@ -52,6 +51,8 @@ void RequirementItem::appendChild(const Requirement &child)
|
||||
{
|
||||
// Create a new requirement item for the child requirement and set this requirement item as its m_parent
|
||||
const auto childItem = make_shared<RequirementItem>(m_requirementManager, shared_from_this());
|
||||
m_requirementManager.registerRequirement(childItem); // Register the child requirement item
|
||||
|
||||
childItem->update(child); // Update the child requirement item with the provided child requirement data
|
||||
|
||||
// Append the child requirement item to the vector of children
|
||||
|
||||
@@ -45,13 +45,16 @@ MainFrame::MainFrame(dBus::Bus &bus)
|
||||
/* Creating controls */
|
||||
void MainFrame::createControls()
|
||||
{
|
||||
m_menuBar = new MenuBar(this);
|
||||
m_statusBar = new StatusBar(this);
|
||||
m_menuBar = new MenuBar(this);
|
||||
m_statusBar = new StatusBar(this);
|
||||
|
||||
const auto framePanel = new wxPanel(this /*, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER*/);
|
||||
m_toolBar = new ToolBar(framePanel);
|
||||
|
||||
m_pageNotebook = new wxNotebook(framePanel, wxID_ANY);
|
||||
m_pageNotebook = new wxNotebook(framePanel, wxID_ANY);
|
||||
|
||||
m_requirementsPanel = new RequirementsPanel(m_pageNotebook, m_bus);
|
||||
m_requirementsPanel->SetBackgroundColour(framePanel->GetBackgroundColour());
|
||||
|
||||
m_pageNotebook->AddPage(m_requirementsPanel, "Requirements", true);
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "requirementDetailPanel.h"
|
||||
|
||||
#include "resources/resources.h"
|
||||
|
||||
#include <wx/statline.h>
|
||||
|
||||
using namespace std;
|
||||
@@ -24,9 +26,9 @@ void RequirementDetailPanel::createControls()
|
||||
const auto mainPanel = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
|
||||
mainPanel->SetScrollRate(0, 10); // Set the scroll rate for the panel (vertical only)
|
||||
|
||||
const auto closeButton = new wxButton(this, wxID_ANY, _("Close"));
|
||||
const auto saveButton = new wxButton(this, wxID_ANY, _("Save"));
|
||||
const auto discardButton = new wxButton(this, wxID_ANY, _("Discard"));
|
||||
const auto closeButton = new wxBitmapButton(this, wxID_ANY, icon_xmark_24x24(), wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW | wxBORDER_NONE);
|
||||
const auto saveButton = new wxBitmapButton(this, wxID_ANY, icon_floppy_disk_24x24(), wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW | wxBORDER_NONE);
|
||||
const auto discardButton = new wxBitmapButton(this, wxID_ANY, icon_rotate_left_24x24(), wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW | wxBORDER_NONE);
|
||||
|
||||
// Controls positioning
|
||||
const auto ctrlSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#include "requirementListPanel.h"
|
||||
|
||||
#include "requirementTreeModel.h"
|
||||
#include "resources/resources.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace gui;
|
||||
//--------------------------------------------------------------
|
||||
@@ -19,5 +22,47 @@ RequirementListPanel::RequirementListPanel(wxWindow *parentWindow, dBus::Bus &bu
|
||||
/* Creating controls */
|
||||
void RequirementListPanel::createControls()
|
||||
{
|
||||
// Create the data view control
|
||||
m_dataViewCtrl = new wxDataViewCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER);
|
||||
m_dataViewCtrl->AppendTextColumn(_T("ID"), 0, wxDATAVIEW_CELL_INERT, 100, wxALIGN_LEFT);
|
||||
m_dataViewCtrl->AppendTextColumn(_("Title"), 1, wxDATAVIEW_CELL_INERT, 200, wxALIGN_LEFT);
|
||||
|
||||
const auto model = new RequirementTreeModel(m_bus);
|
||||
model->loadRootRequirement();
|
||||
m_dataViewCtrl->AssociateModel(model);
|
||||
model->DecRef(); // The control now owns the model, so we can release our reference
|
||||
|
||||
// Create other controls
|
||||
const auto addButton = new wxBitmapButton(this, wxID_ANY, icon_plus_24x24(), wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW | wxBORDER_NONE);
|
||||
addButton->Bind(wxEVT_BUTTON, &RequirementListPanel::on_addButtonClick, this);
|
||||
|
||||
const auto reloadButton = new wxBitmapButton(this, wxID_ANY, icon_rotate_left_24x24(), wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW | wxBORDER_NONE);
|
||||
reloadButton->Bind(wxEVT_BUTTON, &RequirementListPanel::on_reloadButtonClick, this);
|
||||
|
||||
// Controls positioning
|
||||
const auto buttonSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
buttonSizer->Add(addButton, wxSizerFlags(0).Border(wxALL, 5));
|
||||
buttonSizer->AddStretchSpacer();
|
||||
buttonSizer->Add(reloadButton, wxSizerFlags(0).Border(wxALL, 5));
|
||||
|
||||
const auto mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
mainSizer->Add(m_dataViewCtrl, wxSizerFlags(1).Expand().Border(wxALL, 5));
|
||||
mainSizer->Add(buttonSizer, wxSizerFlags(0).Expand());
|
||||
|
||||
SetSizer(mainSizer);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Process the add requirement click event */
|
||||
void RequirementListPanel::on_addButtonClick(wxCommandEvent &event)
|
||||
{
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Process the reload requirements click event */
|
||||
void RequirementListPanel::on_reloadButtonClick(wxCommandEvent &event)
|
||||
{
|
||||
// Reload all requirements
|
||||
|
||||
if (auto *model = dynamic_cast<RequirementTreeModel *>(m_dataViewCtrl->GetModel()))
|
||||
model->loadRootRequirement();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <dBus/dBus.h>
|
||||
#include <memory>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/wx.h>
|
||||
|
||||
namespace gui
|
||||
@@ -9,8 +11,8 @@ namespace gui
|
||||
class RequirementListPanel : public wxPanel
|
||||
{
|
||||
public:
|
||||
RequirementListPanel() = delete; // Default constructor
|
||||
virtual ~RequirementListPanel() = default; // Default destructor
|
||||
RequirementListPanel() = delete; // Default constructor
|
||||
virtual ~RequirementListPanel() = default; // Default destructor
|
||||
RequirementListPanel(const RequirementListPanel &obj) = delete; // Copy constructor
|
||||
RequirementListPanel(RequirementListPanel &&obj) noexcept = delete; // Move constructor
|
||||
RequirementListPanel &operator=(const RequirementListPanel &obj) = delete; // Copy assignment operator
|
||||
@@ -21,8 +23,15 @@ class RequirementListPanel : public wxPanel
|
||||
protected:
|
||||
dBus::Bus &m_bus; // Reference to the application bus
|
||||
|
||||
// Controls
|
||||
wxDataViewCtrl *m_dataViewCtrl = nullptr;
|
||||
|
||||
private:
|
||||
void createControls(); // Creating controls
|
||||
|
||||
// Events
|
||||
void on_addButtonClick(wxCommandEvent &event); // Process the add requirement click event
|
||||
void on_reloadButtonClick(wxCommandEvent &event); // Process the reload requirements click event
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
} // namespace gui
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
#include "requirementTreeModel.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace gui;
|
||||
//--------------------------------------------------------------
|
||||
/* Constructor */
|
||||
RequirementTreeModel::RequirementTreeModel(dBus::Bus &bus)
|
||||
: m_bus(bus)
|
||||
{
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
void RequirementTreeModel::GetValue(wxVariant &variant, const wxDataViewItem &item, const unsigned col) const
|
||||
{
|
||||
// Sanity check
|
||||
if (!item.IsOk())
|
||||
return;
|
||||
|
||||
const auto *data = static_cast<RequirementData *>(item.GetID());
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
// Return the appropriate value based on the column index
|
||||
switch (col)
|
||||
{
|
||||
case 0: // ID column
|
||||
variant = data->id;
|
||||
break;
|
||||
case 1: // Title column
|
||||
variant = data->title;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
bool RequirementTreeModel::SetValue(const wxVariant &variant, const wxDataViewItem &item, unsigned col)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
wxDataViewItem RequirementTreeModel::GetParent(const wxDataViewItem &item) const
|
||||
{
|
||||
if (!item.IsOk())
|
||||
return wxDataViewItem(nullptr);
|
||||
|
||||
// Get the requirement data associated with the item
|
||||
const auto *data = static_cast<RequirementData *>(item.GetID());
|
||||
if (!data)
|
||||
return wxDataViewItem(nullptr); // If the item is invalid, return an invalid item
|
||||
if (data == &m_rootRequirement)
|
||||
return wxDataViewItem(nullptr); // If the item is the root requirement, return an invalid item
|
||||
if (!data->parent)
|
||||
return wxDataViewItem(nullptr); // If the item has no parent, return an invalid item
|
||||
|
||||
return wxDataViewItem(data->parent);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
bool RequirementTreeModel::IsContainer(const wxDataViewItem &item) const
|
||||
{
|
||||
const auto *data = static_cast<RequirementData *>(item.GetID());
|
||||
if (!data)
|
||||
return true;
|
||||
|
||||
return !data->children.empty(); // An item is a container if it has children
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
bool RequirementTreeModel::HasContainerColumns(const wxDataViewItem &wxDataViewItem) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
unsigned int RequirementTreeModel::GetChildren(const wxDataViewItem &item, wxDataViewItemArray &children) const
|
||||
{
|
||||
const auto *data = static_cast<RequirementData *>(item.GetID());
|
||||
if (!data) // If the item is invalid, return the root requirement as the only child
|
||||
{
|
||||
children.Add(wxDataViewItem(const_cast<RequirementData *>(&m_rootRequirement)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Add the children of the current item to the children array
|
||||
for (const auto &child : data->children)
|
||||
{
|
||||
children.Add(wxDataViewItem(child.get()));
|
||||
}
|
||||
return static_cast<uint32_t>(data->children.size());
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
void RequirementTreeModel::loadRootRequirement()
|
||||
{
|
||||
// Get the root requirements from dBus
|
||||
try
|
||||
{
|
||||
const auto ret = api::requirement::postGetRequirementRequest(m_bus);
|
||||
if (!ret)
|
||||
{
|
||||
wxMessageBox("No manager found for requirement request.\n\n"
|
||||
"Error loading root requirements.",
|
||||
"Error",
|
||||
wxOK | wxICON_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
m_rootRequirement = RequirementData(*ret);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
// Handle the error (e.g., log it, show a message to the user, etc.)
|
||||
// For now, we just print the error message to the console
|
||||
std::cerr << "Error loading root requirements: " << e.what() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify the control that the data has changed
|
||||
//ItemChanged(wxDataViewItem(nullptr)); // Notify that the entire tree has changed
|
||||
//ItemsAdded(wxDataViewItem(nullptr), wxDataViewItemArray{ wxDataViewItem(&m_rootRequirement) });
|
||||
Cleared();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
RequirementData *RequirementTreeModel::AddChildRequirement(RequirementData *parent, std::unique_ptr<RequirementData> requirementData)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
void RequirementTreeModel::clear()
|
||||
{
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
RequirementData *RequirementTreeModel::GetRequirementData(const wxDataViewItem &item) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <api/requirement.h>
|
||||
#include <dBus/dBus.h>
|
||||
#include <filesystem>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/wx.h>
|
||||
@@ -9,18 +11,21 @@ namespace gui
|
||||
//--------------------------------------------------------------
|
||||
struct RequirementData
|
||||
{
|
||||
std::string uuid;
|
||||
std::string id;
|
||||
std::string title;
|
||||
|
||||
RequirementData *parent;
|
||||
RequirementData *parent = nullptr;
|
||||
std::vector<std::unique_ptr<RequirementData>> children;
|
||||
|
||||
RequirementData(std::string uuid_, std::string id_, std::string title_)
|
||||
: uuid(std::move(uuid_))
|
||||
, id(std::move(id_))
|
||||
, title(std::move(title_))
|
||||
RequirementData()
|
||||
{
|
||||
id = "badID";
|
||||
title = "badTITLE";
|
||||
}
|
||||
RequirementData(const api::requirement::Requirement &requirement)
|
||||
{
|
||||
id = "testID";
|
||||
title = "testTITLE";
|
||||
}
|
||||
|
||||
// Adds a child requirement
|
||||
@@ -39,34 +44,37 @@ struct RequirementData
|
||||
class RequirementTreeModel : public wxDataViewModel
|
||||
{
|
||||
public:
|
||||
RequirementTreeModel(); // Default constructor
|
||||
RequirementTreeModel() = delete; // Default constructor
|
||||
virtual ~RequirementTreeModel() = default; // Default destructor
|
||||
RequirementTreeModel(const RequirementTreeModel &obj) = delete; // Copy constructor
|
||||
RequirementTreeModel(RequirementTreeModel &&obj) noexcept = delete; // Move constructor
|
||||
RequirementTreeModel &operator=(const RequirementTreeModel &obj) = delete; // Copy assignment operator
|
||||
RequirementTreeModel &operator=(RequirementTreeModel &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
// wxDataViewModel overrides methods
|
||||
unsigned GetColumnCount() const override;
|
||||
wxString GetColumnType(unsigned) const override;
|
||||
explicit RequirementTreeModel(dBus::Bus &bus); // Constructor
|
||||
|
||||
// wxDataViewModel overrides methods
|
||||
void GetValue(wxVariant &variant, const wxDataViewItem &item, unsigned col) const override;
|
||||
bool SetValue(const wxVariant &variant, const wxDataViewItem &item, unsigned col) override;
|
||||
|
||||
wxDataViewItem GetParent(const wxDataViewItem &item) const override;
|
||||
bool IsContainer(const wxDataViewItem &item) const override;
|
||||
bool HasContainerColumns(const wxDataViewItem &) const override;
|
||||
[[nodiscard]] wxDataViewItem GetParent(const wxDataViewItem &item) const override;
|
||||
[[nodiscard]] bool IsContainer(const wxDataViewItem &item) const override;
|
||||
[[nodiscard]] bool HasContainerColumns(const wxDataViewItem &) const override;
|
||||
|
||||
unsigned GetChildren(const wxDataViewItem &item, wxDataViewItemArray &children) const override;
|
||||
unsigned int GetChildren(const wxDataViewItem &item, wxDataViewItemArray &children) const override;
|
||||
|
||||
// Tree data model specific methods
|
||||
RequirementData *AddRootRequirement(std::unique_ptr<RequirementData> requirementData); // Add a root requirement to the tree
|
||||
void loadRootRequirement(); // Add a root requirement to the tree
|
||||
|
||||
RequirementData *AddChildRequirement(RequirementData *parent, std::unique_ptr<RequirementData> requirementData); // Add a child requirement to a parent requirement
|
||||
void clear();
|
||||
RequirementData *GetRequirementData(const wxDataViewItem &item) const; // Get the requirement data associated with a given item
|
||||
|
||||
protected:
|
||||
std::vector<std::unique_ptr<RequirementData>> m_rootRequirement;
|
||||
dBus::Bus &m_bus; // Reference to the application bus
|
||||
|
||||
// std::vector<std::unique_ptr<RequirementData>> m_rootRequirement;
|
||||
RequirementData m_rootRequirement;
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
} // namespace gui
|
||||
|
||||
@@ -12,6 +12,8 @@ RequirementsPanel::RequirementsPanel(wxWindow *parentWindow, dBus::Bus &bus)
|
||||
, dBus::wxNode(this, bus)
|
||||
{
|
||||
// Initialization
|
||||
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
SetOwnBackgroundColour(GetBackgroundColour());
|
||||
|
||||
// Creating controls
|
||||
createControls();
|
||||
|
||||
Reference in New Issue
Block a user