Move occurs because value has type `X`, which does not implement the `Copy` trait

I have a few buttons, when a button gets clicked it needs to be appended to a display (a TextBox).
This done by first writing the pressed buttons to a String.

use orbtk::*;
use std::vec::Vec;
use std::cell::RefCell;
use std::rc::Rc;
use std::io::Write;
use std::str::FromStr;

extern crate strum;
#[macro_use]
extern crate strum_macros;

widget!(MainView);

// Enumeration of all possible operators
#[derive(EnumString)]
enum Operator {
    #[strum(serialize="+")]
    Plus,
    #[strum(serialize="-")]
    Min,
    #[strum(serialize="*")]
    Times,
    #[strum(serialize="/")]
    Divide,
}

impl Template for MainView {
    fn template(self, _: Entity, ctx: &mut BuildContext) -> Self {

        // String holding pressed keys creating an infix string
        // And a TextBox object holding that string
        let pressed_keys : Rc<RefCell<String>> = Rc::new(RefCell::new("".to_string()));
        let mut screen : Rc<RefCell<TextBox>> =
            Rc::new(RefCell::new(
                TextBox::create()
                    .text("")
                    .attach(Grid::row(0))
                    .attach(Grid::column(0))
                    .attach(Grid::column_span(4))
            ));


        // 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(50.0)
                    .row(50.0)
                    .row(50.0)
                    .row(50.0)
                    .row(50.0)
                    .row(50.0)
                    .row(50.0)
                    .build()
            );
       
        //Adding textbox holding entered numbers and operators
        grid = grid.child(
            screen.borrow_mut().build(ctx)
        );

        // Adding all buttons from 1-9 to the grid
        // in calculator format
        let mut counter : u8 = 9;
        for i in 1..4 {
            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 pressed_keys = pressed_keys.clone();
                            let screen = screen.clone();
                            move |_states, _| -> bool {
                                *pressed_keys.borrow_mut() = format!("{}{}", *pressed_keys.borrow(), counter.to_string());
                                let screen_text : String = pressed_keys.borrow().clone();
                                screen.borrow().text(screen_text);
                                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, 500.0)
                .child(MainView::create().build(ctx))
                .build(ctx)
        })
        .run();
}

Cargo.toml:

