[Learn Rust together | Framework | iced framework] rust native cross-platform GUI framework - iced


foreword

To learn a programming language, you must never abandon the application of the programming language. When learning other programming languages, such as C++, it is quite boring to only learn grammar, data structure and algorithm. This is a matter of one's perseverance. The best way at this time is to make learning interesting. When I was studying, the source of my interest was to make gorgeous windows on Windows to improve my learning and work efficiency. For this reason, I have studied QT, MFC, these frameworks are very good, and indeed achieved the effect I want, but the development is more cumbersome. Later, I learned qt python, and the development became very convenient, but a new problem appeared again, that is, the packaging is not Conveniently, for this reason, I have experienced tortuous explorations on this road. At this time, learning Rust is the same. If you want to improve your learning ability, you must find the points of interest. For Rust, I really want to use the advantages of Rust to realize Windows windows, and I have also found related solutions, such as Rust's qt, but it is cumbersome, will be introduced in a later article; there is also Tauri UI, a cross-platform ui framework that uses Rust as the back end, its idea is more like Electron, the front end uses html+css layout, and then The backend uses Rust, and then packages the app. The usage method will be introduced later in this series of articles; there is also Iced, which is introduced in this article, which is more like Rust's flutter, a cross-platform GUI framework based on Elm.

Iced is a GUI framework that I am more interested in. Its development method is quite friendly to people like me who have learned Vue, and it is very comfortable with the characteristics of Rust. In addition, its appearance is also quite high, which is why I learn it.

Features of Iced

  • Simple and easy to use, with a series of built-in APIs
  • Type-safe, with an interactive programming model
  • Cross-platform (supports Windows, Mac, Linux and Web)
  • responsive layout
  • Based on widgets
  • Support custom widgets
  • There are other features, it is recommended to go to Github to view

1. Build the project

1. Create a project normally

First create a project

cargo new gui_iced_001

2. Import idea

Import project using idea

3. Introduce dependencies

Open Cargo.toml, and write in dependencies

iced = "0.4"

注意:I am using a version after 2021. If you are not, it is recommended to go to the official website to learn the corresponding processing strategy, which will not be explained here.

At this point the file should look like this

So far, the project has been built, and the next step is to write our demo.

2. Write a demo

There are many examples of Iced on Github, among which the most classic and the only example written on the official website is the Counter counter, because the Counter demo is realized from here. Although this is very simple, there are many pitfalls in it. It was hard to write this demo. Let’s talk about two pitfalls. This framework does not distinguish between rows and columns. The code in the example is too old. I took it step by step. explored.

The following content will be quite different from the official website, and the official website will not work, please pay attention to screening.

1. Write State

First, write a State for the program. This core concept will inevitably be used when learning Vue, React or Flutter. I will not explain it here for the time being, but give the code directly.

struct Counter {
    
    
    value: i32,
    // The local state of the two buttons
    increment_button: button::State,
    decrement_button: button::State,
}

Here is a structure defined Counter , this structure is regarded as the State of our window, its members valuerepresent the value of the counter count, increment_buttonand decrement_buttonthe state of the two buttons + and -.

2. Define the message type

The next step is to define the message types used in the program. The interaction in the program is carried out through signals. Qt does this very well. If you have studied qt or the front-end Vue and other frameworks, this is easy to understand. .

#[derive(Debug, Clone, Copy)]
enum Message {
    
    
    IncrementPressed,
    DecrementPressed,
}

Two messages are defined here, one is IncrementPressedto represent the + button is clicked, and the other is to DecrementPressedrepresent the - button to be clicked.

3. Write view logic

The official example here is to directly implement view and update for Counter, but after my exploration, it cannot be used directly.

The Iced program needs to implement Application or Sandbox. I don’t care what these two mean for now. We use Sandbox here because it is simple enough.

An empty Sandbox should look like this

impl Sandbox for Counter {
    
    
    type Message = ();

    fn new() -> Self {
    
    
        todo!()
    }

    fn title(&self) -> String {
    
    
        todo!()
    }

    fn update(&mut self, message: Self::Message) {
    
    
        todo!()
    }

    fn view(&mut self) -> Element<'_, Self::Message> {
    
    
        todo!()
    }
}

Here is the introduction from top to bottom

Message

It represents all the messages of the current window. When using it, you need to pass in the defined message enumeration. For example, the usage here should be like this,

#[derive(Debug, Clone, Copy)]
enum Message {
    
    
    IncrementPressed,
    DecrementPressed,
}
// ....
type Message = Message;

new

Here is the same as writing code in general, you need to return your own instance, so I won’t explain too much

fn new() -> Self {
    
    
        Self {
    
     value: 0, increment_button: Default::default(), decrement_button: Default::default() }
    }

title

See the name, here is to return the name of the current window

fn title(&self) -> String {
    
    
        String::from("Counter - Iced")
    }

update

