Skip to content

Commit

Permalink
editor: package node context menu on cards
Browse files Browse the repository at this point in the history
  • Loading branch information
barsoosayque committed Dec 1, 2024
1 parent 7f6d30f commit 5010ddc
Show file tree
Hide file tree
Showing 9 changed files with 333 additions and 261 deletions.
13 changes: 11 additions & 2 deletions crates/opensi-core/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,17 @@ impl PackageNode {
pub fn parent(&self) -> Option<PackageNode> {
match self {
PackageNode::Round(_) => None,
PackageNode::Theme(node) => Some(node.parent().into()),
PackageNode::Question(node) => Some(node.parent().into()),
PackageNode::Theme(idx) => Some(idx.parent().into()),
PackageNode::Question(idx) => Some(idx.parent().into()),
}
}

/// Get child node of this node, unless it's a [`PackageNode::Question`].
pub fn child(&self, child_idx: usize) -> Option<PackageNode> {
match self {
PackageNode::Round(idx) => Some(idx.theme(child_idx).into()),
PackageNode::Theme(idx) => Some(idx.question(child_idx).into()),
PackageNode::Question(_) => None,
}
}

Expand Down
33 changes: 33 additions & 0 deletions crates/opensi-core/src/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,17 @@ impl Package {
let round = Round { name: "Новый раунд".to_string(), ..Default::default() };
self.push_round(round)
}

/// Check if [`Round`] by index exist.
pub fn contains_round(&self, idx: impl Into<RoundIdx>) -> bool {
let idx = idx.into();
*idx < self.rounds.len()
}

/// Return amount of [`Round`]s in this package.
pub fn count_rounds(&self) -> usize {
self.rounds.len()
}
}

/// # Methods around [`Theme`]
Expand All @@ -170,6 +181,12 @@ impl Package {
self.get_round_mut(idx.round_index).and_then(|round| round.themes.get_mut(*idx))
}

/// Check if [`Theme`] by indices exist.
pub fn contains_theme(&self, idx: impl Into<ThemeIdx>) -> bool {
let idx = idx.into();
self.get_round(idx.parent()).map(|round| *idx < round.themes.len()).unwrap_or_default()
}

/// Remove [`Theme`] in [`Round`] by indices.
pub fn remove_theme(&mut self, idx: impl Into<ThemeIdx>) -> Option<Theme> {
let idx = idx.into();
Expand Down Expand Up @@ -215,6 +232,11 @@ impl Package {
let theme = Theme { name: "Новая тема".to_string(), ..Default::default() };
self.push_theme(idx, theme)
}

/// Return amount of [`Theme`]s in a [`Round`].
pub fn count_themes(&self, idx: impl Into<RoundIdx>) -> usize {
self.get_round(idx).map(|round| round.themes.len()).unwrap_or_default()
}
}

/// # Methods around [`Theme`].
Expand All @@ -231,6 +253,12 @@ impl Package {
self.get_theme_mut(idx.parent()).and_then(|theme| theme.questions.get_mut(*idx))
}

/// Check if [`Question`] by indices exist.
pub fn contains_question(&self, idx: impl Into<QuestionIdx>) -> bool {
let idx = idx.into();
self.get_theme(idx.parent()).map(|theme| *idx < theme.questions.len()).unwrap_or_default()
}

/// Remove [`Question`] in [`Theme`] in [`Round`] by indices.
pub fn remove_question(&mut self, idx: impl Into<QuestionIdx>) -> Option<Question> {
let idx = idx.into();
Expand Down Expand Up @@ -287,6 +315,11 @@ impl Package {
let question = Question { price, ..Default::default() };
self.push_question(idx, question)
}

/// Return amount of [`Question`]s in a [`Theme`].
pub fn count_questions(&self, idx: impl Into<ThemeIdx>) -> usize {
self.get_theme(idx).map(|theme| theme.questions.len()).unwrap_or_default()
}
}

