diff --git a/crates/components/src/filesystem_view.rs b/crates/components/src/filesystem_view.rs index 3b858a61..9abc4a3b 100644 --- a/crates/components/src/filesystem_view.rs +++ b/crates/components/src/filesystem_view.rs @@ -22,6 +22,7 @@ // terms of the Steamworks API by Valve Corporation, the licensors of this // Program grant you additional permission to convey the resulting work. +use crate::UiExt; use itertools::Itertools; pub struct FileSystemView { @@ -265,17 +266,14 @@ where let mut should_toggle = false; - let mut frame = egui::containers::Frame::none(); - if self.row_index % 2 != 0 { - frame = frame.fill(ui.visuals().faint_bg_color); - } + let is_faint = self.row_index % 2 != 0; self.row_index += 1; let mut header_response = None; match self.arena[node_id].get_mut() { Entry::File { name, selected } => { - frame.show(ui, |ui| { + ui.with_stripe(is_faint, |ui| { if ui .add(egui::SelectableLabel::new(*selected, name.to_string())) .clicked() @@ -315,7 +313,7 @@ where let layout = *ui.layout(); header_response = Some(header.show_header(ui, |ui| { ui.with_layout(layout, |ui| { - frame.show(ui, |ui| { + ui.with_stripe(is_faint, |ui| { if ui .add(egui::SelectableLabel::new( *selected, diff --git a/crates/components/src/id_vec.rs b/crates/components/src/id_vec.rs index 928a9300..1a336c3b 100644 --- a/crates/components/src/id_vec.rs +++ b/crates/components/src/id_vec.rs @@ -22,6 +22,8 @@ // terms of the Steamworks API by Valve Corporation, the licensors of this // Program grant you additional permission to convey the resulting work. +use crate::UiExt; + #[derive(Default, Clone)] struct IdVecSelectionState { pivot: Option, @@ -122,53 +124,40 @@ where let mut response = ui .group(|ui| { - ui.with_layout( - egui::Layout { - cross_justify: true, - ..Default::default() - }, - |ui| { - ui.set_width(ui.available_width()); - - ui.add( - egui::TextEdit::singleline(&mut state.search_string) - .hint_text("Search"), - ); - - let mut is_faint = false; - - for id in 0..self.len { - let is_id_selected = - self.reference.get(index).is_some_and(|x| *x == id); - if is_id_selected { - index += 1; - } + ui.with_cross_justify(|ui| { + ui.set_width(ui.available_width()); - let formatted = (self.formatter)(id); - if matcher - .fuzzy(&formatted, &state.search_string, false) - .is_none() - { - continue; - } + ui.add( + egui::TextEdit::singleline(&mut state.search_string).hint_text("Search"), + ); - let mut frame = egui::Frame::none(); - if is_faint { - frame = frame.fill(ui.visuals().faint_bg_color); - } - is_faint = !is_faint; - - frame.show(ui, |ui| { - if ui - .selectable_label(is_id_selected, (self.formatter)(id)) - .clicked() - { - clicked_id = Some(id); - } - }); + let mut is_faint = false; + + for id in 0..self.len { + let is_id_selected = self.reference.get(index).is_some_and(|x| *x == id); + if is_id_selected { + index += 1; } - }, - ) + + let formatted = (self.formatter)(id); + if matcher + .fuzzy(&formatted, &state.search_string, false) + .is_none() + { + continue; + } + + ui.with_stripe(is_faint, |ui| { + if ui + .selectable_label(is_id_selected, (self.formatter)(id)) + .clicked() + { + clicked_id = Some(id); + } + }); + is_faint = !is_faint; + } + }) .inner }) .response; @@ -284,73 +273,61 @@ where let mut response = ui .group(|ui| { - ui.with_layout( - egui::Layout { - cross_justify: true, - ..Default::default() - }, - |ui| { - ui.set_width(ui.available_width()); - - ui.add( - egui::TextEdit::singleline(&mut state.search_string) - .hint_text("Search"), - ); - - let mut is_faint = false; - - for id in 0..self.len { - let is_id_plus = self.plus.get(plus_index).is_some_and(|x| *x == id); - if is_id_plus { - plus_index += 1; - } - let is_id_minus = self.minus.get(minus_index).is_some_and(|x| *x == id); + ui.with_cross_justify(|ui| { + ui.set_width(ui.available_width()); + + ui.add( + egui::TextEdit::singleline(&mut state.search_string).hint_text("Search"), + ); + + let mut is_faint = false; + + for id in 0..self.len { + let is_id_plus = self.plus.get(plus_index).is_some_and(|x| *x == id); + if is_id_plus { + plus_index += 1; + } + let is_id_minus = self.minus.get(minus_index).is_some_and(|x| *x == id); + if is_id_minus { + minus_index += 1; + } + + let formatted = (self.formatter)(id); + if matcher + .fuzzy(&formatted, &state.search_string, false) + .is_none() + { + continue; + } + + ui.with_stripe(is_faint, |ui| { + // Make the background of the selectable label red if it's + // a minus if is_id_minus { - minus_index += 1; + ui.visuals_mut().selection.bg_fill = + ui.visuals().gray_out(ui.visuals().error_fg_color); } - let formatted = (self.formatter)(id); - if matcher - .fuzzy(&formatted, &state.search_string, false) - .is_none() + let label = (self.formatter)(id); + if ui + .selectable_label( + is_id_plus || is_id_minus, + if is_id_plus { + format!("+ {label}") + } else if is_id_minus { + format!("‒ {label}") + } else { + label + }, + ) + .clicked() { - continue; + clicked_id = Some(id); } - - let mut frame = egui::Frame::none(); - if is_faint { - frame = frame.fill(ui.visuals().faint_bg_color); - } - is_faint = !is_faint; - - frame.show(ui, |ui| { - // Make the background of the selectable label red if it's - // a minus - if is_id_minus { - ui.visuals_mut().selection.bg_fill = - ui.visuals().gray_out(ui.visuals().error_fg_color); - } - - let label = (self.formatter)(id); - if ui - .selectable_label( - is_id_plus || is_id_minus, - if is_id_plus { - format!("+ {label}") - } else if is_id_minus { - format!("‒ {label}") - } else { - label - }, - ) - .clicked() - { - clicked_id = Some(id); - } - }); - } - }, - ) + }); + is_faint = !is_faint; + } + }) .inner }) .response; diff --git a/crates/components/src/lib.rs b/crates/components/src/lib.rs index 1c69b545..42903467 100644 --- a/crates/components/src/lib.rs +++ b/crates/components/src/lib.rs @@ -44,6 +44,9 @@ pub use filesystem_view::FileSystemView; mod id_vec; pub use id_vec::{IdVecPlusMinusSelection, IdVecSelection}; +mod ui_ext; +pub use ui_ext::UiExt; + pub struct EnumMenuButton<'e, T> { current_value: &'e mut T, id: egui::Id, @@ -145,11 +148,7 @@ where .selected_text(self.reference.to_string()) .show_ui(ui, |ui| { for (i, variant) in T::iter().enumerate() { - let mut frame = egui::Frame::none(); - if i % 2 != 0 { - frame = frame.fill(ui.visuals().faint_bg_color); - } - frame.show(ui, |ui| { + ui.with_stripe(i % 2 != 0, |ui| { if ui .selectable_label( std::mem::discriminant(self.reference) @@ -255,13 +254,7 @@ where continue; } - let mut frame = egui::Frame::none(); - if is_faint { - frame = frame.fill(ui.visuals().faint_bg_color); - } - is_faint = !is_faint; - - frame.show(ui, |ui| { + ui.with_stripe(is_faint, |ui| { if ui .selectable_label(*self.reference == Some(id), formatted) .clicked() @@ -270,6 +263,7 @@ where changed = true; } }); + is_faint = !is_faint; } }); diff --git a/crates/components/src/ui_ext.rs b/crates/components/src/ui_ext.rs new file mode 100644 index 00000000..6d852913 --- /dev/null +++ b/crates/components/src/ui_ext.rs @@ -0,0 +1,118 @@ +// Copyright (C) 2023 Lily Lyons +// +// This file is part of Luminol. +// +// Luminol is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Luminol is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Luminol. If not, see . +// +// Additional permission under GNU GPL version 3 section 7 +// +// If you modify this Program, or any covered work, by linking or combining +// it with Steamworks API by Valve Corporation, containing parts covered by +// terms of the Steamworks API by Valve Corporation, the licensors of this +// Program grant you additional permission to convey the resulting work. + +use egui::InnerResponse; + +/// An extension trait that contains some helper methods for `egui::Ui`. +pub trait UiExt { + /// Determines the width of text in points when displayed with a specific font. + fn text_width( + &self, + text: impl Into, + font: impl Into, + ) -> f32; + + /// Displays widgets with cross justify, i.e. widgets will expand horizontally to take up all + /// available space in vertical layouts and widgets will expand vertically to take up all + /// available space in horizontal layouts. + fn with_cross_justify(&mut self, f: impl FnOnce(&mut Self) -> R) -> InnerResponse; + + /// This is the same as `.with_cross_justify` except it also centers widgets. + fn with_cross_justify_center(&mut self, f: impl FnOnce(&mut Self) -> R) -> InnerResponse; + + /// Displays contents inside a container with spacing on the left side. + fn with_left_margin(&mut self, m: f32, f: impl FnOnce(&mut Self) -> R) -> InnerResponse; + + /// Displays contents inside a container with spacing on the right side. + fn with_right_margin(&mut self, m: f32, f: impl FnOnce(&mut Self) -> R) -> InnerResponse; + + /// Displays contents with a normal or faint background (useful for tables with striped rows). + fn with_stripe(&mut self, faint: bool, f: impl FnOnce(&mut Self) -> R) -> InnerResponse; +} + +impl UiExt for egui::Ui { + fn text_width( + &self, + text: impl Into, + font: impl Into, + ) -> f32 { + Into::::into(text) + .into_galley(self, None, f32::INFINITY, font) + .galley + .rect + .width() + } + + fn with_cross_justify(&mut self, f: impl FnOnce(&mut Self) -> R) -> egui::InnerResponse { + self.with_layout( + egui::Layout { + cross_justify: true, + ..*self.layout() + }, + f, + ) + } + + fn with_cross_justify_center( + &mut self, + f: impl FnOnce(&mut Self) -> R, + ) -> egui::InnerResponse { + self.with_layout( + egui::Layout { + cross_justify: true, + cross_align: egui::Align::Center, + ..*self.layout() + }, + f, + ) + } + + fn with_left_margin(&mut self, m: f32, f: impl FnOnce(&mut Self) -> R) -> InnerResponse { + egui::Frame::none() + .outer_margin(egui::Margin { + left: m, + ..egui::Margin::ZERO + }) + .show(self, f) + } + + fn with_right_margin(&mut self, m: f32, f: impl FnOnce(&mut Self) -> R) -> InnerResponse { + egui::Frame::none() + .outer_margin(egui::Margin { + right: m, + ..egui::Margin::ZERO + }) + .show(self, f) + } + + fn with_stripe(&mut self, faint: bool, f: impl FnOnce(&mut Self) -> R) -> InnerResponse { + let frame = egui::containers::Frame::none(); + if faint { + frame.fill(self.visuals().faint_bg_color) + } else { + frame + } + .show(self, f) + } +} diff --git a/crates/ui/src/windows/archive_manager.rs b/crates/ui/src/windows/archive_manager.rs index 9b852a70..26b230e5 100644 --- a/crates/ui/src/windows/archive_manager.rs +++ b/crates/ui/src/windows/archive_manager.rs @@ -22,6 +22,7 @@ // terms of the Steamworks API by Valve Corporation, the licensors of this // Program grant you additional permission to convey the resulting work. +use luminol_components::UiExt; use luminol_filesystem::{File, FileSystem, OpenFlags}; static CREATE_DEFAULT_SELECTED_DIRS: once_cell::sync::Lazy< @@ -196,43 +197,30 @@ impl luminol_core::Window for Window { self.show_inner(ui, update_state); - ui.with_layout( - egui::Layout { - cross_justify: true, - ..Default::default() - }, - |ui| { - ui.group(|ui| { - ui.set_width(ui.available_width()); - ui.set_height(ui.available_height()); - egui::ScrollArea::both().show(ui, |ui| match &mut self.mode { - Mode::Extract { view, .. } => { - if let Some(v) = view { - v.ui(ui, update_state, None); - } else { - ui.add( - egui::Label::new("No archive chosen").wrap(false), - ); - } + ui.with_cross_justify(|ui| { + ui.group(|ui| { + ui.set_width(ui.available_width()); + ui.set_height(ui.available_height()); + egui::ScrollArea::both().show(ui, |ui| match &mut self.mode { + Mode::Extract { view, .. } => { + if let Some(v) = view { + v.ui(ui, update_state, None); + } else { + ui.add(egui::Label::new("No archive chosen").wrap(false)); } - Mode::Create { view, .. } => { - if let Some(v) = view { - v.ui( - ui, - update_state, - Some(&CREATE_DEFAULT_SELECTED_DIRS), - ); - } else { - ui.add( - egui::Label::new("No source folder chosen") - .wrap(false), - ); - } + } + Mode::Create { view, .. } => { + if let Some(v) = view { + v.ui(ui, update_state, Some(&CREATE_DEFAULT_SELECTED_DIRS)); + } else { + ui.add( + egui::Label::new("No source folder chosen").wrap(false), + ); } - }); + } }); - }, - ); + }); + }); }); }); @@ -294,12 +282,7 @@ impl Window { || save_promise.is_none() { ui.columns(2, |columns| { - columns[0].with_layout( - egui::Layout { - cross_align: egui::Align::Center, - cross_justify: true, - ..Default::default() - }, + columns[0].with_cross_justify_center( |ui| { if load_promise.is_none() && ui.button("Choose archive").clicked() { *load_promise = Some(luminol_core::spawn_future( @@ -314,12 +297,7 @@ impl Window { }, ); - columns[1].with_layout( - egui::Layout { - cross_align: egui::Align::Center, - cross_justify: true, - ..Default::default() - }, + columns[1].with_cross_justify_center( |ui| { if save_promise.is_none() && ui @@ -449,12 +427,7 @@ impl Window { || save_promise.is_none() { ui.columns(2, |columns| { - columns[0].with_layout( - egui::Layout { - cross_align: egui::Align::Center, - cross_justify: true, - ..Default::default() - }, + columns[0].with_cross_justify_center( |ui| { if load_promise.is_none() && ui.button("Choose source folder").clicked() { @@ -467,12 +440,7 @@ impl Window { }, ); - columns[1].with_layout( - egui::Layout { - cross_align: egui::Align::Center, - cross_justify: true, - ..Default::default() - }, + columns[1].with_cross_justify_center( |ui| { if save_promise.is_none() && ui diff --git a/crates/ui/src/windows/items.rs b/crates/ui/src/windows/items.rs index f0bbc86d..34c73eec 100644 --- a/crates/ui/src/windows/items.rs +++ b/crates/ui/src/windows/items.rs @@ -22,6 +22,8 @@ // terms of the Steamworks API by Valve Corporation, the licensors of this // Program grant you additional permission to convey the resulting work. +use luminol_components::UiExt; + /// Database - Items management window. #[derive(Default)] pub struct Window { @@ -92,468 +94,374 @@ impl luminol_core::Window for Window { + 2. * ui.spacing().button_padding.y, ); let button_width = ui.spacing().interact_size.x.max( - egui::WidgetText::from(change_maximum_text) - .into_galley(ui, None, f32::INFINITY, egui::TextStyle::Button) - .galley - .rect - .width() + ui.text_width(change_maximum_text, egui::TextStyle::Button) + 2. * ui.spacing().button_padding.x, ); egui::SidePanel::left(egui::Id::new("item_edit_sidepanel")).show_inside(ui, |ui| { - egui::Frame::none() - .outer_margin(egui::Margin { - right: ui.spacing().window_margin.right, - ..egui::Margin::ZERO - }) - .show(ui, |ui| { - ui.with_layout( - egui::Layout { - cross_justify: true, - ..Default::default() - }, - |ui| { - ui.label("Items"); - egui::ScrollArea::both() - .min_scrolled_width( - button_width + ui.spacing().item_spacing.x, - ) - .max_height( - ui.available_height() - - button_height - - ui.spacing().item_spacing.y, - ) - .show_rows( - ui, - button_height, - items.data.len(), - |ui, rows| { - ui.set_width(ui.available_width()); - - let offset = rows.start; - for (id, item) in - items.data[rows].iter_mut().enumerate() - { - let id = id + offset; - let mut frame = egui::containers::Frame::none(); - if id % 2 != 0 { - frame = - frame.fill(ui.visuals().faint_bg_color); - } - - frame.show(ui, |ui| { - ui.style_mut().wrap = Some(false); - - let response = ui - .selectable_value( - &mut self.selected_item, - id, - format!( - "{:0>3}: {}", - id, item.name - ), - ) - .interact(egui::Sense::click()); - - if response.clicked() { - response.request_focus(); - } - - // Reset this item if delete or backspace - // is pressed while this item is focused - if response.has_focus() - && ui.input(|i| { - i.key_down(egui::Key::Delete) - || i.key_down( - egui::Key::Backspace, - ) - }) - { - *item = Default::default(); - modified = true; - } - }); - } - }, - ); + ui.with_right_margin(ui.spacing().window_margin.right, |ui| { + ui.with_cross_justify(|ui| { + ui.label("Items"); + egui::ScrollArea::both() + .min_scrolled_width(button_width + ui.spacing().item_spacing.x) + .max_height( + ui.available_height() + - button_height + - ui.spacing().item_spacing.y, + ) + .show_rows(ui, button_height, items.data.len(), |ui, rows| { + ui.set_width(ui.available_width()); - if ui - .add(egui::Button::new(change_maximum_text).wrap(false)) - .clicked() - { - luminol_core::basic!( - update_state.toasts, - "`Change maximum...` button trigger" - ); + let offset = rows.start; + for (id, item) in items.data[rows].iter_mut().enumerate() { + let id = id + offset; + + ui.with_stripe(id % 2 != 0, |ui| { + ui.style_mut().wrap = Some(false); + + let response = ui + .selectable_value( + &mut self.selected_item, + id, + format!("{:0>3}: {}", id, item.name), + ) + .interact(egui::Sense::click()); + + if response.clicked() { + response.request_focus(); + } + + // Reset this item if delete or backspace + // is pressed while this item is focused + if response.has_focus() + && ui.input(|i| { + i.key_down(egui::Key::Delete) + || i.key_down(egui::Key::Backspace) + }) + { + *item = Default::default(); + modified = true; + } + }); } - }, - ); + }); + + if ui + .add(egui::Button::new(change_maximum_text).wrap(false)) + .clicked() + { + luminol_core::basic!( + update_state.toasts, + "`Change maximum...` button trigger" + ); + } }); + }); }); - egui::Frame::none() - .outer_margin(egui::Margin { - left: ui.spacing().window_margin.left, - ..egui::Margin::ZERO - }) - .show(ui, |ui| { - ui.with_layout( - egui::Layout { - cross_justify: true, - ..Default::default() - }, - |ui| { - egui::ScrollArea::vertical().show(ui, |ui| { - ui.set_width(ui.available_width()); - ui.set_min_width( - 2. * (ui.spacing().slider_width - + ui.spacing().interact_size.x) - + 3. * ui.spacing().item_spacing.x, - ); + ui.with_left_margin(ui.spacing().window_margin.left, |ui| { + ui.with_cross_justify(|ui| { + egui::ScrollArea::vertical().show(ui, |ui| { + ui.set_width(ui.available_width()); + ui.set_min_width( + 2. * (ui.spacing().slider_width + ui.spacing().interact_size.x) + + 3. * ui.spacing().item_spacing.x, + ); - let Some(selected_item) = - items.data.get_mut(self.selected_item) - else { - return; - }; + let Some(selected_item) = items.data.get_mut(self.selected_item) else { + return; + }; + + modified |= ui + .add(luminol_components::Field::new( + "Name", + egui::TextEdit::singleline(&mut selected_item.name) + .desired_width(f32::INFINITY), + )) + .changed(); + + modified |= ui + .add(luminol_components::Field::new( + "Description", + egui::TextEdit::multiline(&mut selected_item.description) + .desired_width(f32::INFINITY), + )) + .changed(); + + ui.with_stripe(true, |ui| { + ui.columns(2, |columns| { + modified |= columns[0] + .add(luminol_components::Field::new( + "Scope", + luminol_components::EnumComboBox::new( + (selected_item.id, "scope"), + &mut selected_item.scope, + ), + )) + .changed(); - modified |= ui + modified |= columns[1] .add(luminol_components::Field::new( - "Name", - egui::TextEdit::singleline(&mut selected_item.name) - .desired_width(f32::INFINITY), + "Occasion", + luminol_components::EnumComboBox::new( + (selected_item.id, "occasion"), + &mut selected_item.occasion, + ), )) .changed(); + }); + }); - modified |= ui + ui.with_stripe(false, |ui| { + ui.columns(2, |columns| { + modified |= columns[0] .add(luminol_components::Field::new( - "Description", - egui::TextEdit::multiline( - &mut selected_item.description, - ) - .desired_width(f32::INFINITY), + "User Animation", + luminol_components::OptionalIdComboBox::new( + (selected_item.id, "animation1_id"), + &mut selected_item.animation1_id, + animations.data.len(), + |id| { + animations.data.get(id).map_or_else( + || "".into(), + |a| format!("{id:0>3}: {}", a.name), + ) + }, + ), )) .changed(); - egui::Frame::none().fill(ui.visuals().faint_bg_color).show( - ui, - |ui| { - ui.columns(2, |columns| { - modified |= columns[0] - .add(luminol_components::Field::new( - "Scope", - luminol_components::EnumComboBox::new( - (selected_item.id, "scope"), - &mut selected_item.scope, - ), - )) - .changed(); - - modified |= columns[1] - .add(luminol_components::Field::new( - "Occasion", - luminol_components::EnumComboBox::new( - (selected_item.id, "occasion"), - &mut selected_item.occasion, - ), - )) - .changed(); - }); - }, - ); + modified |= columns[1] + .add(luminol_components::Field::new( + "Target Animation", + luminol_components::OptionalIdComboBox::new( + (selected_item.id, "animation2_id"), + &mut selected_item.animation2_id, + animations.data.len(), + |id| { + animations.data.get(id).map_or_else( + || "".into(), + |a| format!("{id:0>3}: {}", a.name), + ) + }, + ), + )) + .changed(); + }); + }); - egui::Frame::none().show(ui, |ui| { - ui.columns(2, |columns| { - modified |= columns[0] - .add(luminol_components::Field::new( - "User Animation", - luminol_components::OptionalIdComboBox::new( - (selected_item.id, "animation1_id"), - &mut selected_item.animation1_id, - animations.data.len(), - |id| { - animations.data.get(id).map_or_else( - || "".into(), - |a| format!("{id:0>3}: {}", a.name), - ) - }, - ), - )) - .changed(); - - modified |= columns[1] - .add(luminol_components::Field::new( - "Target Animation", - luminol_components::OptionalIdComboBox::new( - (selected_item.id, "animation2_id"), - &mut selected_item.animation2_id, - animations.data.len(), - |id| { - animations.data.get(id).map_or_else( - || "".into(), - |a| format!("{id:0>3}: {}", a.name), - ) - }, - ), - )) - .changed(); - }); - }); - - egui::Frame::none().fill(ui.visuals().faint_bg_color).show( - ui, - |ui| { - ui.columns(2, |columns| { - modified |= columns[0] - .add(luminol_components::Field::new( - "Menu Use SE", - egui::Label::new("TODO"), - )) - .changed(); - - modified |= columns[1] - .add(luminol_components::Field::new( - "Common Event", - luminol_components::OptionalIdComboBox::new( - (selected_item.id, "common_event_id"), - &mut selected_item.common_event_id, - common_events.data.len(), - |id| { - common_events - .data - .get(id) - .map_or_else( - || "".into(), - |e| { - format!( - "{id:0>3}: {}", - e.name - ) - }, - ) - }, - ), - )) - .changed(); - }); - }, - ); + ui.with_stripe(true, |ui| { + ui.columns(2, |columns| { + modified |= columns[0] + .add(luminol_components::Field::new( + "Menu Use SE", + egui::Label::new("TODO"), + )) + .changed(); - egui::Frame::none().show(ui, |ui| { - ui.columns(2, |columns| { - modified |= columns[0] - .add(luminol_components::Field::new( - "Price", - egui::DragValue::new(&mut selected_item.price) - .clamp_range(0..=i32::MAX), - )) - .changed(); - - modified |= columns[1] - .add(luminol_components::Field::new( - "Consumable", - egui::Checkbox::without_text( - &mut selected_item.consumable, - ), - )) - .changed(); - }); - }); - - egui::Frame::none().fill(ui.visuals().faint_bg_color).show( - ui, - |ui| { - ui.columns(2, |columns| { - modified |= columns[0] - .add(luminol_components::Field::new( - "Parameter", - luminol_components::EnumComboBox::new( - "parameter_type", - &mut selected_item.parameter_type, - ), - )) - .changed(); - - modified |= columns[1] - .add_enabled( - !matches!( - selected_item.parameter_type, - luminol_data::rpg::item::ParameterType::None - ), - luminol_components::Field::new( - "Parameter Increment", - egui::DragValue::new( - &mut selected_item.parameter_points, - ) - .clamp_range(0..=i32::MAX), - ), + modified |= columns[1] + .add(luminol_components::Field::new( + "Common Event", + luminol_components::OptionalIdComboBox::new( + (selected_item.id, "common_event_id"), + &mut selected_item.common_event_id, + common_events.data.len(), + |id| { + common_events.data.get(id).map_or_else( + || "".into(), + |e| format!("{id:0>3}: {}", e.name), ) - .changed(); - }); - }, - ); + }, + ), + )) + .changed(); + }); + }); - egui::Frame::none().show(ui, |ui| { - ui.columns(2, |columns| { - modified |= columns[0] - .add(luminol_components::Field::new( - "Recover HP Rate", - egui::Slider::new( - &mut selected_item.recover_hp_rate, - 0..=100, - ) - .suffix("%"), - )) - .changed(); - - modified |= columns[1] - .add(luminol_components::Field::new( - "Recover HP", - egui::DragValue::new( - &mut selected_item.recover_hp, - ) - .clamp_range(0..=i32::MAX), - )) - .changed(); - }); - }); - - egui::Frame::none().fill(ui.visuals().faint_bg_color).show( - ui, - |ui| { - ui.columns(2, |columns| { - modified |= columns[0] - .add(luminol_components::Field::new( - "Recover SP Rate", - egui::Slider::new( - &mut selected_item.recover_sp_rate, - 0..=100, - ) - .suffix("%"), - )) - .changed(); - - modified |= columns[1] - .add(luminol_components::Field::new( - "Recover SP", - egui::DragValue::new( - &mut selected_item.recover_sp, - ) - .clamp_range(0..=i32::MAX), - )) - .changed(); - }); - }, - ); + ui.with_stripe(false, |ui| { + ui.columns(2, |columns| { + modified |= columns[0] + .add(luminol_components::Field::new( + "Price", + egui::DragValue::new(&mut selected_item.price) + .clamp_range(0..=i32::MAX), + )) + .changed(); - egui::Frame::none().show(ui, |ui| { - ui.columns(2, |columns| { - modified |= columns[0] - .add(luminol_components::Field::new( - "Hit Rate", - egui::Slider::new( - &mut selected_item.hit, - 0..=100, - ) - .suffix("%"), - )) - .changed(); - - modified |= columns[1] - .add(luminol_components::Field::new( - "Variance", - egui::Slider::new( - &mut selected_item.variance, - 0..=100, - ) - .suffix("%"), - )) - .changed(); - }); - }); - - egui::Frame::none().fill(ui.visuals().faint_bg_color).show( - ui, - |ui| { - ui.columns(2, |columns| { - modified |= columns[0] - .add(luminol_components::Field::new( - "PDEF-F", - egui::Slider::new( - &mut selected_item.pdef_f, - 0..=100, - ) - .suffix("%"), - )) - .changed(); - - modified |= columns[1] - .add(luminol_components::Field::new( - "MDEF-F", - egui::Slider::new( - &mut selected_item.mdef_f, - 0..=100, - ) - .suffix("%"), - )) - .changed(); - }); + modified |= columns[1] + .add(luminol_components::Field::new( + "Consumable", + egui::Checkbox::without_text( + &mut selected_item.consumable, + ), + )) + .changed(); + }); + }); + + ui.with_stripe(true, |ui| { + ui.columns(2, |columns| { + modified |= columns[0] + .add(luminol_components::Field::new( + "Parameter", + luminol_components::EnumComboBox::new( + "parameter_type", + &mut selected_item.parameter_type, + ), + )) + .changed(); + + modified |= columns[1] + .add_enabled( + !matches!( + selected_item.parameter_type, + luminol_data::rpg::item::ParameterType::None + ), + luminol_components::Field::new( + "Parameter Increment", + egui::DragValue::new( + &mut selected_item.parameter_points, + ) + .clamp_range(0..=i32::MAX), + ), + ) + .changed(); + }); + }); + + ui.with_stripe(false, |ui| { + ui.columns(2, |columns| { + modified |= columns[0] + .add(luminol_components::Field::new( + "Recover HP Rate", + egui::Slider::new( + &mut selected_item.recover_hp_rate, + 0..=100, + ) + .suffix("%"), + )) + .changed(); + + modified |= columns[1] + .add(luminol_components::Field::new( + "Recover HP", + egui::DragValue::new(&mut selected_item.recover_hp) + .clamp_range(0..=i32::MAX), + )) + .changed(); + }); + }); + + ui.with_stripe(true, |ui| { + ui.columns(2, |columns| { + modified |= columns[0] + .add(luminol_components::Field::new( + "Recover SP Rate", + egui::Slider::new( + &mut selected_item.recover_sp_rate, + 0..=100, + ) + .suffix("%"), + )) + .changed(); + + modified |= columns[1] + .add(luminol_components::Field::new( + "Recover SP", + egui::DragValue::new(&mut selected_item.recover_sp) + .clamp_range(0..=i32::MAX), + )) + .changed(); + }); + }); + + ui.with_stripe(false, |ui| { + ui.columns(2, |columns| { + modified |= columns[0] + .add(luminol_components::Field::new( + "Hit Rate", + egui::Slider::new(&mut selected_item.hit, 0..=100) + .suffix("%"), + )) + .changed(); + + modified |= columns[1] + .add(luminol_components::Field::new( + "Variance", + egui::Slider::new(&mut selected_item.variance, 0..=100) + .suffix("%"), + )) + .changed(); + }); + }); + + ui.with_stripe(true, |ui| { + ui.columns(2, |columns| { + modified |= columns[0] + .add(luminol_components::Field::new( + "PDEF-F", + egui::Slider::new(&mut selected_item.pdef_f, 0..=100) + .suffix("%"), + )) + .changed(); + + modified |= columns[1] + .add(luminol_components::Field::new( + "MDEF-F", + egui::Slider::new(&mut selected_item.mdef_f, 0..=100) + .suffix("%"), + )) + .changed(); + }); + }); + + ui.with_stripe(false, |ui| { + ui.columns(2, |columns| { + let mut selection = luminol_components::IdVecSelection::new( + (selected_item.id, "element_set"), + &mut selected_item.element_set, + system.elements.len(), + |id| { + system.elements.get(id).map_or_else( + || "".into(), + |e| format!("{id:0>3}: {}", e), + ) }, ); + if self.previous_selected_item != Some(selected_item.id) { + selection.clear_search(); + } + modified |= columns[0] + .add(luminol_components::Field::new("Elements", selection)) + .changed(); - egui::Frame::none().show(ui, |ui| { - ui.columns(2, |columns| { - let mut selection = - luminol_components::IdVecSelection::new( - (selected_item.id, "element_set"), - &mut selected_item.element_set, - system.elements.len(), - |id| { - system.elements.get(id).map_or_else( - || "".into(), - |e| format!("{id:0>3}: {}", e), - ) - }, - ); - if self.previous_selected_item != Some(selected_item.id) - { - selection.clear_search(); - } - modified |= columns[0] - .add(luminol_components::Field::new( - "Elements", selection, - )) - .changed(); - - let mut selection = - luminol_components::IdVecPlusMinusSelection::new( - (selected_item.id, "state_set"), - &mut selected_item.plus_state_set, - &mut selected_item.minus_state_set, - states.data.len(), - |id| { - states.data.get(id).map_or_else( - || "".into(), - |s| format!("{id:0>3}: {}", s.name), - ) - }, - ); - if self.previous_selected_item != Some(selected_item.id) - { - selection.clear_search(); - } - modified |= columns[1] - .add(luminol_components::Field::new( - "State Change", - selection, - )) - .changed(); - }); - }); + let mut selection = + luminol_components::IdVecPlusMinusSelection::new( + (selected_item.id, "state_set"), + &mut selected_item.plus_state_set, + &mut selected_item.minus_state_set, + states.data.len(), + |id| { + states.data.get(id).map_or_else( + || "".into(), + |s| format!("{id:0>3}: {}", s.name), + ) + }, + ); + if self.previous_selected_item != Some(selected_item.id) { + selection.clear_search(); + } + modified |= columns[1] + .add(luminol_components::Field::new( + "State Change", + selection, + )) + .changed(); }); - }, - ); + }); + }); }); + }); }); self.previous_selected_item =