Here is the message logic of the processing window. This window processes two messages. One is that IncrementPressedthe + button is clicked. If it is clicked, the Value of the State will be +1. The other is that DecrementPressedthe - button is clicked. If it is clicked, The Value of State is -1. The processing here is quite simple and does not take boundary values ​​into account.

fn update(&mut self, message: Message) {
    
    
        match message {
    
    
            Message::IncrementPressed => {
    
    
                self.value += 1;
            }
            Message::DecrementPressed => {
    
    
                self.value -= 1;
            }
        }
    }

view

Here to return the layout of the window, in fact, to build this window, here the window code of Counter is as follows

fn view(&mut self) -> Element<Message> {
    
    
        Column::new().push(
            Text::new("Counter")
        ).push(
            Row::new().push(
                Button::new(&mut self.increment_button, Text::new("+"))
                    .on_press(Message::IncrementPressed).width(Length::Fill),
            ).push(
                Text::new(self.value.to_string()).size(22),
            ).push(
                Button::new(&mut self.decrement_button, Text::new("-"))
                    .on_press(Message::DecrementPressed).width(Length::Fill),
            )
        )
        .align_items(Alignment::Center).into()
    }

It can be seen that the time-chain call used here is layer by layer. Its code is very similar to flutter. If you have studied flutter, you must be very familiar with it. Here I draw a picture to explain this code. First The window is divided into two rows by Column, and its layout is consistent with the red frame, with two rows up and down

注意:In this framework, rows and columns are stupidly indistinguishable. Column means column, and in this framework it means row.

In the first line, only a Text component is added, and the initial value is given Counter. It was originally intended to use Chinese here 计数器, but this thing does not support Chinese.

Column::new().push(
            Text::new("Counter")
        )

In the second line, a Row component (component) is added , and three components are added, which are two Buttons, which are + and - buttons, and a Text component to display the current Value

//...
.push(
            Row::new().push(
                Button::new(&mut self.increment_button, Text::new("+"))
                    .on_press(Message::IncrementPressed).width(Length::Fill),
            ).push(
                Text::new(self.value.to_string()).size(22),
            ).push(
                Button::new(&mut self.decrement_button, Text::new("-"))
                    .on_press(Message::DecrementPressed).width(Length::Fill),
            )
        )

So the window layout should look like this

4. Write the main function

The written window cannot be run automatically, it needs to be started, usually start the window in the main function, it will become easier here, paste the code directly here

fn main() -> iced::Result {
    
    
    Counter::run(Settings::default())
}

To start the window, just call the run method of the window, which is passed in Settings to set the initial state of the window. Here, the default state is used directly. If you want to go deeper later, here will be a special issue to explain it.

3. Operation effect

At this point we run the currently written demo

注意:The complete code is at the end of the article, if you are too lazy to type the code, you can copy it directly.


Summarize

This issue introduces Rust's GUI framework Iced. After my exploration, I finally built the Counter Demo. After my experience, I think this framework is not as good as I imagined. It is indeed Rust's native GUI framework, and it does have the features he said. It is indeed beautiful, but it has the biggest problem. It does not support Chinese, and the row and column are stupidly indistinguishable. The official document is too old, so the process of building a demo becomes very complicated, and it is not very friendly to development. The only thing worth mentioning is this code, which is really comfortable Quite a few, this is unmatched by other UI frameworks. This is worthy of approval. I hope that this framework or its successors can solve these problems in the future, so that Rust's UI can become stronger.

full code

use iced::{
    
    Alignment, button, Button, Column, Element, Length, Row, Sandbox, Settings, Text};

fn main() -> iced::Result {
    
    
    Counter::run(Settings::default())
}

struct Counter {
    
    
    value: i32,
    // The local state of the two buttons
    increment_button: button::State,
    decrement_button: button::State,
}

#[derive(Debug, Clone, Copy)]
enum Message {
    
    
    IncrementPressed,
    DecrementPressed,
}

impl Sandbox for Counter {
    
    
    type Message = Message;

    fn new() -> Self {
    
    
        Self {
    
     value: 0, increment_button: Default::default(), decrement_button: Default::default() }
    }

    fn title(&self) -> String {
    
    
        String::from("Counter - Iced")
    }

    fn update(&mut self, message: Message) {
    
    
        match message {
    
    
            Message::IncrementPressed => {
    
    
                self.value += 1;
            }
            Message::DecrementPressed => {
    
    
                self.value -= 1;
            }
        }
    }

    fn view(&mut self) -> Element<Message> {
    
    
        Column::new().push(
            Text::new("Counter")
        ).push(
            Row::new().push(
                Button::new(&mut self.increment_button, Text::new("+"))
                    .on_press(Message::IncrementPressed).width(Length::Fill),
            ).push(
                Text::new(self.value.to_string()).size(22),
            ).push(
                Button::new(&mut self.decrement_button, Text::new("-"))
                    .on_press(Message::DecrementPressed).width(Length::Fill),
            )
        )
        .align_items(Alignment::Center).into()
    }
}


Guess you like

Origin blog.csdn.net/weixin_47754149/article/details/127271805