Hey all, I'm relatively new to Rust, and this is my first toy application using Rust and Slint. I did this to just start getting a hang of both. I'd appreciate feedback!
The application is a very simple calculator, with basic math ops.
use std::cell::RefCell;
use std::rc::{Rc, Weak};
slint::slint! {
import { Button, LineEdit } from "std-widgets.slint";
export enum CalculatorViewOp { Add, Subtract, Multiply, Divide, Evaluate }
export component CalculatorView {
callback input(float);
callback operation(CalculatorViewOp);
in property <float> current_value: 0;
VerticalLayout {
alignment: stretch;
padding: 15px;
spacing: 5px;
Text {
text: "Awesome Calculator";
horizontal-alignment: center;
}
LineEdit {
vertical-stretch: 0;
horizontal-alignment: right;
input-type: decimal;
text: current_value;
}
GridLayout {
vertical-stretch: 1;
spacing: 5px;
Row {
Button {
text: "1";
clicked => { root.input(1); }
}
Button {
text: "2";
clicked => { root.input(2); }
}
Button {
text: "3";
clicked => { root.input(3); }
}
}
Row {
Button {
text: "4";
clicked => { root.input(4); }
}
Button {
text: "5";
clicked => { root.input(5); }
}
Button {
text: "6";
clicked => { root.input(6); }
}
}
Row {
Button {
text: "7";
clicked => { root.input(7); }
}
Button {
text: "8";
clicked => { root.input(8); }
}
Button {
text: "9";
clicked => { root.input(9); }
}
}
Row {
Button {
text: "0";
clicked => { root.input(0); }
}
}
Button {
text: "+";
col: 3;
row: 0;
clicked => { root.operation(CalculatorViewOp.Add); }
}
Button {
text: "-";
col: 3;
row: 1;
clicked => { root.operation(CalculatorViewOp.Subtract); }
}
Button {
text: "×";
col: 3;
row: 2;
clicked => { root.operation(CalculatorViewOp.Multiply); }
}
Button {
text: "÷";
col: 3;
row: 3;
clicked => { root.operation(CalculatorViewOp.Divide); }
}
Button {
text: "=";
row: 3;
colspan: 2;
col: 1;
clicked => { root.operation(CalculatorViewOp.Evaluate); }
}
}
}
}
}
#[derive(Copy, Clone)]
enum CalculatorMathOps {
Add,
Subtract,
Multiply,
Divide,
}
#[derive(Copy, Clone)]
enum CalculatorAction {
Number(f32),
Op(CalculatorMathOps),
Evaluate,
}
#[derive(Default)]
struct CalculatorViewModel {
memory: Option<f32>,
user_entered_value: Option<f32>,
op_value: Option<f32>,
operation: Option<CalculatorMathOps>,
}
impl CalculatorViewModel {
fn perform_calculation(&mut self) {
let prev_op = self.operation.unwrap();
let memory = self.memory.unwrap();
let value = if self.user_entered_value.is_some() {
self.user_entered_value.unwrap()
} else {
self.op_value.unwrap()
};
let computed_value = match prev_op {
CalculatorMathOps::Add => memory + value,
CalculatorMathOps::Subtract => memory - value,
CalculatorMathOps::Multiply => memory * value,
CalculatorMathOps::Divide => memory / value,
};
self.memory = Some(computed_value);
}
fn can_perform_calculation(&self, use_op_value: bool) -> bool {
self.memory.is_some()
&& (self.user_entered_value.is_some() || (use_op_value && self.op_value.is_some()))
}
fn perform_action(&mut self, action: CalculatorAction) {
match action {
CalculatorAction::Number(n) => {
self.user_entered_value = Some((self.user_entered_value.unwrap_or_default() * 10.0) + n);
self.op_value = self.user_entered_value;
}
CalculatorAction::Evaluate if self.can_perform_calculation(true) => {
self.perform_calculation();
self.user_entered_value = None;
}
CalculatorAction::Op(op) if self.can_perform_calculation(false) => {
self.perform_calculation();
self.operation = Some(op);
}
CalculatorAction::Op(op) if self.user_entered_value.is_some() => {
self.memory = self.user_entered_value;
self.op_value = None;
self.user_entered_value = None;
self.operation = Some(op);
}
CalculatorAction::Op(op) => {
self.op_value = None;
self.user_entered_value = None;
self.operation = Some(op);
}
_ => {}
}
}
}
fn invalidate_view(
vm_weak: Weak<RefCell<CalculatorViewModel>>,
view_weak: slint::Weak<CalculatorView>,
) {
let view = view_weak.upgrade().unwrap();
let vm_strong = vm_weak.upgrade().unwrap();
let vm = vm_strong.borrow();
if let Some(value) = vm.user_entered_value {
view.set_current_value(value);
} else if let Some(value) = vm.memory {
view.set_current_value(value);
}
}
fn main() {
let vm: Rc<RefCell<CalculatorViewModel>> =
Rc::new(RefCell::new(CalculatorViewModel::default()));
let vm_weak = Rc::downgrade(&vm);
let view = CalculatorView::new().unwrap();
let view_weak = view.as_weak();
view.on_input(move |id| {
{
let vm_strong = vm_weak.upgrade().unwrap();
let mut vm = vm_strong.borrow_mut();
vm.perform_action(CalculatorAction::Number(id));
}
invalidate_view(vm_weak.clone(), view_weak.clone());
});
let vm_weak = Rc::downgrade(&vm);
let view_weak = view.as_weak();
view.on_operation(move |op| {
let vm_strong = vm_weak.upgrade().unwrap();
{
let mut vm = vm_strong.borrow_mut();
match op {
CalculatorViewOp::Add => {
vm.perform_action(CalculatorAction::Op(CalculatorMathOps::Add));
}
CalculatorViewOp::Subtract => {
vm.perform_action(CalculatorAction::Op(CalculatorMathOps::Subtract));
}
CalculatorViewOp::Multiply => {
vm.perform_action(CalculatorAction::Op(CalculatorMathOps::Multiply));
}
CalculatorViewOp::Divide => {
vm.perform_action(CalculatorAction::Op(CalculatorMathOps::Divide));
}
CalculatorViewOp::Evaluate => {
vm.perform_action(CalculatorAction::Evaluate);
}
}
}
invalidate_view(vm_weak.clone(), view_weak.clone());
});
view.run().unwrap();
}