Reorganize plan tab controls, add Delete Plan, context-sensitive move buttons

Restructure Task Controls into UnitEditor content area with nested
Underlying Unit section. Add Delete Plan button with confirmation dialog.
Rename plan task buttons for clarity. Grey out Task Controls when no plan
loaded. Disable Move Up/Move Down contextually based on task and unit
position in both Plans and Units tabs.
This commit is contained in:
Chris Punches
2026-03-14 18:21:33 -04:00
parent 85ad809887
commit 219e316822
6 changed files with 179 additions and 55 deletions

View File

@@ -73,37 +73,25 @@ PlanView::PlanView(Project& project, GrexConfig& grex_config)
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), task_listbox_); gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), task_listbox_);
gtk_box_append(GTK_BOX(task_controls_), scroll); gtk_box_append(GTK_BOX(task_controls_), scroll);
// Task action buttons — grouped by function // Plan Controls — plan-level actions
auto* plan_ctrl_frame = gtk_frame_new("Plan Controls"); auto* plan_ctrl_frame = gtk_frame_new("Plan Controls");
auto* btn_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8); auto* plan_btn_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
gtk_widget_set_margin_start(btn_box, 8); gtk_widget_set_margin_start(plan_btn_box, 8);
gtk_widget_set_margin_end(btn_box, 8); gtk_widget_set_margin_end(plan_btn_box, 8);
gtk_widget_set_margin_top(btn_box, 8); gtk_widget_set_margin_top(plan_btn_box, 8);
gtk_widget_set_margin_bottom(btn_box, 8); gtk_widget_set_margin_bottom(plan_btn_box, 8);
auto* task_edit_group = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_add_css_class(task_edit_group, "linked");
auto* btn_add = gtk_button_new_with_label("Add");
auto* btn_del = gtk_button_new_with_label("Delete");
gtk_box_append(GTK_BOX(task_edit_group), btn_add);
gtk_box_append(GTK_BOX(task_edit_group), btn_del);
gtk_box_append(GTK_BOX(btn_box), task_edit_group);
auto* move_group = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_add_css_class(move_group, "linked");
auto* btn_up = gtk_button_new_with_label("Move Up");
auto* btn_down = gtk_button_new_with_label("Move Down");
gtk_box_append(GTK_BOX(move_group), btn_up);
gtk_box_append(GTK_BOX(move_group), btn_down);
gtk_box_append(GTK_BOX(btn_box), move_group);
btn_save_plan_ = gtk_button_new_with_label("Save Plan"); btn_save_plan_ = gtk_button_new_with_label("Save Plan");
gtk_box_append(GTK_BOX(btn_box), btn_save_plan_); gtk_box_append(GTK_BOX(plan_btn_box), btn_save_plan_);
auto* btn_refresh = gtk_button_new_with_label("Refresh"); auto* btn_refresh = gtk_button_new_with_label("Reload Plan");
gtk_box_append(GTK_BOX(btn_box), btn_refresh); gtk_box_append(GTK_BOX(plan_btn_box), btn_refresh);
gtk_frame_set_child(GTK_FRAME(plan_ctrl_frame), btn_box); auto* btn_delete_plan = gtk_button_new_with_label("Delete Plan");
gtk_widget_add_css_class(btn_delete_plan, "destructive-action");
gtk_box_append(GTK_BOX(plan_btn_box), btn_delete_plan);
gtk_frame_set_child(GTK_FRAME(plan_ctrl_frame), plan_btn_box);
gtk_box_append(GTK_BOX(task_controls_), plan_ctrl_frame); gtk_box_append(GTK_BOX(task_controls_), plan_ctrl_frame);
gtk_box_append(GTK_BOX(left), task_controls_); gtk_box_append(GTK_BOX(left), task_controls_);
@@ -115,6 +103,38 @@ PlanView::PlanView(Project& project, GrexConfig& grex_config)
gtk_paned_set_end_child(GTK_PANED(root_), unit_editor_->widget()); gtk_paned_set_end_child(GTK_PANED(root_), unit_editor_->widget());
gtk_paned_set_shrink_end_child(GTK_PANED(root_), FALSE); gtk_paned_set_shrink_end_child(GTK_PANED(root_), FALSE);
// Task Controls — appended inside UnitEditor's content area
task_ctrl_frame_ = gtk_frame_new("Task Controls");
auto* task_ctrl_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
gtk_widget_set_margin_start(task_ctrl_box, 8);
gtk_widget_set_margin_end(task_ctrl_box, 8);
gtk_widget_set_margin_top(task_ctrl_box, 8);
gtk_widget_set_margin_bottom(task_ctrl_box, 8);
auto* task_btn_row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
auto* task_edit_group = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_add_css_class(task_edit_group, "linked");
auto* btn_add = gtk_button_new_with_label("Add Task");
auto* btn_del = gtk_button_new_with_label("Delete Task");
gtk_box_append(GTK_BOX(task_edit_group), btn_add);
gtk_box_append(GTK_BOX(task_edit_group), btn_del);
gtk_box_append(GTK_BOX(task_btn_row), task_edit_group);
auto* move_group = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_add_css_class(move_group, "linked");
btn_move_up_ = gtk_button_new_with_label("Move Up");
btn_move_down_ = gtk_button_new_with_label("Move Down");
gtk_box_append(GTK_BOX(move_group), btn_move_up_);
gtk_box_append(GTK_BOX(move_group), btn_move_down_);
gtk_box_append(GTK_BOX(task_btn_row), move_group);
gtk_box_append(GTK_BOX(task_ctrl_box), task_btn_row);
gtk_box_append(GTK_BOX(task_ctrl_box), unit_editor_->unit_controls());
gtk_frame_set_child(GTK_FRAME(task_ctrl_frame_), task_ctrl_box);
gtk_box_append(GTK_BOX(unit_editor_->content_box()), task_ctrl_frame_);
// Name change callback to refresh the task list row label // Name change callback to refresh the task list row label
unit_editor_->set_name_changed_callback([](const std::string&, void* data) { unit_editor_->set_name_changed_callback([](const std::string&, void* data) {
auto* self = static_cast<PlanView*>(data); auto* self = static_cast<PlanView*>(data);
@@ -131,8 +151,8 @@ PlanView::PlanView(Project& project, GrexConfig& grex_config)
g_signal_connect(task_listbox_, "row-selected", G_CALLBACK(on_task_selected), this); g_signal_connect(task_listbox_, "row-selected", G_CALLBACK(on_task_selected), this);
g_signal_connect(btn_add, "clicked", G_CALLBACK(on_add_task), this); g_signal_connect(btn_add, "clicked", G_CALLBACK(on_add_task), this);
g_signal_connect(btn_del, "clicked", G_CALLBACK(on_delete_task), this); g_signal_connect(btn_del, "clicked", G_CALLBACK(on_delete_task), this);
g_signal_connect(btn_up, "clicked", G_CALLBACK(on_move_up), this); g_signal_connect(btn_move_up_, "clicked", G_CALLBACK(on_move_up), this);
g_signal_connect(btn_down, "clicked", G_CALLBACK(on_move_down), this); g_signal_connect(btn_move_down_, "clicked", G_CALLBACK(on_move_down), this);
g_signal_connect(btn_save_plan_, "clicked", G_CALLBACK(+[](GtkButton*, gpointer d) { g_signal_connect(btn_save_plan_, "clicked", G_CALLBACK(+[](GtkButton*, gpointer d) {
auto* self = static_cast<PlanView*>(d); auto* self = static_cast<PlanView*>(d);
@@ -150,6 +170,8 @@ PlanView::PlanView(Project& project, GrexConfig& grex_config)
self->refresh(); self->refresh();
}), this); }), this);
g_signal_connect(btn_delete_plan, "clicked", G_CALLBACK(on_delete_plan), this);
update_plan_buttons(); update_plan_buttons();
} }
@@ -183,6 +205,7 @@ void PlanView::populate_task_list() {
gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), label); gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), label);
gtk_list_box_append(GTK_LIST_BOX(task_listbox_), row); gtk_list_box_append(GTK_LIST_BOX(task_listbox_), row);
} }
update_move_buttons();
} }
void PlanView::refresh_task_row(int idx) { void PlanView::refresh_task_row(int idx) {
@@ -216,6 +239,7 @@ void PlanView::select_task(int idx) {
Unit* unit = project_.find_unit(task.name); Unit* unit = project_.find_unit(task.name);
unit_editor_->load(&task, unit); unit_editor_->load(&task, unit);
update_move_buttons();
} }
// --- Open Plan --- // --- Open Plan ---
@@ -403,6 +427,50 @@ void PlanView::on_close_plan(GtkButton*, gpointer data) {
self->close_plan_impl(); self->close_plan_impl();
} }
void PlanView::on_delete_plan(GtkButton*, gpointer data) {
auto* self = static_cast<PlanView*>(data);
auto* plan = self->current_plan();
if (!plan) {
self->project_.report_status("Error: no plan loaded");
return;
}
auto filepath = plan->filepath;
auto name = filepath.filename().string();
// Confirm deletion
auto* parent = GTK_WINDOW(gtk_widget_get_ancestor(self->root_, GTK_TYPE_WINDOW));
auto* dialog = gtk_alert_dialog_new("Delete plan file '%s' from disk?", name.c_str());
gtk_alert_dialog_set_detail(dialog, "This action cannot be undone.");
gtk_alert_dialog_set_buttons(dialog, (const char*[]){"Cancel", "Delete", nullptr});
gtk_alert_dialog_set_cancel_button(dialog, 0);
gtk_alert_dialog_set_default_button(dialog, 0);
struct DeleteCtx { PlanView* view; std::filesystem::path path; std::string name; };
auto* ctx = new DeleteCtx{self, filepath, name};
gtk_alert_dialog_choose(dialog, parent, nullptr,
+[](GObject* source, GAsyncResult* res, gpointer d) {
auto* ctx = static_cast<DeleteCtx*>(d);
GError* error = nullptr;
int choice = gtk_alert_dialog_choose_finish(GTK_ALERT_DIALOG(source), res, &error);
if (error) { g_error_free(error); delete ctx; return; }
if (choice != 1) { delete ctx; return; } // not "Delete"
// Close the plan first
ctx->view->close_plan_impl();
// Delete from disk
std::error_code ec;
if (std::filesystem::remove(ctx->path, ec))
ctx->view->project_.report_status("Deleted plan file: " + ctx->name);
else
ctx->view->project_.report_status("Error: could not delete " + ctx->name +
(ec ? ": " + ec.message() : ""));
delete ctx;
}, ctx);
}
void PlanView::on_create_plan(GtkButton*, gpointer data) { void PlanView::on_create_plan(GtkButton*, gpointer data) {
auto* self = static_cast<PlanView*>(data); auto* self = static_cast<PlanView*>(data);
auto* window = gtk_widget_get_ancestor(self->root_, GTK_TYPE_WINDOW); auto* window = gtk_widget_get_ancestor(self->root_, GTK_TYPE_WINDOW);
@@ -467,10 +535,23 @@ void PlanView::update_plan_buttons() {
gtk_widget_set_visible(btn_create_plan_, !has_plan); gtk_widget_set_visible(btn_create_plan_, !has_plan);
gtk_widget_set_visible(btn_close_plan_, has_plan); gtk_widget_set_visible(btn_close_plan_, has_plan);
gtk_widget_set_sensitive(task_controls_, has_plan); gtk_widget_set_sensitive(task_controls_, has_plan);
gtk_widget_set_sensitive(task_ctrl_frame_, has_plan);
if (!has_plan) if (!has_plan)
unit_editor_->clear(); unit_editor_->clear();
} }
void PlanView::update_move_buttons() {
auto* plan = current_plan();
if (!plan || current_task_idx_ < 0) {
gtk_widget_set_sensitive(btn_move_up_, FALSE);
gtk_widget_set_sensitive(btn_move_down_, FALSE);
return;
}
int count = (int)plan->tasks.size();
gtk_widget_set_sensitive(btn_move_up_, current_task_idx_ > 0);
gtk_widget_set_sensitive(btn_move_down_, current_task_idx_ < count - 1);
}
void PlanView::refresh() { void PlanView::refresh() {
// reload units if paths now resolve // reload units if paths now resolve
project_.load_all_units(); project_.load_all_units();

View File

@@ -45,6 +45,9 @@ private:
GtkWidget* btn_close_plan_; GtkWidget* btn_close_plan_;
GtkWidget* btn_save_plan_; GtkWidget* btn_save_plan_;
GtkWidget* task_controls_; // container for task list + buttons GtkWidget* task_controls_; // container for task list + buttons
GtkWidget* task_ctrl_frame_; // Task Controls frame in right panel
GtkWidget* btn_move_up_;
GtkWidget* btn_move_down_;
UnitEditor* unit_editor_; UnitEditor* unit_editor_;
int current_task_idx_ = -1; int current_task_idx_ = -1;
@@ -64,11 +67,13 @@ private:
static void on_move_up(GtkButton* btn, gpointer data); static void on_move_up(GtkButton* btn, gpointer data);
static void on_move_down(GtkButton* btn, gpointer data); static void on_move_down(GtkButton* btn, gpointer data);
static void on_close_plan(GtkButton* btn, gpointer data); static void on_close_plan(GtkButton* btn, gpointer data);
static void on_delete_plan(GtkButton* btn, gpointer data);
static void on_create_plan(GtkButton* btn, gpointer data); static void on_create_plan(GtkButton* btn, gpointer data);
static void on_create_plan_response(GObject* source, GAsyncResult* res, gpointer data); static void on_create_plan_response(GObject* source, GAsyncResult* res, gpointer data);
void refresh_task_row(int idx); void refresh_task_row(int idx);
void update_plan_buttons(); void update_plan_buttons();
void update_move_buttons();
void close_plan_impl(); void close_plan_impl();
}; };

View File

@@ -27,17 +27,20 @@ UnitEditor::UnitEditor(Project& project, GrexConfig& grex_config)
root_ = gtk_scrolled_window_new(); root_ = gtk_scrolled_window_new();
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(root_), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(root_), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
auto* box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8); content_box_ = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
gtk_widget_set_margin_start(box, 16); gtk_widget_set_margin_start(content_box_, 16);
gtk_widget_set_margin_end(box, 16); gtk_widget_set_margin_end(content_box_, 16);
gtk_widget_set_margin_top(box, 16); gtk_widget_set_margin_top(content_box_, 16);
gtk_widget_set_margin_bottom(box, 16); gtk_widget_set_margin_bottom(content_box_, 16);
auto* box = content_box_;
// Task properties container — disabled when no task selected
task_section_ = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
// Task section header
auto* task_label = gtk_label_new(nullptr); auto* task_label = gtk_label_new(nullptr);
gtk_label_set_markup(GTK_LABEL(task_label), "<b>Task Properties</b>"); gtk_label_set_markup(GTK_LABEL(task_label), "<b>Task Properties</b>");
gtk_label_set_xalign(GTK_LABEL(task_label), 0.0f); gtk_label_set_xalign(GTK_LABEL(task_label), 0.0f);
gtk_box_append(GTK_BOX(box), task_label); gtk_box_append(GTK_BOX(task_section_), task_label);
auto* task_grid = gtk_grid_new(); auto* task_grid = gtk_grid_new();
gtk_grid_set_row_spacing(GTK_GRID(task_grid), 6); gtk_grid_set_row_spacing(GTK_GRID(task_grid), 6);
@@ -60,30 +63,33 @@ UnitEditor::UnitEditor(Project& project, GrexConfig& grex_config)
gtk_widget_set_hexpand(entry_comment_, TRUE); gtk_widget_set_hexpand(entry_comment_, TRUE);
gtk_grid_attach(GTK_GRID(task_grid), entry_comment_, 1, 1, 1, 1); gtk_grid_attach(GTK_GRID(task_grid), entry_comment_, 1, 1, 1, 1);
// Change/Select Unit button — aligned with the value column gtk_box_append(GTK_BOX(task_section_), task_grid);
gtk_box_append(GTK_BOX(box), task_section_);
// Underlying Unit controls — exposed for external placement
unit_controls_ = gtk_frame_new("Underlying Unit");
auto* unit_btn_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
gtk_widget_set_margin_start(unit_btn_box, 8);
gtk_widget_set_margin_end(unit_btn_box, 8);
gtk_widget_set_margin_top(unit_btn_box, 8);
gtk_widget_set_margin_bottom(unit_btn_box, 8);
btn_select_unit_ = gtk_button_new_with_label("Change/Select Unit..."); btn_select_unit_ = gtk_button_new_with_label("Change/Select Unit...");
gtk_widget_set_halign(btn_select_unit_, GTK_ALIGN_START);
g_signal_connect(btn_select_unit_, "clicked", G_CALLBACK(on_select_unit), this); g_signal_connect(btn_select_unit_, "clicked", G_CALLBACK(on_select_unit), this);
gtk_grid_attach(GTK_GRID(task_grid), btn_select_unit_, 1, 2, 1, 1); gtk_box_append(GTK_BOX(unit_btn_box), btn_select_unit_);
gtk_box_append(GTK_BOX(box), task_grid);
// Buttons below task properties
gtk_box_append(GTK_BOX(box), gtk_separator_new(GTK_ORIENTATION_HORIZONTAL));
auto* btn_row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
btn_edit_unit_ = gtk_button_new_with_label("Edit Unit..."); btn_edit_unit_ = gtk_button_new_with_label("Edit Unit...");
g_signal_connect(btn_edit_unit_, "clicked", G_CALLBACK(on_edit_unit), this); g_signal_connect(btn_edit_unit_, "clicked", G_CALLBACK(on_edit_unit), this);
gtk_box_append(GTK_BOX(btn_row), btn_edit_unit_); gtk_box_append(GTK_BOX(unit_btn_box), btn_edit_unit_);
btn_save_unit_ = gtk_button_new_with_label("Save Unit"); btn_save_unit_ = gtk_button_new_with_label("Save Unit");
g_signal_connect(btn_save_unit_, "clicked", G_CALLBACK(+[](GtkButton*, gpointer d) { g_signal_connect(btn_save_unit_, "clicked", G_CALLBACK(+[](GtkButton*, gpointer d) {
auto* self = static_cast<UnitEditor*>(d); auto* self = static_cast<UnitEditor*>(d);
self->save_current(); self->save_current();
}), this); }), this);
gtk_box_append(GTK_BOX(btn_row), btn_save_unit_); gtk_box_append(GTK_BOX(unit_btn_box), btn_save_unit_);
gtk_box_append(GTK_BOX(box), btn_row); gtk_frame_set_child(GTK_FRAME(unit_controls_), unit_btn_box);
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(root_), box); gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(root_), box);
@@ -108,12 +114,14 @@ void UnitEditor::clear() {
gtk_label_set_text(GTK_LABEL(name_display_), ""); gtk_label_set_text(GTK_LABEL(name_display_), "");
gtk_editable_set_text(GTK_EDITABLE(entry_comment_), ""); gtk_editable_set_text(GTK_EDITABLE(entry_comment_), "");
gtk_widget_set_sensitive(root_, FALSE); gtk_widget_set_sensitive(task_section_, FALSE);
gtk_widget_set_sensitive(unit_controls_, FALSE);
clear_dirty(); clear_dirty();
} }
void UnitEditor::load(Task* task, Unit* unit) { void UnitEditor::load(Task* task, Unit* unit) {
gtk_widget_set_sensitive(root_, TRUE); gtk_widget_set_sensitive(task_section_, TRUE);
gtk_widget_set_sensitive(unit_controls_, TRUE);
g_signal_handlers_disconnect_by_data(entry_comment_, this); g_signal_handlers_disconnect_by_data(entry_comment_, this);
current_task_ = task; current_task_ = task;
current_unit_ = unit; current_unit_ = unit;

View File

@@ -29,6 +29,8 @@ public:
UnitEditor(Project& project, GrexConfig& grex_config); UnitEditor(Project& project, GrexConfig& grex_config);
~UnitEditor() = default; ~UnitEditor() = default;
GtkWidget* widget() { return root_; } GtkWidget* widget() { return root_; }
GtkWidget* content_box() { return content_box_; }
GtkWidget* unit_controls() { return unit_controls_; }
void load(Task* task, Unit* unit); void load(Task* task, Unit* unit);
void clear(); void clear();
@@ -49,6 +51,9 @@ private:
Unit* current_unit_ = nullptr; Unit* current_unit_ = nullptr;
std::string current_unit_name_; std::string current_unit_name_;
GtkWidget* root_; GtkWidget* root_;
GtkWidget* content_box_;
GtkWidget* unit_controls_;
GtkWidget* task_section_;
// task fields // task fields
GtkWidget* name_display_; GtkWidget* name_display_;

View File

@@ -123,10 +123,10 @@ UnitsView::UnitsView(Project& project, GrexConfig& grex_config)
auto* move_group = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); auto* move_group = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_add_css_class(move_group, "linked"); gtk_widget_add_css_class(move_group, "linked");
auto* btn_move_up = gtk_button_new_with_label("Move Up"); btn_move_up_ = gtk_button_new_with_label("Move Up");
auto* btn_move_down = gtk_button_new_with_label("Move Down"); btn_move_down_ = gtk_button_new_with_label("Move Down");
gtk_box_append(GTK_BOX(move_group), btn_move_up); gtk_box_append(GTK_BOX(move_group), btn_move_up_);
gtk_box_append(GTK_BOX(move_group), btn_move_down); gtk_box_append(GTK_BOX(move_group), btn_move_down_);
gtk_box_append(GTK_BOX(unit_btn_box), move_group); gtk_box_append(GTK_BOX(unit_btn_box), move_group);
gtk_frame_set_child(GTK_FRAME(unit_ctrl_frame), unit_btn_box); gtk_frame_set_child(GTK_FRAME(unit_ctrl_frame), unit_btn_box);
@@ -142,9 +142,12 @@ UnitsView::UnitsView(Project& project, GrexConfig& grex_config)
g_signal_connect(btn_new_unit, "clicked", G_CALLBACK(on_new_unit), this); g_signal_connect(btn_new_unit, "clicked", G_CALLBACK(on_new_unit), this);
g_signal_connect(btn_del_unit, "clicked", G_CALLBACK(on_delete_unit), this); g_signal_connect(btn_del_unit, "clicked", G_CALLBACK(on_delete_unit), this);
g_signal_connect(btn_edit_unit, "clicked", G_CALLBACK(on_edit_unit), this); g_signal_connect(btn_edit_unit, "clicked", G_CALLBACK(on_edit_unit), this);
g_signal_connect(btn_move_up, "clicked", G_CALLBACK(on_move_up), this); g_signal_connect(btn_move_up_, "clicked", G_CALLBACK(on_move_up), this);
g_signal_connect(btn_move_down, "clicked", G_CALLBACK(on_move_down), this); g_signal_connect(btn_move_down_, "clicked", G_CALLBACK(on_move_down), this);
g_signal_connect(unit_listbox_, "row-activated", G_CALLBACK(on_unit_activated), this); g_signal_connect(unit_listbox_, "row-activated", G_CALLBACK(on_unit_activated), this);
g_signal_connect(unit_listbox_, "row-selected", G_CALLBACK(+[](GtkListBox*, GtkListBoxRow*, gpointer d) {
static_cast<UnitsView*>(d)->update_move_buttons();
}), this);
g_signal_connect(btn_refresh, "clicked", G_CALLBACK(+[](GtkButton*, gpointer d) { g_signal_connect(btn_refresh, "clicked", G_CALLBACK(+[](GtkButton*, gpointer d) {
auto* self = static_cast<UnitsView*>(d); auto* self = static_cast<UnitsView*>(d);
@@ -238,6 +241,25 @@ void UnitsView::populate_unit_list() {
gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), label); gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), label);
gtk_list_box_append(GTK_LIST_BOX(unit_listbox_), row); gtk_list_box_append(GTK_LIST_BOX(unit_listbox_), row);
} }
update_move_buttons();
}
void UnitsView::update_move_buttons() {
if (!selected_file_) {
gtk_widget_set_sensitive(btn_move_up_, FALSE);
gtk_widget_set_sensitive(btn_move_down_, FALSE);
return;
}
auto* row = gtk_list_box_get_selected_row(GTK_LIST_BOX(unit_listbox_));
if (!row) {
gtk_widget_set_sensitive(btn_move_up_, FALSE);
gtk_widget_set_sensitive(btn_move_down_, FALSE);
return;
}
int idx = gtk_list_box_row_get_index(row);
int count = (int)selected_file_->units.size();
gtk_widget_set_sensitive(btn_move_up_, idx > 0);
gtk_widget_set_sensitive(btn_move_down_, idx < count - 1);
} }
void UnitsView::refresh() { void UnitsView::refresh() {

View File

@@ -39,6 +39,8 @@ private:
GtkWidget* unit_listbox_; GtkWidget* unit_listbox_;
GtkWidget* file_label_; GtkWidget* file_label_;
GtkWidget* btn_save_; GtkWidget* btn_save_;
GtkWidget* btn_move_up_;
GtkWidget* btn_move_down_;
UnitFile* selected_file_ = nullptr; UnitFile* selected_file_ = nullptr;
bool file_dirty_ = false; bool file_dirty_ = false;
@@ -46,6 +48,7 @@ private:
void populate_file_list(); void populate_file_list();
void populate_unit_list(); void populate_unit_list();
void update_move_buttons();
GtkListBoxRow* find_file_row(UnitFile* uf); GtkListBoxRow* find_file_row(UnitFile* uf);
static void on_file_selected(GtkListBox* box, GtkListBoxRow* row, gpointer data); static void on_file_selected(GtkListBox* box, GtkListBoxRow* row, gpointer data);