...
 
Commits (7)
This diff is collapsed.
......@@ -9,6 +9,7 @@ gdk = "0.9.0"
gdk-pixbuf = "0.5.0"
gio = "0.5.0"
glib = "0.6.0"
image = "*"
[dependencies.gtk]
features = ["v3_22"]
......
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.0 -->
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.22"/>
<object class="GtkMenu" id="app_menu">
......@@ -19,17 +19,6 @@
<property name="can_focus">False</property>
<property name="default_width">600</property>
<property name="default_height">300</property>
<child>
<object class="GtkLabel" id="label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">Hello, World!</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="2"/>
</attributes>
</object>
</child>
<child type="titlebar">
<object class="GtkHeaderBar" id="header_bar">
<property name="visible">True</property>
......@@ -38,12 +27,50 @@
<property name="subtitle">A cellular automata application for GNOME</property>
<property name="show_close_button">True</property>
<child>
<object class="GtkButton" id="action_button">
<property name="label">Action</property>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="always_show_image">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkButton" id="randomize_button">
<property name="label">Randomize</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="next_gen_button">
<property name="label" translatable="yes">Next gen</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="clear_button">
<property name="label" translatable="yes">Clear</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
<child>
......@@ -64,5 +91,12 @@
</child>
</object>
</child>
<child>
<object class="GtkImage" id="grid_da">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-missing-image</property>
</object>
</child>
</object>
</interface>
......@@ -7,16 +7,17 @@ use std::sync::{Arc, RwLock};
use crate::backend;
use crate::window;
use bkmessages::{BackendCommand, BackendUpdate};
pub fn launch() {
let app: gtk::Application =
gtk::Application::new("org.gnome.Automata", gio::ApplicationFlags::empty())
.expect("Could not create application");
let (cmd_sender, cmd_receiver) = channel::<String>();
let (cmd_sender, cmd_receiver) = channel::<BackendCommand>();
let cmd_sender = Arc::new(RwLock::new(cmd_sender));
let (update_sender, update_receiver) = channel::<String>();
let (update_sender, update_receiver) = channel::<BackendUpdate>();
let update_receiver = Arc::new(RwLock::new(update_receiver));
backend::launch(cmd_receiver, update_sender);
......
use std::sync::mpsc::{Receiver, Sender};
use std::thread;
pub fn launch(cmd_chan: Receiver<String>, update_chan: Sender<String>) {
use image::{ImageBuffer, Luma};
use super::foundry::Grid;
use super::bkmessages::{BackendCommand, BackendUpdate};
const image_buffer_path: &str = "/tmp/automata_buffer.png";
pub fn launch(cmd_chan: Receiver<BackendCommand>, update_chan: Sender<BackendUpdate>) {
thread::spawn(move || {
let mut count = 0;
let mut grid: Option<Grid> = None;
let width = 300;
let height = 200;
loop {
match cmd_chan.recv() {
Ok(_) => {
count += 1;
Ok(cmd) => {
match cmd {
BackendCommand::Randomize => {
if grid.is_none() {
grid = Some(Grid::new_random(
&String::from("#Toroidal Life"),
true,
&vec![2, 3],
&vec![3],
width,
height,
));
} else {
grid.as_mut().unwrap().randomize();
}
}
BackendCommand::NextGen(n) => {
if grid.is_none() {
update_chan
.send(BackendUpdate::Err(String::from("grid not initialized")))
.unwrap();
continue;
} else {
for _ in 0..n {
grid.as_mut().unwrap().next_gen();
}
}
}
BackendCommand::Clear => {
grid = Some(Grid::new(
&String::from("#Toroidal Life"),
true,
&vec![2, 3],
&vec![3],
width,
height,
))
}
}
let width = grid.as_ref().unwrap().get_width();
let height = grid.as_ref().unwrap().get_height();
let mut bytes: Vec<u8> = Vec::with_capacity(width * height);
for y in 0..height {
for x in 0..width {
bytes.push(grid.as_ref().unwrap().get_cell_state(x as i64, y as i64));
}
}
let img =
ImageBuffer::<Luma<u8>, _>::from_raw(width as u32, height as u32, bytes)
.unwrap();
img.save(image_buffer_path).unwrap();
update_chan
.send(String::from(format!(
"Action button clicked {} times!",
count
)))
.send(BackendUpdate::NewState(String::from(image_buffer_path)))
.unwrap();
}
Err(_) => {
......
pub enum BackendCommand {
Clear,
Randomize,
NextGen(u32),
}
pub enum BackendUpdate {
NewState(String),
Err(String),
}
extern crate gdk_pixbuf;
extern crate gio;
extern crate glib;
extern crate gtk;
extern crate image;
extern crate foundry;
// http://gtk-rs.org/tuto/closures
#[macro_export]
macro_rules! clone {
......@@ -23,6 +28,7 @@ macro_rules! clone {
mod app;
mod backend;
mod bkmessages;
mod static_resource;
mod window;
......
extern crate gio;
extern crate glib;
extern crate gtk;
use gdk_pixbuf::Pixbuf;
use gtk::prelude::*;
use std::sync::{Arc, RwLock};
use std::sync::mpsc::{Receiver, Sender};
use std::sync::{Arc, RwLock};
use bkmessages::{BackendCommand, BackendUpdate};
pub fn new_window(
cmd_chan: Arc<RwLock<Sender<String>>>,
update_chan: Arc<RwLock<Receiver<String>>>,
cmd_chan: Arc<RwLock<Sender<BackendCommand>>>,
update_chan: Arc<RwLock<Receiver<BackendUpdate>>>,
) -> gtk::ApplicationWindow {
let builder: gtk::Builder =
gtk::Builder::new_from_resource("/org/gnome/Automata/gtk/window.ui");
......@@ -23,18 +22,57 @@ pub fn new_window(
gtk::Inhibit(false)
});
let hello_label: gtk::Label = builder.get_object("label").expect("label not found");
let btn_action: gtk::Button = builder
.get_object("action_button")
.expect("action_button not found");
btn_action.connect_clicked(clone!(cmd_chan, update_chan => move |_| {
cmd_chan.read().unwrap().send(String::from("Button clicked!")).unwrap();
let grid_da: gtk::Image = builder.get_object("grid_da").expect("grid_da not found");
let btn_rand: gtk::Button = builder
.get_object("randomize_button")
.expect("randomize_button not found");
btn_rand.connect_clicked(clone!(cmd_chan, update_chan => move |_| {
cmd_chan.read().unwrap().send(BackendCommand::Randomize).unwrap();
let mut path = String::new();
match update_chan.read().unwrap().recv().unwrap() {
BackendUpdate::NewState(p) => path = p,
BackendUpdate::Err(msg) => println!("Error: {}", msg),
}
grid_da.set_from_file(path);
}));
let grid_da: gtk::Image = builder.get_object("grid_da").expect("grid_da not found");
let btn_ng: gtk::Button = builder
.get_object("next_gen_button")
.expect("next_gen_button not found");
btn_ng.connect_clicked(clone!(cmd_chan, update_chan => move |_| {
cmd_chan.read().unwrap().send(BackendCommand::NextGen(1)).unwrap();
let msg = update_chan.read().unwrap().recv().unwrap();
let mut path = String::new();
hello_label.set_text(&msg);
match update_chan.read().unwrap().recv().unwrap() {
BackendUpdate::NewState(p) => path = p,
BackendUpdate::Err(msg) => println!("Error: {}", msg),
}
grid_da.set_from_file(path);
}));
let grid_da: gtk::Image = builder.get_object("grid_da").expect("grid_da not found");
let btn_clear: gtk::Button = builder
.get_object("clear_button")
.expect("clear_button not found");
btn_clear.connect_clicked(clone!(cmd_chan, update_chan => move |_| {
cmd_chan.read().unwrap().send(BackendCommand::Clear).unwrap();
let mut path = String::new();
match update_chan.read().unwrap().recv().unwrap() {
BackendUpdate::NewState(p) => path = p,
BackendUpdate::Err(msg) => println!("Error: {}", msg),
}
grid_da.set_from_file(path);
}));
let mnu_about: gtk::MenuItem = builder
.get_object("about_menu")
.expect("about_menu not found");
......