[package]
name = "orb_tk_calculator"
version = "0.1.0"
authors = ["lol"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
orbtk = { git = "https://github.com/redox-os/orbtk.git", branch = "develop" }

strum = "0.18.0"
strum_macros = "0.18.0"

This is the error I get:

error[E0507]: cannot move out of dereference of `std::cell::Ref<'_, orbtk_widgets::text_box::TextBox>`
  --> src/main.rs:89:33
   |
89 | ...                   screen.borrow().text(screen_text);
   |                       ^^^^^^^^^^^^^^^ move occurs because value has type `orbtk_widgets::text_box::TextBox`, which does not implement the `Copy` trait

I tried dereferencing, borrowing, cloning, and things like *screen.borrow_mut() = (*screen).borrow().text(screen_text); but none worked. Anyone an idea of how to fix this?

You don't have ownership, and for some reason that function requires ownership.

And how would I solve that?

Probably out of my depth, but what if you change the above to

let screen = screen.borrow().clone();

Because you are currently cloning a RefCell outside the scope of your move, which I think is probably problematic since the wrapped type does not implement Copy.

let screen = screen.borrow().clone();

returns

error[E0599]: no method named `clone` found for struct `std::cell::Ref<'_, orbtk_widgets::text_box::TextBox>` in the current scope
  --> src/main.rs:85:58
   |
85 | ...                   let screen = screen.borrow().clone();
   |                                    ----------------^^^^^
   |                                    |               |
   |                                    |               this is an associated function, not a method
   |                                    help: use associated function syntax instead: `std::cell::Ref::<'_, orbtk_widgets::text_box::TextBox>::clone`
   |
   = note: found the following associated functions; to be used as methods, functions must have a `self` parameter
   = note: the candidate is defined in an impl for the type `std::cell::Ref<'_, _>`

Oh sorry. I must be missing something about the types here. When you borrow an Rc<T> you should get &T, so my assumption was screen.borrow() would give you &RefCell<orbtk_widgets::text_box::TextBox> and RefCell does implement Clone as a method. You could try

let screen = std::cell::Ref::<'_, orbtk_widgets::text_box::TextBox>::clone(screen.borrow());

but I'm just not sure. Hopefully someone more knowledgeable will be able to help.

1 Like

Thanks for your help. I have no idea on how to implement that though, where would that code belong?

Are you using Orbtk 0.2.27 or the latest version 0.3.1? Looking at the source for 0.2.27, the on_click API calls for a Fn parameter, which means that let screen = screen.clone() being called in the function you pass as a parameter to on_click is moving the original screen into that closure. Move the screen.clone() out of the grid = expression and it might work.

for i in 1..4 {
            for j in 0..3 {
                let screen = screen.clone();
                grid = grid.child(
                    Button::create()
                        .text(counter.to_string())
                        .attach(Grid::column(2-j))
                        .attach(Grid::row(i))
                        .on_click({
                            let pressed_keys = pressed_keys.clone();
                            
                            move |_states, _| -> bool {
                                *pressed_keys.borrow_mut() = format!("{}{}", *pressed_keys.borrow(), counter.to_string());
                                let screen_text : String = pressed_keys.borrow().clone();
                                screen.borrow().text(screen_text);
                                true
                            }
                        })
                        .build(ctx)
1 Like

The fact that text() takes self indicates that this is only meant to be used as part of the Builder pattern; it is strictly for setting the initial text (or for binding it to another object), not for changing the text once the widget is created. I imagine there is simply nothing you can do that will ever make .text() work for this purpose.

Because the documentation of orbtk is so sparse, I would look at the examples. Here is an example that looks like it is changing the text of an existing widget:

1 Like

I have made a new code (for the sole purpose of resolving this issue) with only a button and a textbox. The textbox gets updated with the text from the button.

use orbtk::*;

widget!(MainView<MainViewState>);

#[derive(Copy, Clone)]
enum Action {
    Char(char),

}

#[derive(AsAny, Default)]
pub struct MainViewState {
    screen: String,
    action: Option<Action>,
}

impl MainViewState {
    fn action(&mut self, action: impl Into<Option<Action>>) { //?
        self.action = action.into();
    }
}

impl State for MainViewState {
    fn update(&mut self, _: &mut Registry, ctx: &mut Context) {
        if let Some(action) = self.action {
            match action {
                Action::Char(c) => match c {
                    '=' => {
                        println!("Here we're launching a calculate function.");
                    }
                    _ => {
                        ctx.child("screen").get_mut::<String16>("text").push(c);
                    }
                }
            }
        }
    }
}

// helper to request MainViewState
fn state<'a>(id: Entity, states: &'a mut StatesContext) -> &'a mut MainViewState {
    states.get_mut(id)
}

fn generate_button(
    ctx: &mut BuildContext,
    text: String,
    id: Entity,
    row: usize,
) -> Entity {
    let mut button = Button::create()
        .text(text)
        .attach(Grid::row(row))
        .on_click({
            move |states, _| {
                state(id, states).action(Action::Char('1'));
                true
            }
        });

    button.build(ctx)
}

impl Template for MainView {
    fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
        self.name("MainView")
            .width(220.0)
            .height(180.0)
            .child(
                Grid::create()
                    .rows(
                        Rows::create()
                            .row("*")
                            .row("*")
                            .build()
                    )
                    .child(
                        TextBlock::create()
                            .text("")
                            .id("screen")
                            .attach(Grid::row(0))
                            .build(ctx)
                    )
                    .child(
                        Button::create()
                            .text("1")
                            .attach(Grid::row(1))
                            .on_click(
                                move |states, _| -> bool {
                                    state(id, states).action(Action::Char('1'));
                                    true
                                }
                            )
                            .build(ctx)
                    ).build(ctx)
            )
    }
}

fn main() {
    Application::new()
        .window(|ctx| {
            Window::create()
                .title("OrbTk")
                .position((100.0, 100.0))
                .size(212.0, 336.0)
                .child(MainView::create().build(ctx))
                .build(ctx)
        })
        .run();
}

This works too.
It's a more OrbTk-ish way.