ability to change hash map key from inspector

This commit is contained in:
Dmitry Stepanov
2026-07-22 20:33:16 +03:00
parent 0f4effd466
commit 9b0282e236
4 changed files with 140 additions and 35 deletions
+70 -2
View File
@@ -377,12 +377,80 @@ pub fn make_command(
PropertyAction::RemoveItemByKey { .. } => {
todo!()
}
PropertyAction::KeyChanged { .. } => {
todo!()
PropertyAction::KeyChanged { old_key, new_key } => {
Some(Command::new(ChangeHashMapKeyCommand {
old_key,
new_key,
path: property_changed.path(),
entity_getter,
}))
}
}
}
struct ChangeHashMapKeyCommand<F: EntityGetter> {
old_key: Box<dyn Reflect>,
new_key: Box<dyn Reflect>,
path: String,
entity_getter: F,
}
impl<F: EntityGetter> ChangeHashMapKeyCommand<F> {
fn swap(&mut self, ctx: &mut dyn CommandContext) {
let entity = some_or_return!((self.entity_getter)(ctx));
entity.resolve_path_mut(&self.path, &mut |result| match result {
Ok(entity) => match entity.as_hash_map_mut() {
Some(hash_map) => {
match hash_map.reflect_replace_key(
&*self.old_key,
self.new_key
.try_clone_box()
.expect("the key must be cloneable"),
) {
Ok(_) => {
std::mem::swap(&mut self.new_key, &mut self.old_key);
}
Err(new_key) => {
self.new_key = new_key;
err!("cannot insert a key because it is occupied!")
}
}
}
None => {
err!("not a hash map")
}
},
Err(reason) => {
err!(
"Failed to set property {}! Invalid path {:?}!",
self.path,
reason
);
}
});
}
}
impl<F: EntityGetter> Debug for ChangeHashMapKeyCommand<F> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "ChangeHashMapKeyCommand")
}
}
impl<F: EntityGetter> CommandTrait for ChangeHashMapKeyCommand<F> {
fn name(&mut self, _context: &dyn CommandContext) -> String {
format!("Change {} Hash Map Key", self.path)
}
fn execute(&mut self, context: &mut dyn CommandContext) {
self.swap(context)
}
fn revert(&mut self, context: &mut dyn CommandContext) {
self.swap(context)
}
}
fn try_modify_property<F>(entity: &mut dyn Reflect, path: &str, func: F)
where
F: FnOnce(&mut dyn Reflect),
@@ -39,13 +39,16 @@ pub enum HashMapPropertyEditorMessage {
key: ObjectValue,
message: UiMessage,
},
KeyChanged {
key: ObjectValue,
message: UiMessage,
},
}
impl MessageData for HashMapPropertyEditorMessage {}
#[derive(Debug, Reflect, Visit, Clone, PartialEq)]
#[reflect(type_uuid = "1440dacb-19ae-425b-a1f4-9d73a1009e6a")]
pub struct Entry<K: HashMapKey> {
pub key_hash: u64,
#[visit(skip)]
pub key: K,
pub key_editor: PropertyEditorInstance,
@@ -76,12 +79,18 @@ impl<K: HashMapKey> DerefMut for HashMapPropertyEditor<K> {
impl<K: HashMapKey> Control for HashMapPropertyEditor<K> {
fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
if let Some(_key_editor) = self
if let Some(key_editor_entry) = self
.entries
.iter()
.find(|e| message.destination() == e.key_editor.editor())
{
// TODO.
ui.post(
self.handle(),
HashMapPropertyEditorMessage::KeyChanged {
key: ObjectValue::new(key_editor_entry.key.clone()),
message: message.clone(),
},
)
} else if let Some(value_editor_entry) = self
.entries
.iter()
+56 -28
View File
@@ -40,7 +40,6 @@ use crate::{
},
message::UiMessage,
};
use std::hash::Hasher;
use std::{
any::TypeId,
collections::HashMap,
@@ -210,11 +209,7 @@ where
let entries = hash_map
.iter()
.filter_map(|(key, value)| {
let mut hasher = hash_map.hasher().build_hasher();
key.hash(&mut hasher);
let key_hash = hasher.finish();
Some(Entry {
key_hash,
key: key.clone(),
key_editor: create_key_editor(key, &mut ctx)?,
value_editor: create_value_editor(value, &mut ctx)?,
@@ -240,29 +235,62 @@ where
fn translate_message(&self, ctx: PropertyEditorTranslationContext) -> Option<PropertyChanged> {
if ctx.message.direction() == MessageDirection::FromWidget {
if let Some(HashMapPropertyEditorMessage::ValueChanged { key, message }) =
ctx.message.data()
{
if let Some(definition) = ctx
.definition_container
.definitions()
.get(&TypeId::of::<V>())
{
return Some(PropertyChanged {
name: ctx.name.to_string(),
action: FieldAction::HashMapAction(Box::new(HashMapAction::ValueChanged {
key: key.clone(),
action: definition
.property_editor
.translate_message(PropertyEditorTranslationContext {
environment: ctx.environment.clone(),
name: "",
message,
definition_container: ctx.definition_container.clone(),
})?
.action,
})),
});
if let Some(msg) = ctx.message.data::<HashMapPropertyEditorMessage>() {
match msg {
HashMapPropertyEditorMessage::ValueChanged { key, message } => {
if let Some(definition) = ctx
.definition_container
.definitions()
.get(&TypeId::of::<V>())
{
return Some(PropertyChanged {
name: ctx.name.to_string(),
action: FieldAction::HashMapAction(Box::new(
HashMapAction::ValueChanged {
key: key.clone(),
action: definition
.property_editor
.translate_message(PropertyEditorTranslationContext {
environment: ctx.environment.clone(),
name: "",
message,
definition_container: ctx
.definition_container
.clone(),
})?
.action,
},
)),
});
}
}
HashMapPropertyEditorMessage::KeyChanged { key, message } => {
if let Some(definition) = ctx
.definition_container
.definitions()
.get(&TypeId::of::<K>())
{
return Some(PropertyChanged {
name: ctx.name.to_string(),
action: FieldAction::HashMapAction(Box::new(
HashMapAction::KeyChanged {
key: key.clone(),
action: definition
.property_editor
.translate_message(PropertyEditorTranslationContext {
environment: ctx.environment.clone(),
name: "",
message,
definition_container: ctx
.definition_container
.clone(),
})?
.action,
},
)),
});
}
}
}
}
}
+2 -2
View File
@@ -236,7 +236,7 @@ impl PropertyAction {
.value
.try_clone_box()
.expect("the key must be cloneable!");
let key_action = PropertyAction::from_field_action(&action);
let key_action = PropertyAction::from_field_action(action);
key_action.apply_on_result(Ok(&mut *new_key), &mut |result| {
Log::verify(result);
});
@@ -491,9 +491,9 @@ impl PropertyChanged {
}
FieldAction::ObjectAction(_) | FieldAction::InheritableAction { .. } => {}
FieldAction::HashMapAction(ref action) => match **action {
HashMapAction::KeyChanged { .. } => {}
HashMapAction::Insert { ref key, .. }
| HashMapAction::Remove { ref key }
| HashMapAction::KeyChanged { ref key, .. }
| HashMapAction::ValueChanged { ref key, .. } => {
path += format!("[{}]", reflect::make_hash_map_key(&*key.value)).as_ref();
}