use crate::{
app::svg::Svg,
ui::{ChangeButton, OverwriteChangeButton},
};
use egui::{Button, Color32, RichText, Stroke, Ui, UiKind};
use serde::{Deserialize, Serialize};
use std::{
collections::{BTreeMap, BTreeSet},
fmt::Display,
};
#[derive(Serialize, Deserialize, Debug, Default, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
#[serde(transparent)]
pub struct Group(pub String);
impl Group {
pub fn color(&self) -> Color32 {
let index = Svg::groups()
.iter()
.position(|group| group == self)
.unwrap_or_default();
static COLORS: &[Color32] = &[
Color32::from_rgb(175, 213, 129),
Color32::from_rgb(177, 152, 221),
Color32::from_rgb(140, 181, 255),
Color32::from_rgb(217, 176, 140),
Color32::from_rgb(217, 148, 140),
Color32::from_rgb(113, 208, 132),
Color32::from_rgb(213, 129, 192),
Color32::from_rgb(163, 218, 224),
Color32::from_rgb(222, 233, 190),
Color32::from_rgb(255, 255, 255),
];
COLORS[index % COLORS.len()]
}
}
impl From<&Group> for String {
fn from(val: &Group) -> Self {
val.0.clone()
}
}
impl Display for Group {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
pub type GroupIndices = BTreeSet<usize>;
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, Hash)]
pub struct Groups(
#[serde(deserialize_with = "crate::storage::serde::deserialize_usize_index_btreemap")]
BTreeMap<usize, Group>,
);
impl Groups {
pub fn new(groups: BTreeMap<usize, Group>) -> Self {
Self(groups)
}
pub fn get(&self, index: usize) -> Option<&Group> {
self.0.get(&index)
}
pub fn remove_nonexistant_groups(&mut self) {
self.0.retain(|_, group| Svg::groups().contains(group));
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn insert(&mut self, index: usize, group: Group) {
self.0.insert(index, group);
}
pub fn remove(&mut self, index: usize) {
self.0.remove(&index);
}
pub fn clear(&mut self) {
self.0.clear();
}
pub fn iter(&self) -> impl Iterator<Item = (&usize, &Group)> {
self.0.iter()
}
}
impl OverwriteChangeButton for Groups {
const NAME: &'static str = "Groups";
}
impl ChangeButton for Groups {
fn change_button(&mut self, ui: &mut egui::Ui) -> bool {
if Svg::groups().is_empty() {
ui.label("No groups available");
return false;
}
let mut changed = false;
let mut next_index = 0;
while self.0.contains_key(&next_index) {
next_index += 1;
}
let mut insert = None;
let mut remove = None;
fn menu_button(
ui: &mut Ui,
index: usize,
group: &Group,
changed: &mut bool,
remove: &mut Option<usize>,
insert: &mut Option<(usize, Group)>,
) {
ui.menu_button(format!("{index} {}", group.0), |ui| {
ui.set_min_width(200.0);
if ui
.add(Button::new("🗑 Remove").fill(Color32::DARK_RED))
.clicked()
{
ui.close_kind(UiKind::Menu);
*remove = Some(index);
*changed = true;
}
let mut group = Some(group.to_owned());
if group_buttons(ui, &mut group) {
ui.close_kind(UiKind::Menu);
if let Some(group) = group {
*insert = Some((index, group));
} else {
*remove = Some(index);
}
*changed = true;
}
});
}
let mut new_group = None;
ui.menu_button(format!("+ Add Group {next_index}"), |ui| {
ui.set_min_width(200.0);
if group_buttons(ui, &mut new_group) {
ui.close_kind(UiKind::Menu);
if let Some(group) = new_group {
insert = Some((next_index, group));
changed = true;
}
}
});
for (index, group) in self.0.iter() {
ui.scope(|ui| {
{
let widgets = &mut ui.visuals_mut().widgets;
widgets.inactive.fg_stroke.color = Color32::from_black_alpha(200);
widgets.inactive.weak_bg_fill = group.color();
widgets.hovered.fg_stroke.color = Color32::from_black_alpha(200);
widgets.hovered.weak_bg_fill = group.color();
}
if ui.layout().main_dir().is_vertical() {
ui.vertical_centered_justified(|ui| {
menu_button(ui, *index, group, &mut changed, &mut remove, &mut insert);
});
} else {
menu_button(ui, *index, group, &mut changed, &mut remove, &mut insert);
}
});
}
if let Some((index, group)) = insert {
self.0.insert(index, group);
}
if let Some(index) = remove {
self.0.remove(&index);
}
changed
}
}
fn group_buttons(ui: &mut Ui, selected: &mut Option<Group>) -> bool {
let mut changed = false;
for group in Svg::groups() {
if ui
.add(group_button(&group, selected.as_ref() == Some(&group)))
.clicked()
{
*selected = Some(group);
changed = true;
}
}
changed
}
pub fn group_button(group: &'_ Group, selected: bool) -> Button<'_> {
let button =
Button::new(RichText::new(group).color(Color32::from_black_alpha(200))).fill(group.color());
if selected {
button.stroke(Stroke::new(3.0, Color32::RED))
} else {
button
}
}