feat(dashboard): Implement Dictionary control

This commit is contained in:
Riccardo Zaglia 2023-09-12 18:35:37 +08:00
parent a06232a4a6
commit ce7328568f
5 changed files with 283 additions and 71 deletions

View File

@ -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!()
};

View File

@ -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
}
}

View File

@ -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"));

View File

@ -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
}

View File

@ -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);
}