add map opening (sort of)

gui-branch
an 2019-03-31 14:58:36 -04:00
parent 2875da07e1
commit 0e32e7893a
5 changed files with 133 additions and 43 deletions

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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]]

View File

@ -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.