Skip to content

Commit

Permalink
Add graphic picker to item editor
Browse files Browse the repository at this point in the history
  • Loading branch information
melody-rs committed Jul 2, 2024
1 parent 81e81a3 commit 31614ec
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 27 deletions.
6 changes: 4 additions & 2 deletions crates/data/src/rmxp/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
// along with Luminol. If not, see <http://www.gnu.org/licenses/>.
pub use crate::{
id_alox, id_serde, id_vec_alox, id_vec_serde, optional_id_alox, optional_id_serde,
optional_path_serde, rpg::AudioFile, Path,
optional_path_alox, optional_path_serde, rpg::AudioFile, Path,
};

#[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone)]
Expand All @@ -27,7 +27,9 @@ pub struct Item {
#[marshal(with = "id_alox")]
pub id: usize,
pub name: String,
pub icon_name: String,
#[serde(with = "optional_path_serde")]
#[marshal(with = "optional_path_alox")]
pub icon_name: Option<camino::Utf8PathBuf>,
pub description: String,
pub scope: crate::rpg::Scope,
pub occasion: crate::rpg::Occasion,
Expand Down
131 changes: 121 additions & 10 deletions crates/modals/src/graphic_picker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ pub struct Modal {
button_size: egui::Vec2,
directory: camino::Utf8PathBuf, // do we make this &'static Utf8Path?

button_viewport: Viewport,
button_sprite: Option<Sprite>,
button_sprite: Option<ButtonSprite>,
}

enum State {
Expand All @@ -59,6 +58,12 @@ enum Selected {
},
}

struct ButtonSprite {
sprite: Sprite,
sprite_size: egui::Vec2,
viewport: Viewport,
}

struct PreviewSprite {
sprite: Sprite,
sprite_size: egui::Vec2,
Expand All @@ -77,42 +82,148 @@ impl Modal {
directory: camino::Utf8PathBuf,
path: Option<&camino::Utf8Path>,
button_size: egui::Vec2,
id_source: egui::Id,
id_source: impl Into<egui::Id>,
) -> Self {
let button_viewport = Viewport::new(&update_state.graphics, Default::default());
let button_sprite = path.map(|path| {
let texture = update_state
.graphics
.texture_loader
.load_now_dir(update_state.filesystem, &directory, path)
.unwrap(); // FIXME

Sprite::basic(&update_state.graphics, &texture, &button_viewport)
let button_viewport = Viewport::new(&update_state.graphics, Default::default());
let sprite = Sprite::basic(&update_state.graphics, &texture, &button_viewport);
ButtonSprite {
sprite,
sprite_size: texture.size_vec2(),
viewport: button_viewport,
}
});

Self {
state: State::Closed,
id_source,
id_source: id_source.into(),
button_size,
directory,
button_viewport,
button_sprite,
}
}
}

impl luminol_core::Modal for Modal {
type Data = camino::Utf8PathBuf;
type Data = Option<camino::Utf8PathBuf>;

fn button<'m>(
&'m mut self,
data: &'m mut Self::Data,
update_state: &'m mut luminol_core::UpdateState<'_>,
) -> impl egui::Widget + 'm {
|ui: &mut egui::Ui| todo!()
|ui: &mut egui::Ui| {
let desired_size = self.button_size + ui.spacing().button_padding * 2.0;
let (rect, mut response) = ui.allocate_at_least(desired_size, egui::Sense::click());

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

if let Some(ButtonSprite {
sprite,
sprite_size,
viewport,
}) = &mut self.button_sprite
{
let translation = (desired_size - *sprite_size) / 2.;
viewport.set(
&update_state.graphics.render_state,
glam::vec2(desired_size.x, desired_size.y),
glam::vec2(translation.x, translation.y),
glam::Vec2::ONE,
);
let callback = luminol_egui_wgpu::Callback::new_paint_callback(
response.rect,
Painter::new(sprite.prepare(&update_state.graphics)),
);
ui.painter().add(callback);
}

if response.clicked() && !is_open {
let selected = match data.clone() {
Some(path) => todo!(),
None => Selected::None,
};

// FIXME error handling
let mut entries: Vec<_> = update_state
.filesystem
.read_dir(&self.directory)
.unwrap()
.into_iter()
.map(|m| {
let path = m
.path
.strip_prefix(&self.directory)
.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,
};
}
if self.show_window(update_state, ui.ctx(), data) {
response.mark_changed();
}

response
}
}

fn reset(&mut self, update_state: &mut luminol_core::UpdateState<'_>, data: &Self::Data) {
todo!()
self.update_graphic(update_state, data); // we need to update the button sprite to prevent desyncs
self.state = State::Closed;
}
}

