diff --git a/src/build.rs b/src/build.rs --- a/src/build.rs +++ b/src/build.rs @@ -186,7 +186,7 @@ /// A builder for binding generation and compilation of a Qt application. /// -/// Pass options into this `Build` and then run `build` to generate binddings +/// Pass options into this `Build` and then run `build` to generate bindings /// and compile the Qt C++ code and resources into a static library. /// This struct is meant to be used in a `build.rs` script. pub struct Build { @@ -196,6 +196,7 @@ build: cc::Build, bindings: Vec, qrc: Vec, + ui: Vec, h: Vec, cpp: Vec, modules: Vec, @@ -217,6 +218,7 @@ /// Build::new(&out_dir) /// .bindings("bindings.json") /// .qrc("qml.qrc") + /// .ui("main.ui") /// .cpp("src/main.cpp") /// .compile("my_app"); /// } @@ -235,6 +237,7 @@ build, bindings: Vec::new(), qrc: Vec::new(), + ui: Vec::new(), h: Vec::new(), cpp: Vec::new(), modules: vec![QtModule::Core], @@ -262,6 +265,13 @@ self.qrc.push(path.as_ref().to_path_buf()); self } + /// Add a ui file to be processed. + /// + /// ui files are generated by Qt Designer and describe how to build the user interface. + pub fn ui>(&mut self, path: P) -> &mut Build { + self.ui.push(path.as_ref().to_path_buf()); + self + } /// Add a C++ header file to be compiled into the program. pub fn h>(&mut self, path: P) -> &mut Build { self.h.push(path.as_ref().to_path_buf()); @@ -314,6 +324,9 @@ handle_binding(&self.out_dir, binding, &mut self.h, &mut self.cpp); } let mut compile_inputs: Vec<&Path> = Vec::new(); + for ui in &self.ui { + handle_ui(&self.out_dir, ui, &mut self.h); + } for h in &self.h { compile_inputs.push(h); handle_header(h, &mut self.cpp); @@ -409,6 +422,11 @@ run("rcc", Command::new("rcc").arg("-o").arg(output).arg(rcfile)); } +/// Run uic to generate C++ code from a QT ui file +fn uic(uifile: &Path, output: &Path) { + run("uic", Command::new("uic").arg("-o").arg(output).arg(uifile)); +} + /// return true if a command should run. /// It returns true if all inputs are present and if any of the inputs is newer /// than the newest output or if the outputs do not exist yet. @@ -465,6 +483,17 @@ cpp.push(qml_cpp); } +fn handle_ui(out_dir: &Path, ui_path: &Path, h: &mut Vec) { + let ui_h = out_dir.join(format!( + "ui_{}.h", + ui_path.file_stem().unwrap().to_str().unwrap() + )); + if should_run(&[ui_path], &[&ui_h]) { + uic(&ui_path, &ui_h); + } + h.push(ui_h); +} + fn handle_header(h: &Path, cpp: &mut Vec) { let moc_file = h.parent().unwrap().join(format!( "moc_{}.cpp", diff --git a/templates/qt_widgets_cargo/Cargo.toml b/templates/qt_widgets_cargo/Cargo.toml new file mode 100644 --- /dev/null +++ b/templates/qt_widgets_cargo/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "qt_widgets_cargo" +version = "0.1.0" +build = "build.rs" +links = "qt_widgets_cargo" + +[dependencies] +libc = "*" + +[build-dependencies] +rust_qt_binding_generator = { path = "../.." } diff --git a/templates/qt_widgets_cargo/README.md b/templates/qt_widgets_cargo/README.md new file mode 100644 --- /dev/null +++ b/templates/qt_widgets_cargo/README.md @@ -0,0 +1,15 @@ +Qt Widgets template project with Rust bindings + +This is a template project for writing a Qt Widgets GUI on top of Rust code. + +`bindings.json` defines the interface between the Qt and Rust code. + +`main.ui` is a Qt Designer interface specification that is used to generates UI code. + +Build instructions are written in `build.rs`. + +Build this code with + +```bash +cargo build +``` diff --git a/templates/qt_widgets_cargo/bindings.json b/templates/qt_widgets_cargo/bindings.json new file mode 100644 --- /dev/null +++ b/templates/qt_widgets_cargo/bindings.json @@ -0,0 +1,19 @@ +{ + "cppFile": "Bindings.cpp", + "rust": { + "dir": "", + "interfaceModule": "interface", + "implementationModule": "implementation" + }, + "objects": { + "Simple": { + "type": "Object", + "properties": { + "message": { + "type": "QString", + "write": true + } + } + } + } +} diff --git a/templates/qt_widgets_cargo/build.rs b/templates/qt_widgets_cargo/build.rs new file mode 100644 --- /dev/null +++ b/templates/qt_widgets_cargo/build.rs @@ -0,0 +1,13 @@ +extern crate rust_qt_binding_generator; + +use rust_qt_binding_generator::build::QtModule; + +fn main() { + let out_dir = ::std::env::var("OUT_DIR").unwrap(); + rust_qt_binding_generator::build::Build::new(&out_dir) + .bindings("bindings.json") + .ui("src/main.ui") + .cpp("src/main.cpp") + .module(QtModule::Widgets) + .compile("qt_widgets_cargo"); +} diff --git a/templates/qt_widgets_cargo/src/implementation.rs b/templates/qt_widgets_cargo/src/implementation.rs new file mode 100644 --- /dev/null +++ b/templates/qt_widgets_cargo/src/implementation.rs @@ -0,0 +1,26 @@ +use interface::*; + +pub struct Simple { + emit: SimpleEmitter, + message: String, +} + +impl SimpleTrait for Simple { + fn new(emit: SimpleEmitter) -> Simple { + Simple { + emit: emit, + message: String::new(), + } + } + fn emit(&mut self) -> &mut SimpleEmitter { + &mut self.emit + } + fn message(&self) -> &str { + "Hello World!" + } + fn set_message(&mut self, value: String) { + self.message = value; + self.emit.message_changed(); + } +} + diff --git a/templates/qt_widgets_cargo/src/main.cpp b/templates/qt_widgets_cargo/src/main.cpp new file mode 100644 --- /dev/null +++ b/templates/qt_widgets_cargo/src/main.cpp @@ -0,0 +1,29 @@ +#include "Bindings.h" + +#include "ui_main.h" + +extern "C" { + int main_cpp(const char* app); +} + +int main_cpp(const char* appPath) +{ + int argc = 1; + char* argv[1] = { (char*)appPath }; + QApplication app(argc, argv); + + Simple simple; // This is the Rust object + + QMainWindow main; + Ui_MainWindow ui; + ui.setupUi(&main); + + ui.mesg->setText(simple.message()); + ui.quitButton->connect(ui.quitButton, &QPushButton::clicked, ui.quitButton, []() { + QCoreApplication::quit(); + }); + + main.show(); + + return app.exec(); +} diff --git a/templates/qt_widgets_cargo/src/main.rs b/templates/qt_widgets_cargo/src/main.rs new file mode 100644 --- /dev/null +++ b/templates/qt_widgets_cargo/src/main.rs @@ -0,0 +1,19 @@ +extern crate libc; + +mod implementation; +pub mod interface { + include!(concat!(env!("OUT_DIR"), "/src/interface.rs")); +} + +extern "C" { + fn main_cpp(app: *const ::std::os::raw::c_char); +} + +fn main() { + use std::ffi::CString; + let app_name = ::std::env::args().next().unwrap(); + let app_name = CString::new(app_name).unwrap(); + unsafe { + main_cpp(app_name.as_ptr()); + } +} diff --git a/templates/qt_widgets_cargo/src/main.ui b/templates/qt_widgets_cargo/src/main.ui new file mode 100644 --- /dev/null +++ b/templates/qt_widgets_cargo/src/main.ui @@ -0,0 +1,61 @@ + + + MainWindow + + + + 0 + 0 + 250 + 100 + + + + Widgets Template + + + + + + 0 + 20 + 250 + 20 + + + + + + + Qt::AlignCenter + + + + + + 85 + 40 + 80 + 30 + + + + Quit + + + + + + + 0 + 0 + 180 + 30 + + + + + + + +