| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648 |
- /*
- * Copyright (c) 2012-2026 Daniele Bartolini et al.
- * SPDX-License-Identifier: GPL-3.0-or-later
- */
- namespace Crown
- {
- public class PropertyGrid : Gtk.Grid
- {
- GLib.ActionEntry[] actions =
- {
- { "remove", on_remove, null, null },
- { "reset_property", on_reset_property, "s", null },
- };
- public Expander? _expander;
- public Gtk.GestureMultiPress _controller_click;
- public GLib.SimpleActionGroup _action_group;
- public Database? _db;
- public StringId64 _type;
- public Guid _id;
- public Guid _component_id;
- public int _rows;
- public double _order;
- public bool _visible;
- public int label_width_chars;
- public Gee.HashMap<string, Gtk.GestureMultiPress> _gestures;
- public Gee.HashMap<string, InputField> _widgets;
- public Gee.HashMap<InputField, PropertyDefinition?> _definitions;
- public void on_remove(GLib.SimpleAction action, GLib.Variant? param)
- {
- string component_type = _db.type_name(_type);
- Guid unit_id = _id;
- Unit unit = Unit(_db, unit_id);
- Guid component_id;
- if (!unit.has_component(out component_id, component_type))
- return;
- Gee.ArrayList<unowned string> dependents = new Gee.ArrayList<unowned string>();
- // Do not remove if any other component needs us.
- foreach (var entry in Unit._component_registry.entries) {
- Guid dummy;
- if (!unit.has_component(out dummy, entry.key))
- continue;
- string[] component_type_dependencies = ((string)entry.value).split(", ");
- if (component_type in component_type_dependencies)
- dependents.add(entry.key);
- }
- if (dependents.size > 0) {
- StringBuilder sb = new StringBuilder();
- sb.append("Cannot remove %s due to the following dependencies:\n\n".printf(component_type));
- foreach (var item in dependents)
- sb.append("• %s\n".printf(item));
- Gtk.MessageDialog md = new Gtk.MessageDialog(null
- , Gtk.DialogFlags.MODAL
- , Gtk.MessageType.WARNING
- , Gtk.ButtonsType.OK
- , sb.str
- );
- md.set_default_response(Gtk.ResponseType.OK);
- md.response.connect(() => { md.destroy(); });
- md.show_all();
- return;
- } else {
- unit.remove_component_type(component_type);
- }
- }
- public void on_reset_property(GLib.SimpleAction action, GLib.Variant? param)
- {
- string property_name = param.get_string();
- InputField field = _widgets[property_name];
- PropertyDefinition? def = _definitions[field];
- field.set_union_value(def.deffault);
- }
- public void on_expander_button_released(int n_press, double x, double y)
- {
- if (_controller_click.get_current_button() == Gdk.BUTTON_SECONDARY) {
- GLib.Menu menu = new GLib.Menu();
- GLib.MenuItem mi;
- if (_db != null && _db.object_type(_id) == OBJECT_TYPE_UNIT && _component_id != GUID_ZERO) {
- mi = new GLib.MenuItem("Remove Component", null);
- mi.set_action_and_target_value("object.remove", null);
- menu.append_item(mi);
- }
- if (menu.get_n_items() > 0) {
- Gtk.Popover popover = new Gtk.Popover.from_model(null, menu);
- popover.set_relative_to(this);
- popover.set_pointing_to({ (int)x, (int)y, 1, 1 });
- popover.set_position(Gtk.PositionType.BOTTOM);
- popover.popup();
- }
- }
- }
- public PropertyGrid(Database? db = null)
- {
- this.row_spacing = 4;
- this.row_homogeneous = true;
- this.column_spacing = 12;
- this.label_width_chars = 13;
- // Data
- _expander = null;
- _db = db;
- _id = GUID_ZERO;
- _component_id = GUID_ZERO;
- _rows = 0;
- _order = 0.0;
- _visible = true;
- _gestures = new Gee.HashMap<string, Gtk.GestureMultiPress>();
- _widgets = new Gee.HashMap<string, InputField>();
- _definitions = new Gee.HashMap<InputField, PropertyDefinition?>();
- _action_group = new GLib.SimpleActionGroup();
- _action_group.add_action_entries(actions, this);
- this.insert_action_group("object", _action_group);
- }
- public PropertyGrid.from_object_type(StringId64 type, Database db)
- {
- this(db);
- _order = db.type_info(type).ui_order;
- _type = type;
- add_object_type(db.object_definition(type));
- }
- public PropertyGrid.from_object(Guid id, Database db)
- {
- this.from_object_type(StringId64(db.object_type(id)), db);
- _id = id;
- }
- public void set_expander(Expander e)
- {
- assert(_expander == null);
- _expander = e;
- _controller_click = new Gtk.GestureMultiPress(e);
- _controller_click.set_button(0);
- _controller_click.released.connect(on_expander_button_released);
- }
- public Gtk.Widget add_row(string label, Gtk.Widget w, string? tooltip = null)
- {
- Gtk.Label l = new Gtk.Label(label);
- l.width_chars = label_width_chars;
- l.xalign = 1.0f;
- l.yalign = 0.5f;
- l.set_tooltip_text(tooltip);
- w.hexpand = true;
- this.attach(l, 0, (int)_rows);
- this.attach(w, 1, (int)_rows);
- ++_rows;
- return l;
- }
- public void add_object_type(PropertyDefinition[] properties)
- {
- foreach (PropertyDefinition def in properties) {
- // Create input field.
- InputField? p = null;
- switch (def.type) {
- case PropertyType.BOOL:
- p = new InputBool();
- break;
- case PropertyType.DOUBLE:
- if (def.editor == PropertyEditorType.ANGLE)
- p = new InputAngle((double)def.deffault, (double)def.min, (double)def.max);
- else
- p = new InputDouble((double)def.deffault, (double)def.min, (double)def.max);
- break;
- case PropertyType.STRING:
- if (def.editor == PropertyEditorType.ENUM)
- p = new InputEnum((string)def.deffault, def.enum_labels, def.enum_values);
- else
- p = new InputString();
- break;
- case PropertyType.VECTOR3:
- if (def.editor == PropertyEditorType.COLOR)
- p = new InputColor3();
- else
- p = new InputVector3((Vector3)def.deffault, (Vector3)def.min, (Vector3)def.max);
- break;
- case PropertyType.QUATERNION:
- p = new InputQuaternion();
- break;
- case PropertyType.RESOURCE:
- p = new InputResource(def.resource_type, _db);
- break;
- case PropertyType.REFERENCE:
- p = new InputObject(def.object_type, _db);
- break;
- case PropertyType.OBJECTS_SET:
- continue;
- default:
- assert(false);
- break;
- }
- p.value_changed.connect(on_property_value_changed);
- Gtk.GestureMultiPress click = new Gtk.GestureMultiPress(p);
- click.set_propagation_phase(Gtk.PropagationPhase.CAPTURE);
- click.set_button(Gdk.BUTTON_SECONDARY);
- click.pressed.connect((n_press, x, y) => {
- if (click.get_current_button() != Gdk.BUTTON_SECONDARY)
- return;
- GLib.Menu menu = new GLib.Menu();
- GLib.MenuItem mi;
- if (_db != null) {
- mi = new GLib.MenuItem("Reset to default", null);
- mi.set_action_and_target_value("object.reset_property", new GLib.Variant.string(def.name));
- menu.append_item(mi);
- }
- if (menu.get_n_items() > 0) {
- Gtk.Popover popover = new Gtk.Popover.from_model(null, menu);
- popover.set_relative_to(p);
- popover.set_position(Gtk.PositionType.BOTTOM);
- popover.popup();
- }
- });
- _gestures[def.name] = click;
- _widgets[def.name] = p;
- _definitions[p] = def;
- if (!def.hidden)
- add_row(def.label, p, def.tooltip);
- }
- }
- // Returns true if the property was written.
- // The property is written to database only if its value
- // differs than the value stored in the database.
- public bool write_property_if_changed(PropertyDefinition def, GLib.Value? new_value)
- {
- bool changed = false;
- if (def.type == PropertyType.BOOL) {
- if (_db.object_type(_id) == OBJECT_TYPE_UNIT) {
- Unit u = Unit(_db, _id);
- if (u.get_component_bool(_component_id, def.name, (bool)def.deffault) != new_value) {
- u.set_component_bool(_component_id, def.name, (bool)new_value);
- changed = true;
- }
- } else {
- if (_db.get_bool(_id, def.name, (bool)def.deffault) != new_value) {
- _db.set_bool(_id, def.name, (bool)new_value);
- changed = true;
- }
- }
- } else if (def.type == PropertyType.DOUBLE) {
- if (_db.object_type(_id) == OBJECT_TYPE_UNIT) {
- Unit u = Unit(_db, _id);
- if (u.get_component_double(_component_id, def.name, (double)def.deffault) != new_value) {
- u.set_component_double(_component_id, def.name, (double)new_value);
- changed = true;
- }
- } else {
- if (_db.get_double(_id, def.name, (double)def.deffault) != new_value) {
- _db.set_double(_id, def.name, (double)new_value);
- changed = true;
- }
- }
- } else if (def.type == PropertyType.STRING) {
- if (_db.object_type(_id) == OBJECT_TYPE_UNIT) {
- Unit u = Unit(_db, _id);
- if (u.get_component_string(_component_id, def.name, (string)def.deffault) != (string)new_value) {
- u.set_component_string(_component_id, def.name, (string)new_value);
- changed = true;
- }
- } else {
- if (_db.get_string(_id, def.name, (string)def.deffault) != (string)new_value) {
- _db.set_string(_id, def.name, (string)new_value);
- changed = true;
- }
- }
- } else if (def.type == PropertyType.VECTOR3) {
- if (_db.object_type(_id) == OBJECT_TYPE_UNIT) {
- Unit u = Unit(_db, _id);
- if (Vector3.equal_func(u.get_component_vector3(_component_id, def.name, (Vector3)def.deffault), (Vector3)new_value) == false) {
- u.set_component_vector3(_component_id, def.name, (Vector3)new_value);
- changed = true;
- }
- } else {
- if (Vector3.equal_func(_db.get_vector3(_id, def.name, (Vector3)def.deffault), (Vector3)new_value) == false) {
- _db.set_vector3(_id, def.name, (Vector3)new_value);
- changed = true;
- }
- }
- } else if (def.type == PropertyType.QUATERNION) {
- if (_db.object_type(_id) == OBJECT_TYPE_UNIT) {
- Unit u = Unit(_db, _id);
- if (Quaternion.equal_func(u.get_component_quaternion(_component_id, def.name, (Quaternion)def.deffault), (Quaternion)new_value) == false) {
- u.set_component_quaternion(_component_id, def.name, (Quaternion)new_value);
- changed = true;
- }
- } else {
- if (Quaternion.equal_func(_db.get_quaternion(_id, def.name, (Quaternion)def.deffault), (Quaternion)new_value) == false) {
- _db.set_quaternion(_id, def.name, (Quaternion)new_value);
- changed = true;
- }
- }
- } else if (def.type == PropertyType.RESOURCE) {
- if (_db.object_type(_id) == OBJECT_TYPE_UNIT) {
- Unit u = Unit(_db, _id);
- if (u.get_component_resource(_component_id, def.name, (string?)def.deffault) != (string?)new_value) {
- u.set_component_resource(_component_id, def.name, (string?)new_value);
- changed = true;
- }
- } else {
- if (_db.get_resource(_id, def.name, (string?)def.deffault) != (string?)new_value) {
- _db.set_resource(_id, def.name, (string?)new_value);
- changed = true;
- }
- }
- } else if (def.type == PropertyType.REFERENCE) {
- if (_db.object_type(_id) == OBJECT_TYPE_UNIT) {
- Unit u = Unit(_db, _id);
- if (Guid.equal_func(u.get_component_reference(_component_id, def.name, (Guid)def.deffault), (Guid)new_value) == false) {
- u.set_component_reference(_component_id, def.name, (Guid)new_value);
- changed = true;
- }
- } else {
- if (Guid.equal_func(_db.get_reference(_id, def.name, (Guid)def.deffault), (Guid)new_value) == false) {
- _db.set_reference(_id, def.name, (Guid)new_value);
- changed = true;
- }
- }
- } else {
- loge("Unknown property type");
- }
- return changed;
- }
- public void on_property_value_changed(InputField p, int undo_redo)
- {
- if (p.is_inconsistent())
- return;
- if (_id == GUID_ZERO)
- return;
- PropertyDefinition def = _definitions[p];
- Gee.ArrayList<PropertyDefinition?> dynamic_properties = new Gee.ArrayList<PropertyDefinition?>();
- Gee.ArrayList<GLib.Value?> dynamic_values = new Gee.ArrayList<GLib.Value?>();
- bool changed = false;
- save_dynamic_properties_values(ref dynamic_properties, ref dynamic_values);
- read_dynamic_properties_ranges_except({ def });
- UndoRedo? ur = null;
- if (undo_redo == 0)
- ur = _db.disable_undo();
- changed = restore_dynamic_properties_values_except(dynamic_properties, dynamic_values, { def }) || changed;
- changed = write_property_if_changed(def, p.union_value()) || changed;
- if (changed)
- _db.add_restore_point(ActionType.CHANGE_OBJECTS, new Guid?[] { _id });
- if (undo_redo == 0)
- _db.restore_undo(ur);
- }
- public void read_all_properties()
- {
- foreach (var e in _definitions) {
- InputField p = e.key;
- PropertyDefinition def = e.value;
- p.value_changed.disconnect(on_property_value_changed);
- if (def.type == PropertyType.BOOL) {
- if (_db.object_type(_id) == OBJECT_TYPE_UNIT) {
- Unit u = Unit(_db, _id);
- p.set_union_value(u.get_component_bool(_component_id, def.name, (bool)def.deffault));
- } else {
- p.set_union_value(_db.get_bool(_id, def.name, (bool)def.deffault));
- }
- } else if (def.type == PropertyType.DOUBLE) {
- if (_db.object_type(_id) == OBJECT_TYPE_UNIT) {
- Unit u = Unit(_db, _id);
- p.set_union_value(u.get_component_double(_component_id, def.name, (double)def.deffault));
- } else {
- p.set_union_value(_db.get_double(_id, def.name, (double)def.deffault));
- }
- } else if (def.type == PropertyType.STRING) {
- if (_db.object_type(_id) == OBJECT_TYPE_UNIT) {
- Unit u = Unit(_db, _id);
- p.set_union_value(u.get_component_string(_component_id, def.name, (string)def.deffault));
- } else {
- p.set_union_value(_db.get_string(_id, def.name, (string)def.deffault));
- }
- } else if (def.type == PropertyType.VECTOR3) {
- if (_db.object_type(_id) == OBJECT_TYPE_UNIT) {
- Unit u = Unit(_db, _id);
- p.set_union_value(u.get_component_vector3(_component_id, def.name, (Vector3)def.deffault));
- } else {
- p.set_union_value(_db.get_vector3(_id, def.name, (Vector3)def.deffault));
- }
- } else if (def.type == PropertyType.QUATERNION) {
- if (_db.object_type(_id) == OBJECT_TYPE_UNIT) {
- Unit u = Unit(_db, _id);
- p.set_union_value(u.get_component_quaternion(_component_id, def.name, (Quaternion)def.deffault));
- } else {
- p.set_union_value(_db.get_quaternion(_id, def.name, (Quaternion)def.deffault));
- }
- } else if (def.type == PropertyType.RESOURCE) {
- if (_db.object_type(_id) == OBJECT_TYPE_UNIT) {
- Unit u = Unit(_db, _id);
- p.set_union_value(u.get_component_resource(_component_id, def.name, (string?)def.deffault));
- } else {
- p.set_union_value(_db.get_resource(_id, def.name, (string?)def.deffault));
- }
- } else if (def.type == PropertyType.REFERENCE) {
- if (_db.object_type(_id) == OBJECT_TYPE_UNIT) {
- Unit u = Unit(_db, _id);
- p.set_union_value(u.get_component_reference(_component_id, def.name, (Guid)def.deffault));
- } else {
- p.set_union_value(_db.get_reference(_id, def.name, (Guid)def.deffault));
- }
- } else {
- loge("Unknown property value type");
- }
- p.value_changed.connect(on_property_value_changed);
- }
- }
- public virtual void read_properties()
- {
- read_all_properties();
- read_dynamic_properties_ranges();
- read_all_properties();
- }
- public void read_dynamic_properties_ranges_except(PropertyDefinition[] excluded)
- {
- foreach (var e in _definitions) {
- PropertyDefinition def = e.value;
- int i;
- // Skip if excluded.
- for (i = 0; i < excluded.length; ++i) {
- if (excluded[i].name == def.name)
- break;
- }
- if (i != excluded.length)
- continue;
- // Read range.
- if (def.enum_callback != null) {
- InputField p = _widgets[def.name];
- InputField parent_p = _widgets[def.enum_property];
- p.value_changed.disconnect(on_property_value_changed);
- def.enum_callback(parent_p, (InputEnum)p, _db._project);
- p.value_changed.connect(on_property_value_changed);
- } else if (def.resource_callback != null) {
- InputField p = _widgets[def.name];
- InputField parent_p = _widgets[def.enum_property];
- p.value_changed.disconnect(on_property_value_changed);
- def.resource_callback(parent_p, (InputResource)p, _db._project);
- p.value_changed.connect(on_property_value_changed);
- }
- }
- }
- public void read_dynamic_properties_ranges()
- {
- read_dynamic_properties_ranges_except({});
- }
- public void save_dynamic_properties_values(ref Gee.ArrayList<PropertyDefinition?> properties, ref Gee.ArrayList<GLib.Value?> values)
- {
- foreach (var e in _definitions) {
- PropertyDefinition def = e.value;
- if (def.enum_callback != null) {
- InputField p = _widgets[def.name];
- properties.add(def);
- values.add(p.union_value());
- } else if (def.resource_callback != null) {
- InputField p = _widgets[def.name];
- properties.add(def);
- values.add(p.union_value());
- }
- }
- }
- public bool restore_dynamic_properties_values_except(Gee.ArrayList<PropertyDefinition?> properties, Gee.ArrayList<GLib.Value?> values, PropertyDefinition[] excluded)
- {
- bool changed = false;
- for (int i = 0; i < properties.size; ++i) {
- PropertyDefinition def = properties[i];
- GLib.Value val = values[i];
- InputField p = _widgets[def.name];
- int j;
- // Skip if excluded.
- for (j = 0; j < excluded.length; ++j) {
- if (excluded[j].name == def.name)
- break;
- }
- if (j != excluded.length)
- continue;
- // Restore value.
- p.value_changed.disconnect(on_property_value_changed);
- if (def.enum_callback != null) {
- p.set_union_value(val);
- if (p.is_inconsistent() || !p.is_inconsistent() && (string)p.union_value() != (string)val)
- p.set_union_value(((InputEnum)p).any_valid_id());
- } else if (def.resource_callback != null) {
- p.set_union_value(val);
- }
- p.value_changed.connect(on_property_value_changed);
- changed = write_property_if_changed(def, p.union_value()) || changed;
- }
- return changed;
- }
- public bool restore_dynamic_properties_values(Gee.ArrayList<PropertyDefinition?> properties, Gee.ArrayList<GLib.Value?> values)
- {
- return restore_dynamic_properties_values_except(properties, values, {});
- }
- }
- public class PropertyGridSet : Gtk.Box
- {
- public Gtk.ListBox _list_box;
- public PropertyGridSet()
- {
- Object(orientation: Gtk.Orientation.VERTICAL, spacing: 0);
- _list_box = new Gtk.ListBox();
- _list_box.selection_mode = Gtk.SelectionMode.NONE;
- _list_box.margin_bottom
- = this.margin_end
- = this.margin_start
- = this.margin_top
- = 12
- ;
- _list_box.set_sort_func(sort_function);
- _list_box.set_filter_func(filter_function);
- this.pack_start(_list_box);
- }
- public static int sort_function(Gtk.ListBoxRow row1, Gtk.ListBoxRow row2)
- {
- Expander e1 = (Expander)row1.get_child();
- Expander e2 = (Expander)row2.get_child();
- double order = ((PropertyGrid)e1._child)._order - ((PropertyGrid)e2._child)._order;
- return (int)order;
- }
- public static bool filter_function(Gtk.ListBoxRow row)
- {
- Expander e = (Expander)row.get_child();
- return ((PropertyGrid)e._child)._visible;
- }
- public Expander add_property_grid(PropertyGrid cv, string label, string? tooltip = null)
- {
- Gtk.Label l = new Gtk.Label(null);
- l.set_markup("<b>%s</b>".printf(label));
- l.xalign = 0.0f;
- l.yalign = 0.5f;
- l.set_tooltip_text(label);
- Expander e = new Expander();
- e.custom_header = l;
- e.expanded = true;
- e.add(cv);
- cv.set_expander(e);
- Gtk.ListBoxRow row = new Gtk.ListBoxRow();
- row.can_focus = false;
- row.add(e);
- _list_box.add(row);
- return e;
- }
- public Expander add_property_grid_optional(PropertyGrid cv, string label, InputBool InputBool, string? tooltip = null)
- {
- Gtk.Label l = new Gtk.Label(null);
- l.set_markup("<b>%s</b>".printf(label));
- l.xalign = 0.0f;
- l.yalign = 0.5f;
- l.set_tooltip_text(tooltip);
- Gtk.Box b = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 6);
- b.pack_start(InputBool, false, false);
- b.pack_start(l, false, false);
- Expander e = new Expander();
- e.custom_header = b;
- e.expanded = true;
- e.add(cv);
- cv.set_expander(e);
- Gtk.ListBoxRow row = new Gtk.ListBoxRow();
- row.can_focus = false;
- row.add(e);
- _list_box.add(row);
- return e;
- }
- }
- } /* namespace Crown */
|