Hello
So I'm basically trying to make a calculator in OrbTk.
This is my idea on how to do it.
I have a struct Calculator with a string property called actions
. This struct also has a function add_action(String)
. Each time a user clicks on a graphical button the character of that button is added to the action string.
So if the user presses the button five, plus-button, and button two. The content of the action string is "5+2"
.
When finally the user presses enter, I interpret the string and execute the math from left to right.
I'm pretty close, but I got a typical Rust-error blocking my way.
Code:
use orbtk::*;
use std::vec::Vec;
use std::cell::RefCell;
use std::rc::Rc;
widget!(MainView);
//This struct holds all the logic and math
//to calculate what the user wants
struct Calculator {
//This string holds all the actions (ciphers, operators,...)
//the user presses. On enter we execute all actions in this string
//and reset it.
actions : String,
}
impl Calculator {
//Constructor
fn new() -> Self {
Calculator{
actions: "".to_string(),
}
}
/*
* Gets executed when a button on the calculator is pressed
* This button gets added to the action string
* :param pressed_key: String representation of the key the user pressed
*/
fn add_action(&mut self, pressed_key : &String) {
self.actions.push_str(pressed_key);
}
}
impl Template for MainView {
fn template(self, _: Entity, ctx: &mut BuildContext) -> Self {
//Delcare Calculator logic here
let mut calc = Calculator::new();
//Initializing Grid
let mut grid = Grid::create();
//Configuring grid (amount of rows and columns)
grid = grid
.columns(
Columns::create()
.column("*")
.column("*")
.column("*")
.column("*")
.build()
)
.rows(
Rows::create()
.row("*")
.row("*")
.row("*")
.row("*")
.build()
);
//Adding all buttons from 1-9 to the grid
//in calculator format
let mut counter : u8 = 9;
for i in 0..3 {
for j in 0..3 {
grid = grid.child(
Button::create()
.text(counter.to_string())
.attach(Grid::column(2-j))
.attach(Grid::row(i))
.on_click(move |_states, _| -> bool {
//Add to Calculator's action-string when clicked
calc.add_action(&counter.to_string());
true
})
.build(ctx)
);
counter = counter - 1;
}
}
self.name("MainView").child(
grid.build(ctx)
)
}
}
fn main() {
Application::new()
.window(|ctx| {
Window::create()
.title("OrbTk - Calculator")
.position((100.0, 100.0))
.size(420.0, 730.0)
.child(MainView::create().build(ctx))
.build(ctx)
})
.run();
}
Error:
error[E0596]: cannot borrow `calc` as mutable, as it is a captured variable in a `Fn` closure
--> src/main.rs:74:29
|
74 | ... calc.add_action(&counter.to_string());
| ^^^^ cannot borrow as mutable
|
help: consider changing this to accept closures that implement `FnMut`
--> src/main.rs:72:35
|
72 | .on_click(move |_states, _| -> bool {
| ___________________________________^
73 | | //Add to Calculator's action-string when clicked
74 | | calc.add_action(&counter.to_string());
75 | | true
76 | | })
| |_________________________^
warning: variable does not need to be mutable
--> src/main.rs:38:13
|
38 | let mut calc = Calculator::new();
| ----^^^^
| |
| help: remove this `mut`
|
= note: `#[warn(unused_mut)]` on by default
error[E0382]: use of moved value: `calc`
--> src/main.rs:72:35
|
38 | let mut calc = Calculator::new();
| -------- move occurs because `calc` has type `Calculator`, which does not implement the `Copy` trait
...
72 | .on_click(move |_states, _| -> bool {
| ^^^^^^^^^^^^^^^^^^^^^^^^^ value moved into closure here, in previous iteration of loop
73 | //Add to Calculator's action-string when clicked
74 | calc.add_action(&counter.to_string());
| ---- use occurs due to use in closure
error: aborting due to 2 previous errors
I asked about this issue on Rust's Discord channel and @Arnavion was so kind to help me further with the following suggestions:
- If on_click wasn't in my control I had to use Mutex or RefCell to change Fn into FnMut.
.on_click({ let calc = RefCell::new(calc); move |_states, _| -> bool { let calc = calc.borrow_mut(); calc.add_action(...); true } })
- But because it was all in a loop: You need to share
calc
amongst multiple closures, so it needs to be created outside the loop, and needs to be in anRc
so that each closure owns a clone of theRc
, ie the same RefCell.
So after implementing that suggestion this way:
let mut calc = RefCell::new(Calculator::new());
//Initializing Grid
let mut grid = Grid::create();
//Adding all buttons from 1-9 to the grid
//in calculator format
let mut counter : u8 = 9;
for i in 0..3 {
for j in 0..3 {
grid = grid.child(
Button::create()
.text(counter.to_string())
.attach(Grid::column(2-j))
.attach(Grid::row(i))
.on_click({let calc = RefCell::new(calc); move |_states, _| -> bool {
//Add to Calculator's action-string when clicked
let mut calc = calc.borrow_mut();
calc.add_action(&counter.to_string());
true
}})
.build(ctx)
);
counter = counter - 1;
}
}
I still got this error:
error[E0382]: use of moved value: `calc`
--> src/main.rs:77:60
|
37 | let mut calc = Calculator::new();
| -------- move occurs because `calc` has type `Calculator`, which does not implement the `Copy` trait
...
77 | .on_click({let calc = RefCell::new(calc); move |_states, _| -> bool {
| ^^^^ value moved here, in previous iteration of loop
error: aborting due to previous error
I'm pretty sure it are good suggestions, I only have no idea what it means.
I know that Rc
is a special kind of reference which allows a variable to have multiple owners, while normally a variable can only have one owner, and other instances can 'borrow' it but don't own it.
And RefCell
is according to Google a run-time borrow-checker. Whatever that means...
A bump in the right direction with some explanation would be appreciated!
Thanks!