Skip to content

Commit

Permalink
Only load directory entries on modal opening
Browse files Browse the repository at this point in the history
  • Loading branch information
melody-rs committed Jul 1, 2024
1 parent b1f223b commit ccd34fa
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 100 deletions.
184 changes: 101 additions & 83 deletions crates/modals/src/event_graphic_picker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,30 @@ use luminol_components::UiExt;
use luminol_core::prelude::*;

pub struct Modal {
entries: Vec<Entry>,
filtered_entries: Vec<Entry>,
search_text: String,

open: bool,
state: State,
id_source: egui::Id,

selected: Selected,
opacity: i32,
hue: i32,
blend_mode: rpg::BlendMode,

tilepicker: Tilepicker,

button_viewport: Viewport,
button_sprite: Option<Event>,
}

enum State {
Closed,
Open {
entries: Vec<Entry>,
filtered_entries: Vec<Entry>,
search_text: String,

selected: Selected,

opacity: i32,
hue: i32,
blend_mode: rpg::BlendMode,
},
}

#[derive(PartialEq, PartialOrd, Eq, Ord, Clone)]
struct Entry {
path: camino::Utf8PathBuf,
Expand Down Expand Up @@ -80,25 +86,6 @@ impl Modal {
id_source: egui::Id,
) -> Self {
// TODO error handling
let mut entries: Vec<_> = update_state
.filesystem
.read_dir("Graphics/Characters")
.unwrap()
.into_iter()
.map(|m| {
let path = m
.path
.strip_prefix("Graphics/Characters")
.unwrap_or(&m.path)
.with_extension("");
Entry {
path,
invalid: false,
}
})
.collect();
entries.sort_unstable();

let mut tilepicker = Tilepicker::new(
&update_state.graphics,
tileset,
Expand All @@ -119,18 +106,9 @@ impl Modal {
.unwrap();

Self {
filtered_entries: entries.clone(),
search_text: String::new(),
entries,

open: false,
state: State::Closed,
id_source,

selected: Selected::None,
opacity: graphic.opacity,
hue: graphic.character_hue,
blend_mode: graphic.blend_type,

tilepicker,

button_viewport,
Expand All @@ -151,7 +129,8 @@ impl luminol_core::Modal for Modal {
let desired_size = egui::vec2(64., 96.) + ui.spacing().button_padding * 2.;
let (rect, mut response) = ui.allocate_at_least(desired_size, egui::Sense::click());

let visuals = ui.style().interact_selectable(&response, self.open);
let is_open = matches!(self.state, State::Open { .. });
let visuals = ui.style().interact_selectable(&response, is_open);
let rect = rect.expand(visuals.expansion);
ui.painter()
.rect(rect, visuals.rounding, visuals.bg_fill, visuals.bg_stroke);
Expand All @@ -171,8 +150,8 @@ impl luminol_core::Modal for Modal {
ui.painter().add(callback);
}

if response.clicked() {
self.selected = if let Some(id) = data.tile_id {
if response.clicked() && !is_open {
let selected = if let Some(id) = data.tile_id {
Selected::Tile(id)
} else if let Some(path) = data.character_name.clone() {
let sprite = match Self::load_preview_sprite(
Expand Down Expand Up @@ -203,11 +182,36 @@ impl luminol_core::Modal for Modal {
} else {
Selected::None
};
self.blend_mode = data.blend_type;
self.hue = data.character_hue;
self.opacity = data.opacity;

self.open = true;
// FIXME error handling
let mut entries: Vec<_> = update_state
.filesystem
.read_dir("Graphics/Characters")
.unwrap()
.into_iter()
.map(|m| {
let path = m
.path
.strip_prefix("Graphics/Characters")
.unwrap_or(&m.path)
.with_extension("");
Entry {
path,
invalid: false,
}
})
.collect();
entries.sort_unstable();

self.state = State::Open {
filtered_entries: entries.clone(),
entries,
search_text: String::new(),
selected,
opacity: data.opacity,
hue: data.character_hue,
blend_mode: data.blend_type,
};
}
if self.show_window(update_state, ui.ctx(), data) {
response.mark_changed();
Expand All @@ -218,7 +222,7 @@ impl luminol_core::Modal for Modal {
}

fn reset(&mut self) {
self.open = false;
self.state = State::Closed;
}
}

Expand Down Expand Up @@ -290,26 +294,40 @@ impl Modal {
ctx: &egui::Context,
data: &mut rpg::Graphic,
) -> bool {
let mut win_open = true;
let mut keep_open = true;
let mut needs_save = false;

let State::Open {
entries,
filtered_entries,
search_text,
selected,
opacity,
hue,
blend_mode,
} = &mut self.state
else {
return false;
};

egui::Window::new("Event Graphic Picker")
.resizable(true)
.open(&mut self.open)
.open(&mut win_open)
.id(self.id_source.with("window"))
.show(ctx, |ui| {
egui::SidePanel::left(self.id_source.with("sidebar")).show_inside(ui, |ui| {
let out = egui::TextEdit::singleline(&mut self.search_text)
let out = egui::TextEdit::singleline(search_text)
.hint_text("Search 🔎")
.show(ui);
if out.response.changed() {
let matcher = fuzzy_matcher::skim::SkimMatcherV2::default();
self.filtered_entries = self
.entries
*filtered_entries =
entries
.iter()
.filter(|entry| {
matcher
.fuzzy(entry.path.as_str(), &self.search_text, false)
.fuzzy(entry.path.as_str(), search_text, false)
.is_some()
})
.cloned()
Expand All @@ -327,21 +345,21 @@ impl Modal {
.show_rows(
ui,
row_height,
self.filtered_entries.len() + 2,
filtered_entries.len() + 2,
|ui, mut rows| {
if rows.contains(&0) {
let res = ui.selectable_label(matches!(self.selected, Selected::None), "(None)");
if res.clicked() && !matches!(self.selected, Selected::None) {
self.selected = Selected::None;
let res = ui.selectable_label(matches!(selected, Selected::None), "(None)");
if res.clicked() && !matches!(selected, Selected::None) {
*selected = Selected::None;
}
}

if rows.contains(&1) {
let checked = matches!(self.selected, Selected::Tile(_));
let checked = matches!(selected, Selected::Tile(_));
ui.with_stripe(true, |ui| {
let res = ui.selectable_label(checked, "(Tileset)");
if res.clicked() && !checked {
self.selected = Selected::Tile(384);
*selected = Selected::Tile(384);
}
});
}
Expand All @@ -350,9 +368,9 @@ impl Modal {
rows.start = rows.start.saturating_sub(2);
rows.end = rows.end.saturating_sub(2);

for (i, Entry { path: entry ,invalid}) in self.filtered_entries[rows.clone()].iter_mut().enumerate() {
for (i, Entry { path: entry ,invalid}) in filtered_entries[rows.clone()].iter_mut().enumerate() {
let checked =
matches!(self.selected, Selected::Graphic { ref path, .. } if path == entry);
matches!(selected, Selected::Graphic { ref path, .. } if path == entry);
let mut text = egui::RichText::new(entry.as_str());
if *invalid {
text = text.color(egui::Color32::LIGHT_RED);
Expand All @@ -362,15 +380,15 @@ impl Modal {
let res = ui.add_enabled(!*invalid, egui::SelectableLabel::new(checked, text));

if res.clicked() {
let sprite = match Self::load_preview_sprite(update_state, entry, self.hue, self.opacity) {
let sprite = match Self::load_preview_sprite(update_state, entry, *hue, *opacity) {
Ok(sprite) => sprite,
Err(e) => {
luminol_core::error!(update_state.toasts, e);
*invalid = true; // FIXME update non-filtered entry too
return;
}
};
self.selected = Selected::Graphic { path: entry.clone(), direction: 2, pattern: 0, sprite };
*selected = Selected::Graphic { path: entry.clone(), direction: 2, pattern: 0, sprite };
}
});
}
Expand All @@ -381,23 +399,23 @@ impl Modal {
egui::TopBottomPanel::top(self.id_source.with("top")).show_inside(ui, |ui| {
ui.horizontal(|ui| {
ui.label("Opacity");
if ui.add(egui::Slider::new(&mut self.opacity, 0..=255)).changed() {
self.tilepicker.tiles.display.set_opacity(&update_state.graphics.render_state, self.opacity as f32 / 255., 0);
if let Selected::Graphic { sprite,.. } = &mut self.selected {
sprite.sprite.graphic.set_opacity(&update_state.graphics.render_state, self.opacity);
if ui.add(egui::Slider::new(opacity, 0..=255)).changed() {
self.tilepicker.tiles.display.set_opacity(&update_state.graphics.render_state, *opacity as f32 / 255., 0);
if let Selected::Graphic { sprite,.. } = selected {
sprite.sprite.graphic.set_opacity(&update_state.graphics.render_state, *opacity);
}
}
ui.label("Hue");
if ui.add(egui::Slider::new(&mut self.hue, 0..=360)).changed() {
self.tilepicker.tiles.display.set_hue(&update_state.graphics.render_state, self.hue as f32 / 360.0, 0);
if let Selected::Graphic { sprite,.. } = &mut self.selected {
sprite.sprite.graphic.set_hue(&update_state.graphics.render_state, self.hue);
if ui.add(egui::Slider::new(hue, 0..=360)).changed() {
self.tilepicker.tiles.display.set_hue(&update_state.graphics.render_state, *hue as f32 / 360.0, 0);
if let Selected::Graphic { sprite,.. } =selected {
sprite.sprite.graphic.set_hue(&update_state.graphics.render_state,*hue);
}
}
});
ui.horizontal(|ui| {
ui.label("Blend Mode");
luminol_components::EnumComboBox::new(self.id_source.with("blend_mode"), &mut self.blend_mode).ui(ui);
luminol_components::EnumComboBox::new(self.id_source.with("blend_mode"), blend_mode).ui(ui);
});
});
egui::TopBottomPanel::bottom(self.id_source.with("bottom")).show_inside(ui, |ui| {
Expand All @@ -407,7 +425,7 @@ impl Modal {

egui::CentralPanel::default().show_inside(ui, |ui| {
egui::ScrollArea::both().auto_shrink([false,false]).show_viewport(ui, |ui, viewport| {
match &mut self.selected {
match selected {
Selected::None => {}
Selected::Graphic { direction, pattern, sprite, .. } => {
let (canvas_rect, response) = ui.allocate_exact_size(
Expand Down Expand Up @@ -441,7 +459,7 @@ impl Modal {

let ch = sprite.sprite_size.y / 4.;
let cw = sprite.sprite_size.x / 4.;
let rect = egui::Rect::from_min_size(egui::pos2(cw ** pattern as f32, ch * (*direction as f32 - 2.) / 2.), egui::vec2(cw, ch)).translate(canvas_rect.min.to_vec2());
let rect = egui::Rect::from_min_size(egui::pos2(*pattern as f32 * cw, (*direction as f32 - 2.) * ch / 2.), egui::vec2(cw, ch)).translate(canvas_rect.min.to_vec2());
ui.painter().rect_stroke(rect, 5.0, egui::Stroke::new(1.0, egui::Color32::WHITE));

if response.clicked() {
Expand Down Expand Up @@ -504,13 +522,13 @@ impl Modal {
});

if needs_save {
match self.selected {
match selected {
Selected::None => {
data.tile_id = None;
data.character_name = None;
}
Selected::Tile(id) => {
data.tile_id = Some(id);
Selected::Tile( id) => {
data.tile_id = Some(*id);
data.character_name = None;
}
Selected::Graphic {
Expand All @@ -521,18 +539,18 @@ impl Modal {
} => {
data.tile_id = None;
data.character_name = Some(path.clone());
data.direction = direction;
data.pattern = pattern;
data.direction = *direction;
data.pattern = *pattern;
}
}
data.blend_type = self.blend_mode;
data.character_hue = self.hue;
data.opacity = self.opacity;
data.blend_type = *blend_mode;
data.character_hue = *hue;
data.opacity = *opacity;
self.update_graphic(update_state, data);
}

if !keep_open {
self.open = false;
if !(win_open && keep_open) {
self.state = State::Closed;
}

needs_save
Expand Down
Loading

0 comments on commit ccd34fa

Please sign in to comment.