Rust UI工具库iced_widget的使用:轻量级跨平台GUI组件库,构建现代化用户界面

Rust UI工具库iced_widget的使用:轻量级跨平台GUI组件库,构建现代化用户界面

安装

在项目目录中运行以下Cargo命令:

cargo add iced_widget

或者在Cargo.toml中添加以下行:

iced_widget = "0.13.4"

基本使用示例

下面是一个使用iced_widget创建简单计数器的完整示例:

use iced::widget::{button, column, text};
use iced::{Alignment, Element, Sandbox, Settings};

// 定义应用状态
struct Counter {
    value: i32,
}

// 定义消息类型
#[derive(Debug, Clone)]
enum Message {
    Increment,
    Decrement,
}

// 实现Sandbox trait
impl Sandbox for Counter {
    type Message = Message;

    fn new() -> Self {
        Self { value: 0 }
    }

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

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

    fn view(&self) -> Element<Message> {
        // 使用iced_widget提供的组件构建UI
        column![
            // 显示当前计数值
            text(self.value).size(50),
            
            // 增加按钮
            button("+").on_press(Message::Increment),
            
            // 减少按钮
            button("-").on_press(Message::Decrement),
        ]
        .padding(20)
        .align_items(Alignment::Center)
        .into()
    }
}

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

更多UI组件示例

use iced::widget::{button, checkbox, column, container, progress_bar, radio, row, scrollable, slider, text, text_input};
use iced::{Alignment, Color, Element, Length, Sandbox, Settings};

#[derive(Debug, Clone)]
enum Message {
    TextInputChanged(String),
    CheckboxToggled(bool),
    RadioSelected(u8),
    SliderChanged(f32),
    ButtonPressed,
}

struct WidgetGallery {
    text_input_value: String,
    checkbox_value: bool,
    radio_value: u8,
    slider_value: f32,
}

impl Sandbox for WidgetGallery {
    type Message = Message;