impl Modal {
fn update_graphic(
&mut self,
update_state: &UpdateState<'_>,
data: &Option<camino::Utf8PathBuf>,
) {
self.button_sprite = data.as_ref().map(|path| {
let texture = update_state
.graphics
.texture_loader
.load_now_dir(update_state.filesystem, &self.directory, path)
.unwrap(); // FIXME

let button_viewport = Viewport::new(&update_state.graphics, Default::default());
let sprite = Sprite::basic(&update_state.graphics, &texture, &button_viewport);
ButtonSprite {
sprite,
sprite_size: texture.size_vec2(),
viewport: button_viewport,
}
});
}

fn show_window(
&mut self,
update_state: &mut luminol_core::UpdateState<'_>,
ctx: &egui::Context,
data: &mut Option<camino::Utf8PathBuf>,
) -> bool {
false
}
}
46 changes: 32 additions & 14 deletions crates/ui/src/windows/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,32 +30,36 @@ pub struct Window {
selected_item_name: Option<String>,

menu_se_picker: luminol_modals::sound_picker::Modal,
graphic_picker: luminol_modals::graphic_picker::Modal,

previous_item: Option<usize>,

view: luminol_components::DatabaseView,
}

impl Window {
pub fn new() -> Self {
pub fn new(update_state: &luminol_core::UpdateState<'_>) -> Self {
let items = update_state.data.items();
let item = &items.data[0];
Self {
selected_item_name: None,
menu_se_picker: luminol_modals::sound_picker::Modal::new(
luminol_audio::Source::SE,
"item_menu_se_picker",
),
graphic_picker: luminol_modals::graphic_picker::Modal::new(
update_state,
camino::Utf8PathBuf::from("Graphics/Icons"),
item.icon_name.as_deref(),
egui::vec2(32., 32.),
"item_icon_picker",
),
previous_item: None,
view: luminol_components::DatabaseView::new(),
}
}
}

impl Default for Window {
fn default() -> Self {
Self::new()
}
}

impl luminol_core::Window for Window {
fn name(&self) -> String {
if let Some(name) = &self.selected_item_name {
Expand Down Expand Up @@ -106,13 +110,27 @@ impl luminol_core::Window for Window {
self.selected_item_name = Some(item.name.clone());

ui.with_padded_stripe(false, |ui| {
modified |= ui
.add(luminol_components::Field::new(
"Name",
egui::TextEdit::singleline(&mut item.name)
.desired_width(f32::INFINITY),
))
.changed();
ui.horizontal(|ui| {
modified |= ui
.add(luminol_components::Field::new(
"Icon",
self.graphic_picker
.button(&mut item.icon_name, update_state),
))
.changed();
if self.previous_item != Some(item.id) {
// avoid desyncs by resetting the modal if the item has changed
self.graphic_picker.reset(update_state, &item.icon_name);
}

modified |= ui
.add(luminol_components::Field::new(
"Name",
egui::TextEdit::singleline(&mut item.name)
.desired_width(f32::INFINITY),
))
.changed();
});

modified |= ui
.add(luminol_components::Field::new(
Expand Down
2 changes: 1 addition & 1 deletion src/app/top_bar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ impl TopBar {
if ui.button("Items").clicked() {
update_state
.edit_windows
.add_window(luminol_ui::windows::items::Window::new());
.add_window(luminol_ui::windows::items::Window::new(update_state));
}

if ui.button("Skills").clicked() {
Expand Down

0 comments on commit 31614ec

Please sign in to comment.