//! Map editor interface. use super::glib::*; use gobject_sys::*; use gtk_sys::*; use maraiah::{durandal::ffi, marathon::map, rozinante::editor}; // Iterates over each flag widget with its respective ordered flag object. fn each_flag(buttons: &[PropFlag], ordering: &[T], mut f: F) where F: FnMut(&PropFlag, &T) { for (flg, ord) in buttons.iter().zip(ordering) { f(flg, ord); } } // Creates a closure which sets if a button is toggled. fn r_flg(mut f: F) -> impl FnMut(&PropFlag, &T) where F: FnMut(&T) -> bool { move |flg, ord| { unsafe { // we have to block the signal handler so that it doesn't cause an // infinite recursion of updating g_signal_handler_block(*flg.w as _, flg.h); gtk_toggle_button_set_active(*flg.w, f(ord).into()); g_signal_handler_unblock(*flg.w as _, flg.h); } } } // Creates a closure which checks if a button is toggled. fn s_flg(mut f: F) -> impl FnMut(&PropFlag, &T) where F: FnMut(&T, bool) { move |flg, ord| f(ord, unsafe {gtk_toggle_button_get_active(*flg.w)} != 0) } impl MapEditor { /// Propagates all updated map information to widgets. pub fn cause_refresh(&self) { self.cause_refresh_view(); self.cause_refresh_props(); } /// Propagates updated map view information to widgets. pub fn cause_refresh_view(&self) { unsafe { gtk_widget_queue_draw(*self.draw); } } /// Propagates updated map property information to widgets. pub fn cause_refresh_props(&self) { let inf = &self.cur_block().info; each_flag(&self.fent, &O_ENT, r_flg(|&f| inf.entr_flags.contains(f))); each_flag(&self.fenv, &O_ENV, r_flg(|&f| inf.envi_flags.contains(f))); each_flag(&self.fmsn, &O_MSN, r_flg(|&f| inf.miss_flags.contains(f))); } /// Propagates updated map property information to the editor state. pub fn cause_update_props(&mut self) { let mut blk = self.cur_block().clone(); let inf = &mut blk.info; each_flag(&self.fent, &O_ENT, s_flg(|&f, s| inf.entr_flags.set(f, s))); each_flag(&self.fenv, &O_ENV, s_flg(|&f, s| inf.envi_flags.set(f, s))); each_flag(&self.fmsn, &O_MSN, s_flg(|&f, s| inf.miss_flags.set(f, s))); self.push_block(blk); self.cause_refresh_view(); } } impl std::ops::Deref for MapEditor { type Target = editor::MapEditor; #[inline] fn deref(&self) -> &Self::Target {&self.edit} } impl std::ops::DerefMut for MapEditor { #[inline] fn deref_mut(&mut self) -> &mut Self::Target {&mut self.edit} } /// Specialized map editor which has callbacks for frontend purposes. pub struct MapEditor { pub edit: editor::MapEditor, pub draw: Refc<'static, GtkWidget>, pub fent: Vec, pub fenv: Vec, pub fmsn: Vec, } /// A runtime reference to the map editor. pub type MapEditorRef = std::cell::RefCell; pub struct PropFlag { pub w: Refc<'static, GtkToggleButton>, pub h: ffi::c_ulong, } // NOTE: this is flipped because of GTK weirdness. don't touch const O_ENT: [map::minf::EntFlags; 8] = [ map::minf::EntFlags::CTF, map::minf::EntFlags::RUGBY, map::minf::EntFlags::DEFENSE, map::minf::EntFlags::KOTH, map::minf::EntFlags::KTMWTB, map::minf::EntFlags::CARNAGE, map::minf::EntFlags::CO_OP, map::minf::EntFlags::SOLO, ]; const O_ENV: [map::minf::EnvFlags; 11] = [ map::minf::EnvFlags::VACUUM, map::minf::EnvFlags::MAGNETIC, map::minf::EnvFlags::REBELLION, map::minf::EnvFlags::LOW_GRAV, map::minf::EnvFlags::M1_GLUE, map::minf::EnvFlags::LAVA_FLOOR, map::minf::EnvFlags::REBELLION2, map::minf::EnvFlags::MUSIC, map::minf::EnvFlags::TERM_PAUSE, map::minf::EnvFlags::M1_MONSTER, map::minf::EnvFlags::M1_WEPS, ]; const O_MSN: [map::minf::MsnFlags; 8] = [ map::minf::MsnFlags::EXTERMINATION, map::minf::MsnFlags::EXPLORATION, map::minf::MsnFlags::RETRIEVAL, map::minf::MsnFlags::REPAIR, map::minf::MsnFlags::RESCUE, map::minf::MsnFlags::M1_EXPLORATION, map::minf::MsnFlags::M1_RESCUE, map::minf::MsnFlags::M1_REPAIR, ]; // EOF