/// # IO and resource methods
Expand Down
58 changes: 6 additions & 52 deletions crates/opensi-editor/src/app/package_tab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,61 +63,15 @@ fn package_metadata_edit(package: &Package, ui: &mut egui::Ui) {

fn package_rounds(package: &mut Package, selected: &mut Option<PackageNode>, ui: &mut egui::Ui) {
CardTable::new("package-rounds").show(ui, (1, package.rounds.len() + 1), |mut row| {
let index = row.index();
let Some(round) = package.get_round(index) else {
let idx = row.index();
if package.contains_round(idx) {
if row.round(package, idx, CardStyle::Important).clicked() {
*selected = Some(idx.into());
}
} else {
if row.custom("➕ Новый раунд", CardStyle::Weak).clicked() {
package.allocate_round();
}
return;
};

if row.round(round, CardStyle::Important).clicked() {
*selected = Some(index.into());
}
});
// let button_size = 20.0;
// egui_extras::TableBuilder::new(ui)
// .id_salt("rounds")
// .column(egui_extras::Column::remainder())
// .column(egui_extras::Column::exact(button_size))
// .cell_layout(
// egui::Layout::top_down_justified(egui::Align::Center)
// .with_main_wrap(false)
// .with_cross_justify(true)
// .with_cross_align(egui::Align::Center),
// )
// .body(|mut body| {
// for index in 0..package.rounds.len() {
// body.row((button_size + 4.0) * 3.0, |mut row| {
// row.col(|ui| {
// let Some(round) = package.get_round_mut(index) else {
// return;
// };
// if ui.add(Card::Round(round)).clicked() {
// *selected = Some(index.into());
// }
// });
// row.col(|ui| {
// ui.add_space(4.0);
// if ui.button("✏").on_hover_text("Редактировать").clicked()
// {
// *selected = Some(index.into());
// }
// if ui.button("🗐").on_hover_text("Дублировать").clicked()
// {
// package.duplicate_round(index);
// }
// if danger_button("❌", ui).on_hover_text("Удалить").clicked()
// {
// package.remove_round(index);
// }
// });
// });
// }
// });
// });

// strip.cell(|ui| {
// });
// });
}
134 changes: 8 additions & 126 deletions crates/opensi-editor/src/app/package_tree.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use egui::collapsing_header::CollapsingState;
use opensi_core::prelude::*;

use crate::element::node_name;
use crate::element::{node_context::PackageNodeContextMenu, node_name};

/// Ui for a whole [`Package`] in a form of a tree.
///
Expand Down Expand Up @@ -35,133 +35,15 @@ fn tree_node_ui<'a>(
is_selected: bool,
ui: &mut egui::Ui,
) -> bool {
#[derive(Default)]
struct Result {
new_name: Option<String>,
is_selected: bool,
is_duplicated: bool,
is_populated: bool,
is_deleted: bool,
}
let id = match node {
PackageNode::Round(RoundIdx { index }) => format!("tree-node-round-{index}"),
PackageNode::Theme(ThemeIdx { round_index, index }) => {
format!("tree-node-theme-{round_index}-{index}")
},
PackageNode::Question(QuestionIdx { round_index, theme_index, index }) => {
format!("tree-node-question-{round_index}-{theme_index}-{index}")
},
};
let id = egui::Id::new(id);
let mut result = Result::default();
let is_question = matches!(node, PackageNode::Question { .. });

if let Some(mut new_name) = ui.memory(|memory| memory.data.get_temp::<String>(id)) {
// renaming in process
let response = ui.text_edit_singleline(&mut new_name);

let is_renaming_done = ui.input(|input| input.key_pressed(egui::Key::Enter));
let is_exiting = is_renaming_done || !response.has_focus();

if is_renaming_done {
result.new_name = Some(new_name);
} else if response.changed() {
if is_question {
new_name.retain(|c| c.is_digit(10));
}
ui.memory_mut(|memory| memory.data.insert_temp(id, new_name));
}

if is_exiting {
ui.memory_mut(|memory| memory.data.remove_temp::<String>(id));
ui.ctx().request_repaint();
}
} else {
// regular button
let node_name = node_name(node, package);
let button = egui::Button::new(node_name.as_ref())
.selected(is_selected)
.fill(egui::Color32::TRANSPARENT);
let response = ui.add(button);

response.context_menu(|ui| {
if let Some(add_text) = match node {
PackageNode::Round { .. } => Some("Добавить тему"),
PackageNode::Theme { .. } => Some("Добавить вопрос"),
PackageNode::Question { .. } => None,
} {
if ui.button(format!("➕ {add_text}")).clicked() {
result.is_populated = true;
ui.close_menu();
}
ui.separator();
}

let change_text = if is_question {
"Изменить цену"
} else {
"Переименовать"
};
if ui.button(format!("✏ {}", change_text)).clicked() {
ui.memory_mut(|memory| {
let mut renaming = node_name.to_string();
if is_question {
renaming.retain(|c| c.is_digit(10));
}
memory.data.insert_temp(id, renaming);
});
response.request_focus();
ui.close_menu();
}
if ui.button("🗐 Дублировать").clicked() {
result.is_duplicated = true;
ui.close_menu();
}
ui.separator();
if ui.button("❌ Удалить").clicked() {
result.is_deleted = true;
ui.close_menu();
}
});
if response.clicked() {
result.is_selected = true;
}
}
let node_name = node_name(node, package);
let button = egui::Button::new(node_name.as_ref())
.selected(is_selected)
.fill(egui::Color32::TRANSPARENT);
let response = ui.add(button);

if result.is_populated {
if let Some(parent) = node.parent() {
package.allocate_node(parent);
}
}
if result.is_duplicated {
package.duplicate_node(node);
}
if result.is_deleted {
package.remove_node(node);
}
if let Some(new_name) = result.new_name {
match node {
PackageNode::Round(idx) => {
if let Some(round) = package.get_round_mut(idx) {
round.name = new_name;
};
},
PackageNode::Theme(idx) => {
if let Some(theme) = package.get_theme_mut(idx) {
theme.name = new_name;
};
},
PackageNode::Question(idx) => {
if let Some(question) = package.get_question_mut(idx) {
if let Ok(new_price) = new_name.parse() {
question.price = new_price;
}
};
},
}
}
PackageNodeContextMenu { package, node }.show(&response, ui);

return result.is_selected;
return response.clicked();
}

let Some(node) = node else {
Expand Down
33 changes: 17 additions & 16 deletions crates/opensi-editor/src/app/round_tab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,26 +72,27 @@ fn round_themes(
};

CardTable::new("round-themes").show(ui, count, |mut row| {
let theme_idx = idx.theme(row.index());
let Some(theme) = package.get_theme(theme_idx) else {
if row.custom("➕ Новая тема", CardStyle::Weak).clicked() {
package.allocate_theme(idx);
}
return;
};
let idx = idx.theme(row.index());

if row.theme(theme, CardStyle::Important).clicked() {
*selected = Some(theme_idx.into());
}
if package.contains_theme(idx) {
if row.theme(package, idx, CardStyle::Important).clicked() {
*selected = Some(idx.into());
}

for (question_index, question) in theme.questions.iter().enumerate() {
if row.question(question, CardStyle::Normal).clicked() {
*selected = Some(theme_idx.question(question_index).into());
for question_idx in 0..package.count_questions(idx).min(count.0 - 2) {
let idx = idx.question(question_idx);
if row.question(package, idx, CardStyle::Normal).clicked() {
*selected = Some(idx.into());
}
}
}

if row.custom("➕ Новый вопрос", CardStyle::Weak).clicked() {
package.allocate_question(theme_idx);
if row.custom("➕ Новый вопрос", CardStyle::Weak).clicked() {
package.allocate_question(idx);
}
} else {
if row.custom("➕ Новая тема", CardStyle::Weak).clicked() {
package.allocate_theme(idx.parent());
}
}
});
}
15 changes: 7 additions & 8 deletions crates/opensi-editor/src/app/theme_tab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,15 @@ fn theme_questions(
};

CardTable::new("theme-questions").show(ui, (1, theme.questions.len() + 1), |mut row| {
let index = row.index();
let Some(question) = package.get_question(idx.question(index)) else {
let idx = idx.question(row.index());
if package.contains_question(idx) {
if row.question(package, idx, CardStyle::Important).clicked() {
*selected = Some(idx.into());
}
} else {
if row.custom("➕ Новый вопрос", CardStyle::Weak).clicked() {
package.allocate_question(idx);
package.allocate_question(idx.parent());
}
return;
};

if row.question(question, CardStyle::Normal).clicked() {
*selected = Some(idx.question(index).into());
}
});
}
Loading

0 comments on commit 5010ddc

Please sign in to comment.