    fn new() -> Self {
        Self {
            text_input_value: String::new(),
            checkbox_value: false,
            radio_value极简风格,适合现代UI设计

## 完整示例代码

下面是一个结合了多种iced_widget组件的完整示例,展示了一个简单的待办事项应用:

```rust
use iced::widget::{
    button, checkbox, column, container, row, scrollable, text, text_input,
};
use iced::{Alignment, Element, Length, Sandbox, Settings};

// 定义待办事项结构
#[derive(Debug, Clone)]
struct TodoItem {
    description: String,
    completed: bool,
}

// 定义应用状态
struct TodoApp {
    todos: Vec<TodoItem>,
    new_todo: String,
}

// 定义消息类型
#[derive(Debug, Clone)]
enum Message {
    NewTodoChanged(String),
    AddTodo,
    ToggleTodo(usize),
}

// 实现Sandbox trait
impl Sandbox for TodoApp {
    type Message = Message;

    fn new() -> Self {
        Self {
            todos: Vec::new(),
            new_todo: String::new(),
        }
    }

    fn title(&self) -> String {
        String::from("Todo App with Iced")
    }

    fn update(&mut self, message: Message) {
        match message {
            Message::NewTodoChanged(text) => {
                self.new_todo = text;
            }
            Message::AddTodo => {
                if !self.new_todo.is_empty() {
                    self.todos.push(TodoItem {
                        description: self.new_todo.clone(),
                        completed: false,
                    });
                    self.new_todo.clear();
                }
            }
            Message::ToggleTodo(index) => {
                if let Some(todo) = self.todos.get_mut(index) {
                    todo.completed = !todo.completed;
                }
            }
        }
    }

    fn view(&self) -> Element<Message> {
        // 构建输入区域
        let input_area = row![
            text_input("Add a new todo...", &self.new_todo)
                .on_input(Message::NewTodoChanged)
                .on_submit(Message::AddTodo)
                .padding(10),
            button("Add")
                .on_press(Message::AddTodo)
                .padding([10, 20]),
        ]
        .spacing(10)
        .align_items(Alignment::Center);

        // 构建待办事项列表
        let todos_list = if self.todos.is_empty() {
            column![text("No todos yet!").size(16)]
        } else {
            column(
                self.todos
                    .iter()
                    .enumerate()
                    .map(|(index, todo)| {
                        checkbox(
                            &todo.description,
                            todo.completed,
                            move |checked| {
                                if checked == todo.completed {
                                    None
                                } else {
                                    Some(Message::ToggleTodo(index))
                                }
                            },
                        )
                        .into()
                    })
                    .collect(),
            )
            .spacing(10)
        };

        // 组合所有组件
        let content = column![
            text("Todo App").size(24),
            input_area,
            scrollable(todos_list).height(Length::Fill),
            text(format!("Total: {} items", self.todos.len())).size(14),
        ]
        .spacing(20)
        .padding(20)
        .max_width(600);

        container(content)
            .width(Length::Fill)
            .height(Length::Fill)
            .center_x()
            .center_y()
            .into()
    }
}

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

关键特性

  1. 跨平台支持:可在Windows、macOS和Linux上运行
  2. 响应式布局:内置灵活的布局系统
  3. 主题支持:可自定义应用外观
  4. 轻量级:不依赖复杂的运行时环境
  5. Elm架构:采用单向数据流模式

iced_widget是iced生态系统中的组件库部分,提供了构建现代化用户界面所需的各种组件。它结合了Rust的性能优势与直观的UI开发体验,适合需要本地GUI应用的场景。


1 回复

Rust UI工具库iced_widget的使用指南

介绍

iced_widget是Rust生态中一个轻量级、跨平台的GUI组件库,它是iced框架的核心部件集合。iced采用Elm架构模式,专注于简单性、类型安全性和跨平台兼容性,非常适合构建现代化的用户界面。

主要特点:

  • 纯Rust实现,无外部依赖
  • 响应式布局
  • 跨平台支持(Windows/macOS/Linux/Web)
  • 简洁易学的API设计
  • 可定制的主题和样式

基本使用方法

添加依赖

首先在Cargo.toml中添加iced依赖:

[dependencies]
iced = { version = "0.10", features = ["widgets"] }

最小示例

use iced::widget::{button, column, text};
use iced::{Element, Sandbox, Settings};

struct Counter {
    value: i32,
}

#[derive(Debug, Clone, Copy)]
enum Message {
    Increment,
    Decrement,
}

impl Sandbox for Counter {
    type Message = Message;
    
    fn new() -> Self {
        Self { value: 0 }
    }
    
    fn title(&self) -> String {
        String::from("Counter - Iced")
    }
    
    fn update(&mut self, message: Message) {
        match message {
            Message::Increment => self.value += 1,
            Message::Decrement => self.value -= 1,
        }
    }
    
    fn view(&self) -> Element<Message> {
        column![
            button("+").on_press(Message::Increment),
            text(self.value).size(50),
            button("-").on_press(Message::Decrement),
        ]
        .padding(20)
        .into()
    }
}

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

常用组件示例

按钮(Button)

use iced::widget::button;

let my_button = button("Click me!")
    .on_press(Message::ButtonClicked)
    .padding(10);

文本(Text)

use iced::widget::text;

let title = text("Hello Iced!")
    .size(24)
    .style(iced::theme::Text::Color(iced::Color::from_rgb(1.0, 0.0, 0.0)));

输入框(TextInput)

use 极客d::widget::text_input;

let input = text_input("Placeholder", &self.input_value)
    .on_input(Message::InputChanged)
    .padding(10);

复选框(Checkbox)

use iced::widget::checkbox;

let checkbox = checkbox("Enable feature", self.is_checked)
    .on_toggle(Message::CheckboxToggled);

滑动条(Slider)

use iced::widget::slider;

let slider = slider(0..=100, self.slider_value, Message::SliderChanged);

下拉选择(ComboBox)

use iced::widget::{combo_box, text};

let options = ["Option 1", "Option 2", "Option 3"];
let combo = combo_box(&options, Some(&self.selected_option), Message::OptionSelected);

布局示例

垂直布局(Column)

use iced::widget::column;

let content = column![
    text("First item"),
    text("Second item"),
    text("Third item"),
]
.spacing(10)
.padding(20);

水平布局(Row)

use iced::widget::row;

let buttons = row![
    button("Cancel"),
    button("Save"),
]
.spacing(10);

滚动视图(Scrollable)

use iced::widget::{scrollable, text};

let scroll_content = scrollable(
    column![
        text("Long content item 1"),
        // ...更多内容
        text("Long content item 20"),
    ]
    .width(iced::Length::Fill)
);

主题定制

use iced::theme;

// 应用自定义主题
impl Application for MyApp {
    fn theme(&self) -> theme::Theme {
        theme::Theme::custom(theme::Palette {
            background: iced::Color::from_rgb(0.9, 0.9, 0.9),
            text: iced::Color::from_rgb(极客.1, 0.1, 0.1),
            primary: iced::Color::from_rgb(0.3, 0.6, 0.9),
            success: iced::Color::from_rgb(0.2, 0.7, 0.2),
            danger: iced::Color::from_rgb(0.8, 0.2, 0.2),
        })
    }
    // ...
}

高级功能

自定义组件

use iced::widget::{container, text};
use iced::{Color, Length, Theme};

struct CustomWidget;

impl CustomWidget {
    fn view<'a>(&self, message: &'a str) -> iced::Element<'a, Message> {
        container(text(message))
            .width(Length::Fill)
            .height(Length::Units(100))
            .center_x()
            .center_y()
            .style(theme::Container::Custom(Box::new(|theme| {
                container::Appearance {
                    background: Some(iced::Background::Color(Color::from_rgb(0.8, 0.8, 1.0))),
                    border_radius: 10.0,
                    border_width: 2.0,
                    border_color: Color::BLACK,
                    ..Default::default()
                }
            })))
            .into()
    }
}

异步操作

use iced::futures;
use iced::Command;

#[derive(Debug, Clone)]
enum Message {
    StartLoading,
    DataLoaded(Result<String, String>),
}

impl Sandbox for MyApp {
    // ...
    
    fn update(&mut self, message: Message) -> Command<Message> {
        match message {
            Message::StartLoading => Command::perform(
                async {
                    // 模拟异步操作
                    tokio::time::sleep(std::time::Duration::from_secs(2)).await;
                    Ok("Data loaded!".to_string())
                },
                Message::DataLoaded,
            ),
            Message::DataLoaded(result) => {
                self.data = result.unwrap_or_else(|e| e);
                Command::none()
            }
        }
    }
}

完整示例

下面是一个结合了多种组件的完整示例:

use iced::{
    widget::{button, checkbox, column, combo_box, row, slider, text, text_input},
    Element, Sandbox, Settings,
};

#[derive(Debug, Clone)]
enum Message {
    ButtonClicked,
    InputChanged(String),
    CheckboxToggled(bool),
    SliderChanged(u32),
    OptionSelected(String),
}

struct MyApp {
    input_value: String,
    is_checked: bool,
    slider_value: u32,
    selected_option: String,
}

impl Sandbox for MyApp {
    type Message = Message;

    fn new() -> Self {
        Self {
            input_value: String::new(),
            is_checked: false,
            slider_value: 50,
            selected_option: "Option 1".to_string(),
        }
    }

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

    fn update(&mut self, message: Message) {
        match message {
            Message::ButtonClicked => {
                println!("Button clicked!");
            }
            Message::InputChanged(value) => {
                self.input_value = value;
            }
            Message::CheckboxToggled(value) => {
                self.is_checked = value;
            }
            Message::SliderChanged(value) => {
                self.slider_value = value;
            }
            Message::OptionSelected(value) => {
                self.selected_option = value;
            }
        }
    }

    fn view(&self) -> Element<Message> {
        let options = ["Option 1", "Option 2", "Option 3"];

        column![
            text("Iced Widget Demo").size(24),
            text_input("Enter text...", &self.input_value)
                .on_input(Message::InputChanged)
                .padding(10),
            checkbox("Enable feature", self.is_checked)
                .on_toggle(Message::CheckboxToggled),
            slider(0..=100, self.slider_value, Message::SliderChanged),
            combo_box(&options, Some(&self.selected_option), Message::OptionSelected),
            row![
                button("Cancel").on_press(Message::ButtonClicked),
                button("Submit").on_press(Message::ButtonClicked),
            ]
            .spacing(10),
            text(format!("Slider value: {}", self.slider_value)),
            text(format!("Selected option: {}", self.selected_option)),
        ]
        .padding(20)
        .spacing(10)
        .into()
    }
}

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

总结

iced_widget提供了一套简洁而强大的组件库,使开发者能够轻松构建跨平台的GUI应用。通过组合基本组件和自定义样式,可以创建出既美观又功能丰富的用户界面。其响应式设计和Elm架构模式使得状态管理变得直观可靠。

对于更复杂的应用场景,iced还支持窗口定制、画布绘制、多线程等高级功能,是Rust生态中GUI开发的一个优秀选择。

回到顶部