feat(dashboard): ✨ Implement Dictionary control
This commit is contained in:
parent
a06232a4a6
commit
ce7328568f
|
@ -107,7 +107,6 @@ impl Control {
|
|||
super::grid_flow_inline(ui, allow_inline);
|
||||
|
||||
let session_variants_mut = session_fragment.as_object_mut().unwrap();
|
||||
|
||||
let json::Value::String(variant_mut) = &mut session_variants_mut["variant"] else {
|
||||
unreachable!()
|
||||
};
|
||||
|
|
|
@ -1,18 +1,197 @@
|
|||
// use crate::dashboard::DashboardResponse;
|
||||
// use egui::Ui;
|
||||
// use serde_json as json;
|
||||
use super::{reset, NestingInfo, SettingControl, INDENTATION_STEP};
|
||||
use crate::dashboard::components::{
|
||||
collapsible,
|
||||
up_down::{self, UpDownResult},
|
||||
};
|
||||
use alvr_packets::PathValuePair;
|
||||
use alvr_session::settings_schema::SchemaNode;
|
||||
use eframe::{
|
||||
egui::{Layout, TextEdit, Ui},
|
||||
emath::Align,
|
||||
};
|
||||
use serde_json as json;
|
||||
|
||||
// use super::{SettingContainer, SettingsContext, SettingsResponse};
|
||||
struct Entry {
|
||||
editing_key: Option<String>,
|
||||
control: SettingControl,
|
||||
}
|
||||
|
||||
// pub struct Dictionary {}
|
||||
pub struct Control {
|
||||
nesting_info: NestingInfo,
|
||||
default_key: String,
|
||||
default_value: SchemaNode,
|
||||
default: Vec<json::Value>,
|
||||
controls: Vec<Entry>,
|
||||
}
|
||||
|
||||
// impl SettingContainer for Dictionary {
|
||||
// fn ui(
|
||||
// &mut self,
|
||||
// ui: &mut Ui,
|
||||
// session_fragment: json::Value,
|
||||
// context: &SettingsContext,
|
||||
// ) -> Option<SettingsResponse> {
|
||||
// None
|
||||
// }
|
||||
// }
|
||||
impl Control {
|
||||
pub fn new(
|
||||
nesting_info: NestingInfo,
|
||||
default_key: String,
|
||||
default_value: SchemaNode,
|
||||
default: Vec<(String, json::Value)>,
|
||||
) -> Self {
|
||||
Self {
|
||||
nesting_info,
|
||||
default_key,
|
||||
default_value,
|
||||
default: default
|
||||
.into_iter()
|
||||
.map(|pair| json::to_value(pair).unwrap())
|
||||
.collect(),
|
||||
controls: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ui(
|
||||
&mut self,
|
||||
ui: &mut Ui,
|
||||
session_fragment: &mut json::Value,
|
||||
allow_inline: bool,
|
||||
) -> Option<PathValuePair> {
|
||||
super::grid_flow_inline(ui, allow_inline);
|
||||
|
||||
fn get_content_request(
|
||||
nesting_info: &NestingInfo,
|
||||
entries: Vec<json::Value>,
|
||||
) -> Option<PathValuePair> {
|
||||
super::get_single_value(
|
||||
nesting_info,
|
||||
"content".into(),
|
||||
json::to_value(entries).unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
let mut request = None;
|
||||
let collapsed = ui
|
||||
.with_layout(Layout::left_to_right(Align::Center), |ui| {
|
||||
let collapsed = collapsible::collapsible_button(
|
||||
ui,
|
||||
&self.nesting_info,
|
||||
session_fragment,
|
||||
&mut request,
|
||||
);
|
||||
|
||||
if reset::reset_button(ui, true, "default list").clicked() {
|
||||
request = get_content_request(&self.nesting_info, self.default.clone())
|
||||
}
|
||||
|
||||
collapsed
|
||||
})
|
||||
.inner;
|
||||
|
||||
let session_content = session_fragment["content"].as_array_mut().unwrap();
|
||||
|
||||
while session_content.len() > self.controls.len() {
|
||||
let mut nesting_info = self.nesting_info.clone();
|
||||
nesting_info.path.extend_from_slice(&[
|
||||
"content".into(),
|
||||
self.controls.len().into(),
|
||||
1.into(),
|
||||
]);
|
||||
|
||||
self.controls.push(Entry {
|
||||
editing_key: None,
|
||||
control: SettingControl::new(nesting_info, self.default_value.clone()),
|
||||
});
|
||||
}
|
||||
while session_content.len() < self.controls.len() {
|
||||
self.controls.pop();
|
||||
}
|
||||
|
||||
if !collapsed {
|
||||
ui.end_row();
|
||||
|
||||
let mut idx = 0;
|
||||
while idx < self.controls.len() {
|
||||
let delete_entry = ui
|
||||
.horizontal(|ui| {
|
||||
ui.add_space(INDENTATION_STEP * self.nesting_info.indentation_level as f32);
|
||||
|
||||
let delete_entry = ui.button("❌").clicked();
|
||||
|
||||
let up_down_result = up_down::up_down_buttons(ui, idx, self.controls.len());
|
||||
|
||||
if up_down_result != UpDownResult::None {
|
||||
if up_down_result == UpDownResult::Up {
|
||||
session_content.swap(idx, idx - 1);
|
||||
} else {
|
||||
session_content.swap(idx, idx + 1);
|
||||
}
|
||||
|
||||
request =
|
||||
get_content_request(&self.nesting_info, session_content.clone());
|
||||
}
|
||||
|
||||
let json::Value::String(text_mut) = &mut session_content[idx][0] else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let editing_key_mut = &mut self.controls[idx].editing_key;
|
||||
|
||||
let textbox = if let Some(editing_key_mut) = editing_key_mut {
|
||||
TextEdit::singleline(editing_key_mut)
|
||||
} else {
|
||||
TextEdit::singleline(text_mut)
|
||||
};
|
||||
|
||||
let response = ui.add(textbox.desired_width(f32::INFINITY));
|
||||
if response.lost_focus() {
|
||||
if let Some(editing_key_mut) = editing_key_mut {
|
||||
let mut nesting_info = self.nesting_info.clone();
|
||||
nesting_info
|
||||
.path
|
||||
.extend_from_slice(&["content".into(), idx.into()]);
|
||||
|
||||
request = super::get_single_value(
|
||||
&nesting_info,
|
||||
0.into(),
|
||||
json::Value::String(editing_key_mut.clone()),
|
||||
);
|
||||
|
||||
*text_mut = editing_key_mut.clone();
|
||||
}
|
||||
|
||||
*editing_key_mut = None;
|
||||
}
|
||||
if response.gained_focus() {
|
||||
*editing_key_mut = Some(text_mut.clone());
|
||||
};
|
||||
|
||||
delete_entry
|
||||
})
|
||||
.inner;
|
||||
|
||||
if delete_entry {
|
||||
session_content.remove(idx);
|
||||
self.controls.remove(idx);
|
||||
|
||||
request = get_content_request(&self.nesting_info, session_content.clone());
|
||||
} else {
|
||||
request = self.controls[idx]
|
||||
.control
|
||||
.ui(ui, &mut session_content[idx][1], true)
|
||||
.or(request);
|
||||
}
|
||||
|
||||
ui.end_row();
|
||||
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
ui.label(" ");
|
||||
if ui.button("Add entry").clicked() {
|
||||
let mut session_content =
|
||||
session_fragment["content"].as_array_mut().unwrap().clone();
|
||||
session_content.push(json::Value::Array(vec![
|
||||
json::Value::String(self.default_key.clone()),
|
||||
session_fragment["value"].clone(),
|
||||
]));
|
||||
|
||||
request = get_content_request(&self.nesting_info, session_content);
|
||||
}
|
||||
}
|
||||
|
||||
request
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ pub mod reset;
|
|||
pub mod section;
|
||||
pub mod switch;
|
||||
pub mod text;
|
||||
pub mod up_down;
|
||||
pub mod vector;
|
||||
|
||||
use alvr_packets::{PathSegment, PathValuePair};
|
||||
|
@ -67,6 +68,7 @@ pub enum SettingControl {
|
|||
Numeric(number::Control),
|
||||
Array(array::Control),
|
||||
Vector(vector::Control),
|
||||
Dictionary(dictionary::Control),
|
||||
None,
|
||||
}
|
||||
|
||||
|
@ -119,7 +121,16 @@ impl SettingControl {
|
|||
*default_element,
|
||||
default,
|
||||
)),
|
||||
// SchemaNode::Dictionary { default_key, default_value, default } => todo!(),
|
||||
SchemaNode::Dictionary {
|
||||
default_key,
|
||||
default_value,
|
||||
default,
|
||||
} => Self::Dictionary(dictionary::Control::new(
|
||||
nesting_info,
|
||||
default_key,
|
||||
*default_value,
|
||||
default,
|
||||
)),
|
||||
_ => Self::None,
|
||||
}
|
||||
}
|
||||
|
@ -141,6 +152,7 @@ impl SettingControl {
|
|||
Self::Numeric(control) => control.ui(ui, session_fragment, allow_inline),
|
||||
Self::Array(control) => control.ui(ui, session_fragment, allow_inline),
|
||||
Self::Vector(control) => control.ui(ui, session_fragment, allow_inline),
|
||||
Self::Dictionary(control) => control.ui(ui, session_fragment, allow_inline),
|
||||
Self::None => {
|
||||
grid_flow_inline(ui, allow_inline);
|
||||
ui.add_enabled_ui(false, |ui| ui.label("Unimplemented UI"));
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
use eframe::{
|
||||
egui::{self, Layout, Ui},
|
||||
emath::Align,
|
||||
};
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub enum UpDownResult {
|
||||
Up,
|
||||
Down,
|
||||
None,
|
||||
}
|
||||
|
||||
pub fn up_down_buttons(ui: &mut Ui, index: usize, count: usize) -> UpDownResult {
|
||||
ui.with_layout(Layout::top_down(Align::LEFT), |ui| {
|
||||
ui.spacing_mut().item_spacing = egui::vec2(0.0, 0.0);
|
||||
|
||||
let up_clicked = ui
|
||||
.add_visible_ui(index > 0, |ui| ui.small_button("⬆"))
|
||||
.inner
|
||||
.clicked();
|
||||
let down_clicked = ui
|
||||
.add_visible_ui(index < count - 1, |ui| ui.small_button("⬇"))
|
||||
.inner
|
||||
.clicked();
|
||||
|
||||
if up_clicked {
|
||||
UpDownResult::Up
|
||||
} else if down_clicked {
|
||||
UpDownResult::Down
|
||||
} else {
|
||||
UpDownResult::None
|
||||
}
|
||||
})
|
||||
.inner
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
use crate::dashboard::components::collapsible;
|
||||
|
||||
use super::{reset, NestingInfo, SettingControl, INDENTATION_STEP};
|
||||
use crate::dashboard::components::{
|
||||
collapsible,
|
||||
up_down::{self, UpDownResult},
|
||||
};
|
||||
use alvr_packets::PathValuePair;
|
||||
use alvr_session::settings_schema::SchemaNode;
|
||||
use eframe::{
|
||||
egui::{self, Layout, Ui},
|
||||
egui::{Layout, Ui},
|
||||
emath::Align,
|
||||
};
|
||||
use serde_json as json;
|
||||
|
@ -38,6 +40,13 @@ impl Control {
|
|||
) -> Option<PathValuePair> {
|
||||
super::grid_flow_inline(ui, allow_inline);
|
||||
|
||||
fn get_content_request(
|
||||
nesting_info: &NestingInfo,
|
||||
elements: Vec<json::Value>,
|
||||
) -> Option<PathValuePair> {
|
||||
super::get_single_value(nesting_info, "content".into(), json::Value::Array(elements))
|
||||
}
|
||||
|
||||
let mut request = None;
|
||||
let collapsed = ui
|
||||
.with_layout(Layout::left_to_right(Align::Center), |ui| {
|
||||
|
@ -56,29 +65,20 @@ impl Control {
|
|||
})
|
||||
.inner;
|
||||
|
||||
{
|
||||
let session_array_mut = session_fragment["content"].as_array().unwrap();
|
||||
let session_content = session_fragment["content"].as_array_mut().unwrap();
|
||||
|
||||
while session_array_mut.len() > self.controls.len() {
|
||||
let mut nesting_info = self.nesting_info.clone();
|
||||
nesting_info.path.push("content".into());
|
||||
nesting_info.path.push(self.controls.len().into());
|
||||
while session_content.len() > self.controls.len() {
|
||||
let mut nesting_info = self.nesting_info.clone();
|
||||
nesting_info.path.push("content".into());
|
||||
nesting_info.path.push(self.controls.len().into());
|
||||
|
||||
self.controls.push(SettingControl::new(
|
||||
nesting_info,
|
||||
self.default_element.clone(),
|
||||
))
|
||||
}
|
||||
while session_array_mut.len() < self.controls.len() {
|
||||
self.controls.pop();
|
||||
}
|
||||
self.controls.push(SettingControl::new(
|
||||
nesting_info,
|
||||
self.default_element.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
fn get_content_request(
|
||||
nesting_info: &NestingInfo,
|
||||
elements: Vec<json::Value>,
|
||||
) -> Option<PathValuePair> {
|
||||
super::get_single_value(nesting_info, "content".into(), json::Value::Array(elements))
|
||||
while session_content.len() < self.controls.len() {
|
||||
self.controls.pop();
|
||||
}
|
||||
|
||||
if !collapsed {
|
||||
|
@ -86,50 +86,37 @@ impl Control {
|
|||
|
||||
let mut idx = 0;
|
||||
while idx < self.controls.len() {
|
||||
let response = ui
|
||||
let delete_element = ui
|
||||
.horizontal(|ui| {
|
||||
ui.add_space(INDENTATION_STEP * self.nesting_info.indentation_level as f32);
|
||||
|
||||
let response = ui.button("❌");
|
||||
let delete_element = ui.button("❌").clicked();
|
||||
|
||||
ui.with_layout(Layout::top_down(Align::LEFT), |ui| {
|
||||
let session_content =
|
||||
session_fragment["content"].as_array_mut().unwrap();
|
||||
let up_down_result = up_down::up_down_buttons(ui, idx, self.controls.len());
|
||||
|
||||
ui.spacing_mut().item_spacing = egui::vec2(0.0, 0.0);
|
||||
|
||||
let up_clicked = ui
|
||||
.add_visible_ui(idx > 0, |ui| ui.small_button("⬆"))
|
||||
.inner
|
||||
.clicked();
|
||||
let down_clicked = ui
|
||||
.add_visible_ui(idx < self.controls.len() - 1, |ui| {
|
||||
ui.small_button("⬇")
|
||||
})
|
||||
.inner
|
||||
.clicked();
|
||||
|
||||
if up_clicked || down_clicked {
|
||||
let mut session_content = session_content.clone();
|
||||
session_content
|
||||
.swap(idx, if up_clicked { idx - 1 } else { idx + 1 });
|
||||
|
||||
request = get_content_request(&self.nesting_info, session_content);
|
||||
if up_down_result != UpDownResult::None {
|
||||
if up_down_result == UpDownResult::Up {
|
||||
session_content.swap(idx, idx - 1);
|
||||
} else {
|
||||
session_content.swap(idx, idx + 1);
|
||||
}
|
||||
});
|
||||
|
||||
response
|
||||
request =
|
||||
get_content_request(&self.nesting_info, session_content.clone());
|
||||
}
|
||||
|
||||
delete_element
|
||||
})
|
||||
.inner;
|
||||
|
||||
let session_array_mut = session_fragment["content"].as_array_mut().unwrap();
|
||||
|
||||
if response.clicked() {
|
||||
session_array_mut.remove(idx);
|
||||
if delete_element {
|
||||
session_content.remove(idx);
|
||||
self.controls.remove(idx);
|
||||
|
||||
request = get_content_request(&self.nesting_info, session_content.clone());
|
||||
} else {
|
||||
request = self.controls[idx]
|
||||
.ui(ui, &mut session_array_mut[idx], true)
|
||||
.ui(ui, &mut session_content[idx], true)
|
||||
.or(request);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue