/* * Copyright (c) 2012-2026 Daniele Bartolini et al. * SPDX-License-Identifier: GPL-3.0-or-later */ namespace Crown { public class ProjectRow : Gtk.ListBoxRow { public Gtk.Box _vbox; public Gtk.Box _hbox; public Gtk.Label _name; public Gtk.Label _source_dir; public Gtk.Button _remove_button; public Gtk.Button _open_button; public ProjectsList _projects_list; public ProjectRow(string source_dir, string time, string name, ProjectsList pl) { this.set_data("source_dir", source_dir); this.set_data("mtime", time); _projects_list = pl; _vbox = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); _name = new Gtk.Label(null); _name.set_margin_start(12); _name.set_margin_end(12); _name.set_margin_top(8); _name.set_margin_bottom(8); _name.set_markup("%s".printf(name)); _name.set_xalign(0.0f); _vbox.pack_start(_name); _source_dir = new Gtk.Label(null); _source_dir.set_margin_start(12); _source_dir.set_margin_end(12); _source_dir.set_margin_bottom(8); _source_dir.set_markup("%s".printf(source_dir)); _source_dir.set_xalign(0.0f); _vbox.pack_start(_source_dir); _hbox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 6); _hbox.pack_start(_vbox); _remove_button = new Gtk.Button.from_icon_name("list-remove-symbolic"); _remove_button.set_tooltip_text("Remove the project from the list."); _remove_button.get_style_context().add_class("flat"); _remove_button.get_style_context().add_class("destructive-action"); _remove_button.set_halign(Gtk.Align.CENTER); _remove_button.set_valign(Gtk.Align.CENTER); _remove_button.set_margin_end(12); _hbox.pack_end(_remove_button, false, false, 0); _open_button = new Gtk.Button.with_label("Open"); _open_button.set_tooltip_text("Open the project."); _open_button.get_style_context().add_class("flat"); _open_button.set_halign(Gtk.Align.CENTER); _open_button.set_valign(Gtk.Align.CENTER); // _open_button.set_margin_end(12); _hbox.pack_end(_open_button, false, false, 0); _remove_button.clicked.connect(on_remove_button_clicked); _open_button.clicked.connect(on_open_button_clicked); this.add(_hbox); } public void on_remove_button_clicked() { GLib.Application.get_default().activate_action("remove-project" , new GLib.Variant.string(this.get_data("source_dir")) ); } public void on_open_button_clicked() { GLib.Application.get_default().activate_action("open-project" , new GLib.Variant.tuple({this.get_data("source_dir"), LEVEL_NONE, ProjectFlags.NONE}) ); } } public class ProjectsList : Gtk.Box { // Data public User _user; // Widgets public Gtk.Label _projects_list_label; public Gtk.Label _local_label; public Gtk.Box _project_list_empty; public Gtk.ListBox _list_projects; public Gtk.Button _button_import_project; public Gtk.Button _button_new_project; public Gtk.Box _buttons_box; public Gtk.Box _projects_box; public Clamp _clamp; public ProjectsList(User user) { Object(orientation: Gtk.Orientation.VERTICAL); // Data _user = user; _projects_list_label = new Gtk.Label(null); _projects_list_label.xalign = 0; _projects_list_label.set_markup("Projects"); _local_label = new Gtk.Label("Local"); _local_label.set_markup("Local"); _project_list_empty = new Gtk.Box(Gtk.Orientation.VERTICAL, 6); _project_list_empty.margin_top = 12; _project_list_empty.margin_bottom = 12; var label = new Gtk.Label(null); label.set_markup("No projects found"); _project_list_empty.pack_start(label, false, false); label = new Gtk.Label(null); label.set_markup("Use the buttons above to create a new project or import an already existing one."); _project_list_empty.pack_start(label, false, false); _project_list_empty.show_all(); _list_projects = new Gtk.ListBox(); _list_projects.set_placeholder(_project_list_empty); _list_projects.set_sort_func((row1, row2) => { int64 mtime1 = int64.parse(row1.get_data("mtime")); int64 mtime2 = int64.parse(row2.get_data("mtime")); return mtime1 > mtime2 ? -1 : 1; // LRU }); _button_import_project = new Gtk.Button.with_label("Import..."); _button_import_project.set_tooltip_text("Import an existing project."); _button_import_project.action_name = "app.add-project"; _button_new_project = new Gtk.Button.with_label("Create New"); _button_new_project.set_tooltip_text("Create a new project."); _button_new_project.get_style_context().add_class("suggested-action"); _button_new_project.action_name = "app.new-project"; _buttons_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); _buttons_box.spacing = 6; _buttons_box.pack_start(_local_label, false, true); _buttons_box.pack_end(_button_new_project, false, true); _buttons_box.pack_end(_button_import_project, false, true); _projects_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); _projects_box.margin_start = 12; _projects_box.margin_end = 12; _projects_box.margin_top = 32; _projects_box.margin_bottom = 32; _projects_box.spacing = 12; _projects_box.pack_start(_projects_list_label, false, true); _projects_box.pack_start(_buttons_box, false, true); _projects_box.pack_start(_list_projects, false, true); _clamp = new Clamp(); _clamp.set_child(_projects_box); this.add(_clamp); _user.recent_project_added.connect(on_recent_project_added); _user.recent_project_touched.connect(on_recent_project_touched); _user.recent_project_removed.connect(on_recent_project_removed); } public void on_recent_project_added(string source_dir, string name, string time) { // Add project row. var row = new ProjectRow(source_dir, time, name, this); _list_projects.add(row); _list_projects.show_all(); // Otherwise the list is not always updated... if (!GLib.FileUtils.test(source_dir, FileTest.EXISTS)) row._open_button.sensitive = false; invalidate_sort(); } public void on_recent_project_touched(string source_dir, string mtime) { _list_projects.foreach((row) => { if (row.get_data("source_dir") == source_dir) { row.set_data("mtime", mtime); return; } }); invalidate_sort(); } public void on_recent_project_removed(string source_dir) { _list_projects.foreach((row) => { if (row.get_data("source_dir") == source_dir) { _list_projects.remove(row); return; } }); } public void invalidate_sort() { _list_projects.invalidate_sort(); // Give focus to most recent project's open button. ProjectRow? first_row = (ProjectRow?)_list_projects.get_row_at_index(0); if (first_row != null) first_row._open_button.has_focus = true; } } } /* namespace Crown */