collapsable rule editors
This commit is contained in:
parent
5c282a28ed
commit
c345d671dd
2 changed files with 163 additions and 143 deletions
|
@ -27,8 +27,8 @@ pub struct Rule {
|
||||||
variants: Vec<SubRule>,
|
variants: Vec<SubRule>,
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
// probability: u8
|
// probability: u8
|
||||||
pub flip_h: bool,
|
pub flip_x: bool,
|
||||||
pub flip_v: bool,
|
pub flip_y: bool,
|
||||||
pub rotate: bool,
|
pub rotate: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,8 +121,8 @@ impl Rule {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
base: SubRule::new(),
|
base: SubRule::new(),
|
||||||
variants: vec![SubRule::new()],
|
variants: vec![SubRule::new()],
|
||||||
flip_h: false,
|
flip_x: false,
|
||||||
flip_v: false,
|
flip_y: false,
|
||||||
rotate: false,
|
rotate: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -192,7 +192,7 @@ impl Rule {
|
||||||
variants.extend_from_slice(&new);
|
variants.extend_from_slice(&new);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.flip_h {
|
if self.flip_x {
|
||||||
transform_variants(&mut self.variants, |b| {
|
transform_variants(&mut self.variants, |b| {
|
||||||
let mut new = b.clone();
|
let mut new = b.clone();
|
||||||
for y in 0..new.height {
|
for y in 0..new.height {
|
||||||
|
@ -204,7 +204,7 @@ impl Rule {
|
||||||
new
|
new
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if self.flip_v {
|
if self.flip_y {
|
||||||
transform_variants(&mut self.variants, |b| {
|
transform_variants(&mut self.variants, |b| {
|
||||||
let mut new = b.clone();
|
let mut new = b.clone();
|
||||||
for y in 0..new.height {
|
for y in 0..new.height {
|
||||||
|
@ -302,7 +302,7 @@ impl Dish {
|
||||||
(RuleCellFrom::One(Cell(0)), RuleCellTo::One(Cell(1))),
|
(RuleCellFrom::One(Cell(0)), RuleCellTo::One(Cell(1))),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
flip_h: true,
|
flip_x: true,
|
||||||
..Rule::new()
|
..Rule::new()
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
@ -10,7 +10,7 @@ use eframe::{
|
||||||
epaint::Hsva,
|
epaint::Hsva,
|
||||||
NativeOptions,
|
NativeOptions,
|
||||||
};
|
};
|
||||||
use egui::PointerButton;
|
use egui::{collapsing_header::CollapsingState, PointerButton};
|
||||||
use native_dialog::FileDialog;
|
use native_dialog::FileDialog;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -176,16 +176,16 @@ impl eframe::App for UScope {
|
||||||
let mut to_remove = None;
|
let mut to_remove = None;
|
||||||
let mut to_clone = None;
|
let mut to_clone = None;
|
||||||
for (i, rule) in self.dish.rules.iter_mut().enumerate() {
|
for (i, rule) in self.dish.rules.iter_mut().enumerate() {
|
||||||
ui.separator();
|
// ui.separator();
|
||||||
rule_editor(ui, rule, &self.cell_types, &self.dish.cell_groups);
|
rule_editor(
|
||||||
ui.horizontal(|ui| {
|
ui,
|
||||||
if ui.button("delete").clicked() {
|
rule,
|
||||||
to_remove = Some(i);
|
i,
|
||||||
}
|
&self.cell_types,
|
||||||
if ui.button("copy").clicked() {
|
&self.dish.cell_groups,
|
||||||
to_clone = Some(i);
|
&mut to_remove,
|
||||||
}
|
&mut to_clone,
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
if let Some(i) = to_remove {
|
if let Some(i) = to_remove {
|
||||||
self.dish.rules.remove(i);
|
self.dish.rules.remove(i);
|
||||||
|
@ -246,138 +246,158 @@ const CSIZE: f32 = 24.;
|
||||||
const RESIZE_BUTTON_WIDTH: f32 = 8.;
|
const RESIZE_BUTTON_WIDTH: f32 = 8.;
|
||||||
|
|
||||||
const OUTLINE: (f32, Color32) = (2., Color32::GRAY);
|
const OUTLINE: (f32, Color32) = (2., Color32::GRAY);
|
||||||
fn rule_editor(ui: &mut Ui, rule: &mut Rule, cells: &[CellData], groups: &[Vec<Option<Cell>>]) {
|
fn rule_editor(
|
||||||
ui.checkbox(&mut rule.enabled, "enable rule");
|
ui: &mut Ui,
|
||||||
ui.horizontal(|ui| {
|
rule: &mut Rule,
|
||||||
ui.label("flip");
|
index: usize,
|
||||||
if ui.checkbox(&mut rule.flip_h, "H").changed() {
|
cells: &[CellData],
|
||||||
rule.generate_variants();
|
groups: &[Vec<Option<Cell>>],
|
||||||
}
|
to_remove: &mut Option<usize>,
|
||||||
if ui.checkbox(&mut rule.flip_v, "V").changed() {
|
to_clone: &mut Option<usize>,
|
||||||
rule.generate_variants();
|
) {
|
||||||
}
|
let id = ui.make_persistent_id(format!("rule {index}"));
|
||||||
});
|
CollapsingState::load_with_default_open(ui.ctx(), id, true)
|
||||||
if ui.checkbox(&mut rule.rotate, "rotate").changed() {
|
.show_header(ui, |ui| {
|
||||||
rule.generate_variants();
|
ui.checkbox(&mut rule.enabled, &rule.name);
|
||||||
}
|
if ui.button("delete").clicked() {
|
||||||
|
*to_remove = Some(index);
|
||||||
let cells_y = rule.height();
|
}
|
||||||
let cells_x = rule.width();
|
if ui.button("copy").clicked() {
|
||||||
let patt_width = CSIZE * cells_x as f32;
|
*to_clone = Some(index);
|
||||||
let patt_height = CSIZE * cells_y as f32;
|
}
|
||||||
|
})
|
||||||
let (_, bounds) = ui.allocate_space(Vec2::new(
|
.body(|ui| {
|
||||||
patt_width * 2. + RESIZE_BUTTON_WIDTH * 4. + CSIZE,
|
ui.text_edit_singleline(&mut rule.name);
|
||||||
patt_height + RESIZE_BUTTON_WIDTH * 2.,
|
ui.horizontal(|ui| {
|
||||||
));
|
if ui.checkbox(&mut rule.flip_x, "flip X").changed() {
|
||||||
|
rule.generate_variants();
|
||||||
let from_cells_rect = Rect::from_min_size(
|
}
|
||||||
bounds.min + Vec2::splat(RESIZE_BUTTON_WIDTH),
|
if ui.checkbox(&mut rule.flip_y, "flip Y").changed() {
|
||||||
Vec2::new(patt_width, patt_height),
|
rule.generate_variants();
|
||||||
);
|
}
|
||||||
let to_cells_rect = Rect::from_min_size(
|
});
|
||||||
bounds.min
|
if ui.checkbox(&mut rule.rotate, "rotate").changed() {
|
||||||
+ Vec2::splat(RESIZE_BUTTON_WIDTH)
|
|
||||||
+ Vec2::X * (patt_width + RESIZE_BUTTON_WIDTH * 2. + CSIZE),
|
|
||||||
Vec2::new(patt_width, patt_height),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut overlay_lines = Vec::new();
|
|
||||||
for x in 0..cells_x {
|
|
||||||
for y in 0..cells_y {
|
|
||||||
let (left, right) = rule.get_mut(x, y);
|
|
||||||
let changed_left =
|
|
||||||
rule_cell_edit_from(ui, from_cells_rect.min, left, x, y, cells, groups);
|
|
||||||
let changed_right = rule_cell_edit_to(
|
|
||||||
ui,
|
|
||||||
to_cells_rect.min,
|
|
||||||
right,
|
|
||||||
(x, y),
|
|
||||||
cells,
|
|
||||||
groups,
|
|
||||||
(cells_x, cells_y),
|
|
||||||
&mut overlay_lines,
|
|
||||||
);
|
|
||||||
if changed_left || changed_right {
|
|
||||||
rule.generate_variants();
|
rule.generate_variants();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let delete_mode = ui.input(|i| i.modifiers.shift);
|
let cells_y = rule.height();
|
||||||
|
let cells_x = rule.width();
|
||||||
|
let patt_width = CSIZE * cells_x as f32;
|
||||||
|
let patt_height = CSIZE * cells_y as f32;
|
||||||
|
|
||||||
let mut resize_box = |x, y, w, h| {
|
let (_, bounds) = ui.allocate_space(Vec2::new(
|
||||||
let rect_a = Rect::from_min_size(Pos2::new(x, y), Vec2::new(w, h));
|
patt_width * 2. + RESIZE_BUTTON_WIDTH * 4. + CSIZE,
|
||||||
let a = ui.allocate_rect(rect_a, Sense::click());
|
patt_height + RESIZE_BUTTON_WIDTH * 2.,
|
||||||
let rect_b = rect_a.translate(to_cells_rect.min - from_cells_rect.min);
|
));
|
||||||
let b = ui.allocate_rect(rect_b, Sense::click());
|
|
||||||
let result = a.union(b);
|
let from_cells_rect = Rect::from_min_size(
|
||||||
let color = if result.hovered() {
|
bounds.min + Vec2::splat(RESIZE_BUTTON_WIDTH),
|
||||||
if delete_mode {
|
Vec2::new(patt_width, patt_height),
|
||||||
Color32::RED
|
);
|
||||||
} else {
|
let to_cells_rect = Rect::from_min_size(
|
||||||
Color32::GRAY
|
bounds.min
|
||||||
|
+ Vec2::splat(RESIZE_BUTTON_WIDTH)
|
||||||
|
+ Vec2::X * (patt_width + RESIZE_BUTTON_WIDTH * 2. + CSIZE),
|
||||||
|
Vec2::new(patt_width, patt_height),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut overlay_lines = Vec::new();
|
||||||
|
for x in 0..cells_x {
|
||||||
|
for y in 0..cells_y {
|
||||||
|
let (left, right) = rule.get_mut(x, y);
|
||||||
|
let changed_left =
|
||||||
|
rule_cell_edit_from(ui, from_cells_rect.min, left, x, y, cells, groups);
|
||||||
|
let changed_right = rule_cell_edit_to(
|
||||||
|
ui,
|
||||||
|
to_cells_rect.min,
|
||||||
|
right,
|
||||||
|
(x, y),
|
||||||
|
cells,
|
||||||
|
groups,
|
||||||
|
(cells_x, cells_y),
|
||||||
|
&mut overlay_lines,
|
||||||
|
);
|
||||||
|
if changed_left || changed_right {
|
||||||
|
rule.generate_variants();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Color32::DARK_GRAY
|
|
||||||
};
|
|
||||||
ui.painter_at(bounds).rect_filled(rect_a, 0., color);
|
|
||||||
ui.painter_at(bounds).rect_filled(rect_b, 0., color);
|
|
||||||
|
|
||||||
result.clicked()
|
let delete_mode = ui.input(|i| i.modifiers.shift);
|
||||||
};
|
|
||||||
if resize_box(
|
|
||||||
bounds.min.x,
|
|
||||||
bounds.min.y + RESIZE_BUTTON_WIDTH,
|
|
||||||
RESIZE_BUTTON_WIDTH,
|
|
||||||
patt_height,
|
|
||||||
) {
|
|
||||||
if delete_mode {
|
|
||||||
rule.resize(Rule::SHRINK_LEFT);
|
|
||||||
} else {
|
|
||||||
rule.resize(Rule::EXTEND_LEFT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if resize_box(
|
|
||||||
from_cells_rect.max.x,
|
|
||||||
bounds.min.y + RESIZE_BUTTON_WIDTH,
|
|
||||||
RESIZE_BUTTON_WIDTH,
|
|
||||||
patt_height,
|
|
||||||
) {
|
|
||||||
if delete_mode {
|
|
||||||
rule.resize(Rule::SHRINK_RIGHT);
|
|
||||||
} else {
|
|
||||||
rule.resize(Rule::EXTEND_RIGHT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if resize_box(
|
|
||||||
bounds.min.x + RESIZE_BUTTON_WIDTH,
|
|
||||||
bounds.min.y,
|
|
||||||
patt_width,
|
|
||||||
RESIZE_BUTTON_WIDTH,
|
|
||||||
) {
|
|
||||||
if delete_mode {
|
|
||||||
rule.resize(Rule::SHRINK_UP);
|
|
||||||
} else {
|
|
||||||
rule.resize(Rule::EXTEND_UP);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if resize_box(
|
|
||||||
bounds.min.x + RESIZE_BUTTON_WIDTH,
|
|
||||||
bounds.max.y - RESIZE_BUTTON_WIDTH,
|
|
||||||
patt_width,
|
|
||||||
RESIZE_BUTTON_WIDTH,
|
|
||||||
) {
|
|
||||||
if delete_mode {
|
|
||||||
rule.resize(Rule::SHRINK_DOWN);
|
|
||||||
} else {
|
|
||||||
rule.resize(Rule::EXTEND_DOWN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (a, b) in overlay_lines {
|
let mut resize_box = |x, y, w, h| {
|
||||||
ui.painter().line_segment([a, b], (2., Color32::WHITE));
|
let rect_a = Rect::from_min_size(Pos2::new(x, y), Vec2::new(w, h));
|
||||||
}
|
let a = ui.allocate_rect(rect_a, Sense::click());
|
||||||
|
let rect_b = rect_a.translate(to_cells_rect.min - from_cells_rect.min);
|
||||||
|
let b = ui.allocate_rect(rect_b, Sense::click());
|
||||||
|
let result = a.union(b);
|
||||||
|
let color = if result.hovered() {
|
||||||
|
if delete_mode {
|
||||||
|
Color32::RED
|
||||||
|
} else {
|
||||||
|
Color32::GRAY
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Color32::DARK_GRAY
|
||||||
|
};
|
||||||
|
ui.painter_at(bounds).rect_filled(rect_a, 0., color);
|
||||||
|
ui.painter_at(bounds).rect_filled(rect_b, 0., color);
|
||||||
|
|
||||||
|
result.clicked()
|
||||||
|
};
|
||||||
|
if resize_box(
|
||||||
|
bounds.min.x,
|
||||||
|
bounds.min.y + RESIZE_BUTTON_WIDTH,
|
||||||
|
RESIZE_BUTTON_WIDTH,
|
||||||
|
patt_height,
|
||||||
|
) {
|
||||||
|
if delete_mode {
|
||||||
|
rule.resize(Rule::SHRINK_LEFT);
|
||||||
|
} else {
|
||||||
|
rule.resize(Rule::EXTEND_LEFT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if resize_box(
|
||||||
|
from_cells_rect.max.x,
|
||||||
|
bounds.min.y + RESIZE_BUTTON_WIDTH,
|
||||||
|
RESIZE_BUTTON_WIDTH,
|
||||||
|
patt_height,
|
||||||
|
) {
|
||||||
|
if delete_mode {
|
||||||
|
rule.resize(Rule::SHRINK_RIGHT);
|
||||||
|
} else {
|
||||||
|
rule.resize(Rule::EXTEND_RIGHT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if resize_box(
|
||||||
|
bounds.min.x + RESIZE_BUTTON_WIDTH,
|
||||||
|
bounds.min.y,
|
||||||
|
patt_width,
|
||||||
|
RESIZE_BUTTON_WIDTH,
|
||||||
|
) {
|
||||||
|
if delete_mode {
|
||||||
|
rule.resize(Rule::SHRINK_UP);
|
||||||
|
} else {
|
||||||
|
rule.resize(Rule::EXTEND_UP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if resize_box(
|
||||||
|
bounds.min.x + RESIZE_BUTTON_WIDTH,
|
||||||
|
bounds.max.y - RESIZE_BUTTON_WIDTH,
|
||||||
|
patt_width,
|
||||||
|
RESIZE_BUTTON_WIDTH,
|
||||||
|
) {
|
||||||
|
if delete_mode {
|
||||||
|
rule.resize(Rule::SHRINK_DOWN);
|
||||||
|
} else {
|
||||||
|
rule.resize(Rule::EXTEND_DOWN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (a, b) in overlay_lines {
|
||||||
|
ui.painter().line_segment([a, b], (2., Color32::WHITE));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rule_cell_edit_from(
|
fn rule_cell_edit_from(
|
||||||
|
|
Loading…
Reference in a new issue