250 lines
9.9 KiB
C++
250 lines
9.9 KiB
C++
#pragma once
|
|
|
|
#include "Breadcrumb.hpp"
|
|
#include "Button.hpp"
|
|
#include "Collapsible.hpp"
|
|
#include "ColorPicker.hpp"
|
|
#include "Containers.hpp"
|
|
#include "Dialog.hpp"
|
|
#include "KanbanBoard.hpp"
|
|
#include "Docking.hpp"
|
|
#include "Control.hpp"
|
|
#include "Label.hpp"
|
|
#include "ListView.hpp"
|
|
#include "Menus.hpp"
|
|
#include "TabView.hpp"
|
|
#include "TreeView.hpp"
|
|
#include "PageStack.hpp"
|
|
#include "PageList.hpp"
|
|
#include "SearchField.hpp"
|
|
#include "SecretsPanel.hpp"
|
|
#include "SessionPanel.hpp"
|
|
#include "Commands.hpp"
|
|
#include "SkillsPanel.hpp"
|
|
#include "TimelineScrubber.hpp"
|
|
#include "TaskBoard.hpp"
|
|
#include "TaskCard.hpp"
|
|
#include "TaskContextMenu.hpp"
|
|
#include "TaskKanban.hpp"
|
|
#include "TaskPage.hpp"
|
|
#include "TaskPanels.hpp"
|
|
#include "TaskStore.hpp"
|
|
#include "StatusChip.hpp"
|
|
#include "TaskWidget.hpp"
|
|
#include "TaskInbox.hpp"
|
|
#include "TextField.hpp"
|
|
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
namespace tp::taskboard {
|
|
|
|
class TaskTreeView : public ui::Panel {
|
|
public:
|
|
explicit TaskTreeView(
|
|
TaskBoard& board,
|
|
ui::FileSystem& files = ui::FileSystem::local(),
|
|
const AppStorage& storage = taskboardStorage(),
|
|
std::string skillsDirectory = defaultSkillsDirectory()
|
|
);
|
|
|
|
[[nodiscard]] static std::string defaultSkillsDirectory();
|
|
|
|
[[nodiscard]] TaskWidget& taskWidget() { return mTaskWidget; }
|
|
[[nodiscard]] TaskPage& page() { return mTaskWidget.page; }
|
|
[[nodiscard]] TaskHeader& header() { return mTaskWidget.description.header; }
|
|
[[nodiscard]] TaskDetails& details() { return mTaskWidget.page.details(); }
|
|
[[nodiscard]] SessionTab& session() { return mTaskWidget.session; }
|
|
[[nodiscard]] TaskInbox& inbox() { return mInbox; }
|
|
[[nodiscard]] ui::SearchField& search() { return mSearch; }
|
|
[[nodiscard]] ui::PageStack& pages() { return mTaskWidget.taskPages; }
|
|
[[nodiscard]] ui::Breadcrumb& breadcrumb() { return mBreadcrumb; }
|
|
[[nodiscard]] ui::TreeView& graph() { return mGraph; }
|
|
[[nodiscard]] ui::KanbanBoard& kanban() { return mKanban; }
|
|
[[nodiscard]] size_t kanbanColumn(TaskStatus status) const { return mKanbanModel.columnOf(status); }
|
|
[[nodiscard]] SkillsPanel& skills() { return mSkills; }
|
|
[[nodiscard]] SecretsPanel& secrets() { return mSecrets; }
|
|
[[nodiscard]] ui::TabView& views() { return mViews; }
|
|
[[nodiscard]] ui::DockArea& dock() { return mDock; }
|
|
[[nodiscard]] ui::DockPanel& taskPanel() { return mTaskPanel; }
|
|
[[nodiscard]] ui::DockPanel& taskListPanel() { return mTaskListPanel; }
|
|
[[nodiscard]] ui::PageList& taskList() { return mTaskList; }
|
|
[[nodiscard]] ui::ListView& taskListItems() { return mTaskListItems; }
|
|
[[nodiscard]] ui::ConfirmDialog& graphDeleteConfirm() { return mGraphDeleteConfirm; }
|
|
[[nodiscard]] TaskContextMenu& contextMenu() { return mContextMenu; }
|
|
[[nodiscard]] const TaskClipboard& clipboard() const { return mClipboard; }
|
|
[[nodiscard]] TaskDescriptionPanel& description() { return mTaskWidget.description; }
|
|
[[nodiscard]] TaskMessagesPanel& messages() { return mTaskWidget.messages; }
|
|
[[nodiscard]] TaskWorkspacePanel& workspace() { return mTaskWidget.workspace; }
|
|
[[nodiscard]] ui::ScrollView& sessionScroll() { return mTaskWidget.sessionScroll; }
|
|
[[nodiscard]] ui::FullViewFrame* zoomFrame() const { return mZoomFrame; }
|
|
[[nodiscard]] ui::TimelineScrubber& timeline() { return mTimeline; }
|
|
void setReadOnly(bool readOnly);
|
|
void showTimeline(bool shown);
|
|
[[nodiscard]] bool isTimelineShown() const { return mTimeline.isVisible(); }
|
|
[[nodiscard]] bool isReadOnly() const { return mReadOnly; }
|
|
[[nodiscard]] bool isInGraph(ui::NodeId id) const;
|
|
[[nodiscard]] std::optional<ui::NodeId> currentTask() const { return mTaskWidget.page.shownTask(); }
|
|
|
|
void openTask(std::optional<ui::NodeId> id);
|
|
void openTerminal(ui::NodeId id);
|
|
void createTask();
|
|
void selectRoot(std::optional<ui::NodeId> root);
|
|
void toggleRoot();
|
|
void setStore(TaskStore* store);
|
|
void setSecretsStore(SecretsStore* store) { mSecrets.setStore(store); }
|
|
void setAgentOptions(AgentOptions options) { session().setAgentOptions(std::move(options)); }
|
|
void showConflicts(const std::vector<std::string>& summaries);
|
|
void showSaveState(size_t unsavedChanges, bool saving);
|
|
void boardReloaded();
|
|
void attachMenus(ui::MenuBar& menuBar, std::function<void()> quit);
|
|
void openPanels();
|
|
void openTaskPanel();
|
|
|
|
void setOnBoardEdited(std::function<void()> callback) { mOnBoardEdited = std::move(callback); }
|
|
void setOnSave(std::function<void()> callback) { mOnSave = std::move(callback); }
|
|
void setOnOpenSession(std::function<void(ui::NodeId)> callback);
|
|
|
|
ui::Menu fileMenu;
|
|
ui::Menu editMenu;
|
|
ui::Menu viewMenu;
|
|
ui::Menu connectionMenu;
|
|
ui::Label status;
|
|
ui::StatusChip connectionStatus;
|
|
|
|
[[nodiscard]] std::vector<ui::SearchResult> searchResults(const std::string& query);
|
|
|
|
protected:
|
|
void onEvent(ui::Event& event) override;
|
|
void filterDescendantEvent(ui::Event& event) override;
|
|
|
|
private:
|
|
enum class BoardCommand { NewTask, OpenSession, Save, Search, Copy, Paste, DeleteTask, ToggleRoot };
|
|
|
|
void openActivity(const TaskActivity& entry);
|
|
void buildMenus();
|
|
[[nodiscard]] std::string rootMenuTitle() const;
|
|
void buildGraph();
|
|
void buildKanban();
|
|
void bindTaskWidget();
|
|
void updateKanbanCard(ui::Card& card) const;
|
|
void kanbanMoved(const ui::KanbanMove& move);
|
|
void refreshKanban();
|
|
void refreshTaskList();
|
|
void showTaskListDetail(ui::NodeId id);
|
|
void defineCommands();
|
|
void graphSelectionChanged();
|
|
void currentPageChanged();
|
|
void reportEditing(std::optional<std::pair<ui::NodeId, TaskField>> editing);
|
|
enum class SelectionSource { Board, Graph, Kanban };
|
|
void showFullView(ui::FullViewFrame& frame, std::optional<ui::NodeId> id, SelectionSource source);
|
|
void zoomInto(ui::FullViewFrame& frame);
|
|
void zoomOutOf(ui::FullViewFrame& frame);
|
|
void leaveZoom();
|
|
|
|
void focusTask(ui::NodeId id, SelectionSource source);
|
|
void syncSelection(std::optional<ui::NodeId> id);
|
|
void mainViewChanged(size_t index);
|
|
void focusCreatedTask(ui::NodeId id);
|
|
void createChildTask(ui::NodeId parent);
|
|
void createTaskAt(std::optional<ui::NodeId> parent, std::optional<TaskStatus> status);
|
|
[[nodiscard]] TaskContextMenu::Actions contextMenuActions();
|
|
void showContextMenu(TaskMenuTarget target, Vec2F windowPoint);
|
|
void copyTasks(const std::vector<ui::NodeId>& ids);
|
|
void pasteTasks(std::optional<ui::NodeId> parent, std::optional<TaskStatus> status);
|
|
void setTasksStatus(const std::vector<ui::NodeId>& ids, TaskStatus status);
|
|
[[nodiscard]] std::vector<ui::NodeId> mainViewSelection() const;
|
|
[[nodiscard]] bool isMainViewFocused();
|
|
void reparentTask(ui::NodeId child, ui::NodeId parent);
|
|
[[nodiscard]] std::unique_ptr<ui::Card> createCard(ui::TreeItemId id);
|
|
void updateCard(ui::Card& card) const;
|
|
void refreshGraph();
|
|
void showPath(const std::vector<std::optional<ui::NodeId>>& path);
|
|
void pathChosen(size_t index);
|
|
void deleteCurrentTask();
|
|
void askDeleteTasks(const std::vector<ui::NodeId>& ids);
|
|
void deleteTasks(const std::vector<ui::NodeId>& ids);
|
|
void focusMainView();
|
|
[[nodiscard]] std::vector<ui::NodeId> selectedTasks() const;
|
|
void boardEdited();
|
|
void refreshSummary();
|
|
[[nodiscard]] std::vector<std::optional<ui::NodeId>> pathTo(std::optional<ui::NodeId> id) const;
|
|
[[nodiscard]] std::string pageTitle(std::optional<ui::NodeId> id) const;
|
|
|
|
TaskBoard& mBoard;
|
|
TaskStore* mStore = nullptr;
|
|
std::optional<ui::NodeId> mDisplayedRoot;
|
|
ui::Menu mRootMenu;
|
|
ui::Menu mRecentRootsMenu;
|
|
ui::PopupButton* mRootMenuButton = nullptr;
|
|
TaskInbox mInbox;
|
|
std::optional<TaskActivity> mPendingActivity;
|
|
ui::SearchField mSearch;
|
|
unsigned mSearchGeneration = 0;
|
|
std::vector<ui::NodeId> mSearchChoices;
|
|
class GraphModel : public ui::TreeModel {
|
|
public:
|
|
explicit GraphModel(const TaskTreeView& view) :
|
|
mView(view) {}
|
|
|
|
[[nodiscard]] std::vector<ui::TreeItemId> roots() const override;
|
|
[[nodiscard]] std::vector<ui::TreeItemId> children(ui::TreeItemId id) const override;
|
|
[[nodiscard]] std::vector<std::pair<ui::TreeItemId, ui::TreeItemId>> crossLinks() const override;
|
|
[[nodiscard]] bool canReparent(ui::TreeItemId child, ui::TreeItemId parent) const override;
|
|
|
|
private:
|
|
const TaskTreeView& mView;
|
|
};
|
|
|
|
SelectionSource mSelectionSource = SelectionSource::Board;
|
|
bool mSyncingSelection = false;
|
|
GraphModel mGraphModel{ *this };
|
|
ui::TreeView mGraph{ mGraphModel };
|
|
KanbanColumnSettings mKanbanColumns;
|
|
TaskKanbanModel mKanbanModel{ mBoard, mKanbanColumns };
|
|
ui::KanbanBoard mKanban{ mKanbanModel };
|
|
SkillsPanel mSkills;
|
|
SecretsPanel mSecrets;
|
|
ui::TabView mViews;
|
|
ui::DockArea mDock;
|
|
ui::DockPanel mTaskPanel{ "Task", ui::DockSide::Right };
|
|
ui::Widget mTaskListRoot;
|
|
ui::Widget mTaskListControls;
|
|
ui::ListView mTaskListItems{ ui::ListView::Mode::Single };
|
|
ui::PageList mTaskList{ "Tasks", mTaskListRoot };
|
|
ui::Dropdown mTaskListSort{ { "Updated time", "Created time" } };
|
|
ui::Dropdown mTaskListStatus{
|
|
{ "All statuses", "Backlog", "Queued", "In progress", "Daemon", "Review", "Done", "Blocked" }
|
|
};
|
|
std::vector<ui::NodeId> mTaskListIds;
|
|
std::optional<ui::NodeId> mTaskListShown;
|
|
ui::Widget mTaskListDetail;
|
|
ui::Label mTaskListSummary;
|
|
ui::Label mTaskListDescription;
|
|
ui::Button mTaskListOpen{ "Open task" };
|
|
ui::DockPanel mTaskListPanel{ "Tasks", ui::DockSide::Left };
|
|
TaskWidget mTaskWidget;
|
|
ui::FullViewFrame* mZoomFrame = nullptr;
|
|
bool mTaskPanelOpenBeforeZoom = false;
|
|
ui::Widget mPathArea;
|
|
ui::Widget mPathBar;
|
|
ui::Breadcrumb mBreadcrumb;
|
|
std::vector<std::optional<ui::NodeId>> mPath;
|
|
ui::Commands<BoardCommand> mCommands;
|
|
ui::ConfirmDialog mGraphDeleteConfirm;
|
|
TaskClipboard mClipboard;
|
|
TaskContextMenu mContextMenu{ mBoard, mClipboard, contextMenuActions() };
|
|
ui::TimelineScrubber mTimeline;
|
|
bool mReadOnly = false;
|
|
ui::Panel mStatusArea{ ui::Panel::Surface::None };
|
|
ui::StatusChip mSaveState;
|
|
std::function<void()> mQuit;
|
|
std::function<void()> mOnBoardEdited;
|
|
std::function<void()> mOnSave;
|
|
std::function<void(ui::NodeId)> mOnOpenSession;
|
|
};
|
|
}
|