add map opening (sort of)
parent
2875da07e1
commit
0e32e7893a
|
@ -4,8 +4,8 @@
|
|||
//! and human interactions with it, but is otherwise not permitted to directly
|
||||
//! edit it.
|
||||
|
||||
pub mod block;
|
||||
pub mod state;
|
||||
mod block;
|
||||
mod state;
|
||||
|
||||
use crate::durandal::image::*;
|
||||
use super::{color, draw::*};
|
||||
|
@ -16,7 +16,13 @@ impl MapEditor
|
|||
#[inline]
|
||||
pub fn open_new(&mut self)
|
||||
{
|
||||
*self = MapEditor::Opened(state::OpenMap::default());
|
||||
self.map = Some(state::OpenMap::default());
|
||||
}
|
||||
|
||||
/// Opens the editor with an existing map.
|
||||
pub fn open_buf(&mut self, b: &[u8])
|
||||
{
|
||||
self.map = Some(state::OpenMap::open_buf(b));
|
||||
}
|
||||
|
||||
/// Draws the screen for this editor state.
|
||||
|
@ -29,8 +35,8 @@ impl MapEditor
|
|||
let iw = im.w();
|
||||
let ih = im.h();
|
||||
|
||||
match self {
|
||||
MapEditor::Closed => {
|
||||
match &self.map {
|
||||
None => {
|
||||
let tx_top = "Map Required To Proceed";
|
||||
let tx_bot = "CAS.qterm//CyberAcme Systems Inc.";
|
||||
|
||||
|
@ -44,8 +50,8 @@ impl MapEditor
|
|||
d.rect(Rect{x: 0, y: dh - 18, w: dw, h: 18}, color::DARK_RED);
|
||||
d.text((4, dh - 4), tx_bot, color::RED);
|
||||
}
|
||||
MapEditor::Opened(st) => {
|
||||
let text = &format!("tool: {:?}", st.tool);
|
||||
Some(st) => {
|
||||
let text = &format!("tool: {:?}", st.tool());
|
||||
|
||||
d.clear(Color16::new(0, 0, 0));
|
||||
d.text((dw/2, dh/2), text, color::RED);
|
||||
|
@ -57,34 +63,28 @@ impl MapEditor
|
|||
#[inline]
|
||||
pub fn is_closed(&self) -> bool
|
||||
{
|
||||
match self {
|
||||
MapEditor::Closed => true,
|
||||
MapEditor::Opened(_) => false,
|
||||
}
|
||||
self.map.is_none()
|
||||
}
|
||||
|
||||
/// Returns `true` if `self` is opened.
|
||||
#[inline]
|
||||
pub fn is_opened(&self) -> bool
|
||||
{
|
||||
match self {
|
||||
MapEditor::Closed => false,
|
||||
MapEditor::Opened(_) => true,
|
||||
}
|
||||
self.map.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MapEditor
|
||||
{
|
||||
#[inline]
|
||||
fn default() -> Self {MapEditor::Closed}
|
||||
fn default() -> Self {Self{map: None}}
|
||||
}
|
||||
|
||||
/// An entire map editor, which may be opened or closed.
|
||||
pub enum MapEditor
|
||||
/// An entire map editor, which may be opened or closed. Holds state which
|
||||
/// outlives the opened map state.
|
||||
pub struct MapEditor
|
||||
{
|
||||
Closed,
|
||||
Opened(state::OpenMap),
|
||||
map: Option<state::OpenMap>,
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
|
|
@ -10,10 +10,10 @@ impl Default for Block
|
|||
}
|
||||
|
||||
/// Copyable, versioned map state.
|
||||
#[derive(Clone)]
|
||||
pub struct Block
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) struct Block
|
||||
{
|
||||
info: map::Minf,
|
||||
pub(super) info: map::Minf,
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
|
|
@ -1,18 +1,56 @@
|
|||
//! Map editor state.
|
||||
|
||||
use super::block;
|
||||
use crate::marathon::{machdr, wad};
|
||||
|
||||
impl OpenMap
|
||||
{
|
||||
fn cur_block(&self) -> &block::Block
|
||||
pub(super) fn open_buf(b: &[u8]) -> Self
|
||||
{
|
||||
// TODO: handle errors gracefully
|
||||
let b = &b[machdr::try_mac_header(b)..];
|
||||
let wad = wad::read_wad(b).unwrap();
|
||||
|
||||
let ent = wad.entries.iter().nth(0).unwrap().1;
|
||||
let info = ent.chunks.iter().find_map(|cnk| {
|
||||
match cnk {
|
||||
wad::Chunk::Minf(info) => Some(info),
|
||||
_ => None,
|
||||
}
|
||||
}).unwrap().clone();
|
||||
|
||||
let block = block::Block{info};
|
||||
|
||||
dbg!(&block);
|
||||
|
||||
Self{blocks: vec![block],
|
||||
tools: Self::default_tools()}
|
||||
}
|
||||
|
||||
pub(super) fn cur_block(&self) -> &block::Block
|
||||
{
|
||||
self.blocks.last().unwrap()
|
||||
}
|
||||
|
||||
fn cur_block_mut(&mut self) -> &mut block::Block
|
||||
pub(super) fn cur_block_mut(&mut self) -> &mut block::Block
|
||||
{
|
||||
self.blocks.last_mut().unwrap()
|
||||
}
|
||||
|
||||
pub(super) fn tool(&self) -> &Tool
|
||||
{
|
||||
&self.tools.0
|
||||
}
|
||||
|
||||
pub(super) fn set_tool(&mut self, t: Tool) -> &Tool
|
||||
{
|
||||
self.tools.1 = self.tools.0.clone();
|
||||
self.tools.0 = t;
|
||||
|
||||
&self.tools.1
|
||||
}
|
||||
|
||||
const fn default_tools() -> (Tool, Tool) {(Tool::Points, Tool::Lines)}
|
||||
}
|
||||
|
||||
impl Default for OpenMap
|
||||
|
@ -21,25 +59,19 @@ impl Default for OpenMap
|
|||
fn default() -> Self
|
||||
{
|
||||
Self{blocks: vec![block::Block::default()],
|
||||
tool: Tool::default()}
|
||||
tools: Self::default_tools()}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Tool
|
||||
{
|
||||
#[inline]
|
||||
fn default() -> Self {Tool::Points}
|
||||
}
|
||||
|
||||
/// The state of an opened map editor.
|
||||
pub struct OpenMap
|
||||
pub(super) struct OpenMap
|
||||
{
|
||||
pub(super) blocks: Vec<block::Block>,
|
||||
pub(super) tool: Tool,
|
||||
blocks: Vec<block::Block>,
|
||||
tools: (Tool, Tool),
|
||||
}
|
||||
|
||||
/// A tool in the map editor.
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) enum Tool
|
||||
{
|
||||
Points,
|
||||
|
|
|
@ -8,8 +8,6 @@ edition = "2018"
|
|||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
maraiah = {path = "../.."}
|
||||
|
||||
atk-sys = "0.8"
|
||||
cairo-sys-rs = "0.8"
|
||||
gdk-pixbuf-sys = "0.8"
|
||||
|
@ -18,6 +16,8 @@ gio-sys = "0.8"
|
|||
glib-sys = "0.8"
|
||||
gobject-sys = "0.8"
|
||||
gtk-sys = {version = "0.8", features = ["v3_16"]}
|
||||
maraiah = {path = "../.."}
|
||||
memmap = "0.7"
|
||||
pango-sys = "0.8"
|
||||
|
||||
[[bin]]
|
||||
|
|
|
@ -231,7 +231,7 @@ unsafe fn setup_win_main(b: &Refc<GtkBuilder>,
|
|||
if edit.is_opened() {
|
||||
let titl = c_str!("Confirm");
|
||||
let text = c_str!("Are you sure you want to create a new project? \
|
||||
Data may be lost.");
|
||||
Unsaved data may be lost.");
|
||||
|
||||
if !run_ok_cancel_dlg(titl, text) {
|
||||
return;
|
||||
|
@ -242,6 +242,31 @@ unsafe fn setup_win_main(b: &Refc<GtkBuilder>,
|
|||
edit.cause_update();
|
||||
}
|
||||
|
||||
/// Callback to open an existing map when the "Open" button is pressed.
|
||||
unsafe extern "C" fn c_open_act(_: *mut GtkWidget, edit: gpointer)
|
||||
{
|
||||
let edit = &*(edit as *const MapEditorRef);
|
||||
let mut edit = edit.borrow_mut();
|
||||
|
||||
if edit.is_opened() {
|
||||
let titl = c_str!("Confirm");
|
||||
let text = c_str!("Are you sure you want to open this project? \
|
||||
Unsaved data may be lost.");
|
||||
|
||||
if !run_ok_cancel_dlg(titl, text) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(path) = run_file_chooser_open() {
|
||||
// TODO: handle errors gracefully
|
||||
let fp = std::fs::File::open(&path).unwrap();
|
||||
let mm = memmap::Mmap::map(&fp).unwrap();
|
||||
edit.open_buf(&mm);
|
||||
edit.cause_update();
|
||||
}
|
||||
}
|
||||
|
||||
// set up main window
|
||||
let win = get_obj::<GtkWindow>(b, c_str!("win-main"));
|
||||
|
||||
|
@ -256,6 +281,9 @@ unsafe fn setup_win_main(b: &Refc<GtkBuilder>,
|
|||
|
||||
let btn = get_obj::<GtkMenuItem>(b, c_str!("btn-new"));
|
||||
connect(btn, ACTIVATE, c_new_act as _, connect_ref(btn, edit.clone()));
|
||||
|
||||
let btn = get_obj::<GtkMenuItem>(b, c_str!("btn-open"));
|
||||
connect(btn, ACTIVATE, c_open_act as _, connect_ref(btn, edit.clone()));
|
||||
}
|
||||
|
||||
/// Sets up the CSS styling providers.
|
||||
|
@ -292,10 +320,40 @@ unsafe fn run_ok_cancel_dlg(title: ffi::NT, text: ffi::NT) -> bool
|
|||
|
||||
gtk_widget_destroy(dlg);
|
||||
|
||||
match res {
|
||||
GTK_RESPONSE_ACCEPT => true,
|
||||
_ => false,
|
||||
}
|
||||
res == GTK_RESPONSE_ACCEPT
|
||||
}
|
||||
|
||||
/// Runs a modal Open File dialogue.
|
||||
unsafe fn run_file_chooser_open() -> Option<String>
|
||||
{
|
||||
let action = GTK_FILE_CHOOSER_ACTION_OPEN;
|
||||
let dlg = gtk_file_chooser_dialog_new(c_str!("Open File"),
|
||||
ffi::null_mut(),
|
||||
action,
|
||||
c_str!("_Cancel"),
|
||||
GTK_RESPONSE_CANCEL,
|
||||
c_str!("_Open"),
|
||||
GTK_RESPONSE_ACCEPT,
|
||||
ffi::null_mut_void());
|
||||
|
||||
let res = gtk_dialog_run(dlg as _);
|
||||
|
||||
let ret = if res == GTK_RESPONSE_ACCEPT {
|
||||
let fna = gtk_file_chooser_get_filename(dlg as _);
|
||||
|
||||
let own = ffi::CStr::from_ptr(fna);
|
||||
let own = own.to_str().ok()?.to_owned();
|
||||
|
||||
g_free(fna as _);
|
||||
|
||||
Some(own)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
gtk_widget_destroy(dlg);
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
/// Connects a handler that hides a toplevel widget when deleted.
|
||||
|
|
Loading…
Reference in New Issue