Allow loading an object tree into the editor

This commit is contained in:
Tad Hardesty 2017-11-07 21:13:17 -08:00
parent 14154fd272
commit 46eb0dfafa

View file

@ -3,17 +3,135 @@
#[macro_use] extern crate qt_extras as qt;
#[macro_use] extern crate glium;
extern crate dreammaker as dm;
extern crate dmm_tools;
mod map_renderer;
use std::cell::RefCell;
use std::path::PathBuf;
use dm::objtree::{ObjectTree, TypeRef};
use qt::widgets;
use qt::widgets::application::Application;
use qt::widgets::file_dialog::FileDialog;
use qt::widgets::tree_widget::TreeWidget;
use qt::widgets::tree_widget_item::TreeWidgetItem;
use qt::core::connection::Signal;
use qt::core::slots::SlotNoArgs;
use qt::cpp_utils::static_cast_mut;
use qt::gui::key_sequence::KeySequence;
unsafe fn show_error(message: &str) {
qt::widgets::message_box::MessageBox::critical((
0 as *mut qt::widgets::widget::Widget,
qstr!("Error"),
qstr!(message),
qt::core::flags::Flags::from_enum(qt::widgets::message_box::StandardButton::Ok),
));
}
struct State {
environment_file: Option<PathBuf>,
objtree: Option<dm::objtree::ObjectTree>,
}
impl State {
fn new() -> State {
State {
environment_file: None,
objtree: None,
}
}
unsafe fn load_env(&mut self, path: String, widget: &mut TreeWidget) {
let path = PathBuf::from(path);
let mut preprocessor;
match dm::preprocessor::Preprocessor::new(path.clone()) {
Err(_) => return show_error(&format!("Could not open for reading:\n{}", path.display())),
Ok(pp) => preprocessor = pp,
};
let objtree;
match dm::parser::parse(dm::indents::IndentProcessor::new(&mut preprocessor)) {
Err(e) => {
let mut message = format!("\
Could not parse the environment:\n\
{}\n\n\
This may be caused by incorrect or unusual code, but is typically a parser bug. \
Change the code to use a more common form, or report the parsing problem.\n\
", path.display());
let mut message_buf = Vec::new();
let _ = dm::pretty_print_error(&mut message_buf, &preprocessor, &e);
message.push_str(&String::from_utf8_lossy(&message_buf[..]));
return show_error(&message);
},
Ok(t) => objtree = t,
}
self.environment_file = Some(path);
{
widget.clear();
let root = objtree.root();
for &root_child in ["area", "turf", "obj", "mob"].iter() {
let ty = root.child(root_child, &objtree).expect("builtins missing");
let mut root_item = TreeWidgetItem::new(());
root_item.set_text(0, qstr!(&ty.name));
add_children(&mut root_item, ty, &objtree);
widget.add_top_level_item(qt_own!(root_item));
}
}
self.objtree = Some(objtree);
}
}
unsafe fn add_children(parent: &mut TreeWidgetItem, ty: TypeRef, tree: &ObjectTree) {
let mut children = ty.children(tree);
children.sort_unstable_by(|a, b| a.name.cmp(&b.name));
for each in children {
let mut child = TreeWidgetItem::new(());
child.set_text(0, qstr!(&each.name));
add_children(&mut child, each, tree);
parent.add_child(qt_own!(child));
}
}
macro_rules! action {
(@[$it:ident] (tip = $text:expr)) => {
$it.set_status_tip(&qstr!($text));
};
(@[$it:ident] (key = $(^$m:ident)* $k:ident)) => {
$it.set_shortcut(&KeySequence::new( qt::core::qt::Key::$k as i32 $(+ qt::core::qt::Modifier::$m as i32)* ));
};
(@[$it:ident] (slot = $slot:expr)) => {
$it.signals().triggered().connect(&$slot);
};
(@[$it:ident] $closure:block) => {
let slot = SlotNoArgs::new(|| $closure);
$it.signals().triggered().connect(&slot);
};
($add_to:expr, $name:expr $(, $x:tt)*) => {
let it = &mut *$add_to.add_action(qstr!($name));
$(action!(@[it] $x);)*
}
}
#[allow(unused_mut)]
fn main() {
let mut state = RefCell::new(State::new());
Application::create_and_exit(|_app| unsafe {
let mut window = widgets::main_window::MainWindow::new();
let window_ptr = window.as_mut_ptr();
// object tree
let mut tree_view = widgets::tree_view::TreeView::new();
let mut tree_widget = widgets::tree_widget::TreeWidget::new();
let tree_widget_ptr = tree_widget.as_mut_ptr();
tree_widget.set_column_count(1);
tree_widget.set_header_hidden(true);
// minimap
let mut minimap_widget = qt::glium_widget::create(map_renderer::GliumTest);
@ -46,7 +164,7 @@ fn main() {
// root splitter
let mut splitter = widgets::splitter::Splitter::new(());
splitter.set_children_collapsible(false);
splitter.add_widget(qt_own!(tree_view));
splitter.add_widget(qt_own!(tree_widget));
splitter.add_widget(qt_own!(h_layout_widget));
splitter.set_stretch_factor(0, 0);
splitter.set_stretch_factor(1, 1);
@ -54,19 +172,29 @@ fn main() {
// menus
let mut menu_bar = widgets::menu_bar::MenuBar::new();
let mut menu_file = &mut *menu_bar.add_menu(qstr!("File"));
menu_file.add_action(qstr!("Open Environment"));
menu_file.add_action(qstr!("Recent Environments"));
action!(menu_file, "Open Environment", (tip = "Load a DME file."), {
let file = FileDialog::get_open_file_name_unsafe((
static_cast_mut(window_ptr),
qstr!("Open File"),
qstr!("."),
qstr!("Environments (*.dme)"),
)).to_std_string();
if !file.is_empty() {
state.borrow_mut().load_env(file, &mut *tree_widget_ptr);
}
});
menu_file.add_menu(qstr!("Recent Environments"));
menu_file.add_separator();
menu_file.add_action(qstr!("New"));
menu_file.add_action(qstr!("Open"));
menu_file.add_action(qstr!("Close"));
action!(menu_file, "New", (key = ^CTRL KeyN), (tip = "Create a new map."));
action!(menu_file, "Open", (key = ^CTRL KeyO), (tip = "Open a map."));
action!(menu_file, "Close", (key = ^CTRL KeyW), (tip = "Close the current map."));
menu_file.add_separator();
action!(menu_file, "Exit", (key = ^ALT KeyF4), (slot = window.slots().close()));
// status bar
let mut status_bar = widgets::status_bar::StatusBar::new();
// build main window
let mut window = widgets::main_window::MainWindow::new();
window.set_window_title(qstr!("SpacemanDMM"));
window.resize((1400, 768));
@ -74,9 +202,6 @@ fn main() {
window.set_status_bar(qt_own!(status_bar));
window.set_central_widget(qt_own!(splitter));
// connect signals
(*menu_file.add_action(qstr!("Exit"))).signals().triggered().connect(&window.slots().close());
// cede control
window.show();
Application::exec()