<rust><iced><GUI>iced的选择列表部件:pick_list

70 阅读4分钟

前言

本专栏是关于iced库的部件的介绍,iced库是基于rust的GUI库,可以创建基于rust的窗口程序。

关于iced

iced是跨平台的GUI框架,基于rust语言,它的架构受到Elm的启发。

发文平台

稀土掘金

概述

本文是专栏的第三篇,主要介绍iced的选择列表部件:pick_list。

1、pick_list部件构建

pub fn pick_list<'a, T, L, V, Message, Theme, Renderer>(
    options: L,
    selected: Option<V>,
    on_selected: impl Fn(T) -> Message + 'a,
) -> PickList<'a, T, L, V, Message, Theme, Renderer>
where
    T: ToString + PartialEq + Clone + 'a,
    L: Borrow<[T]> + 'a,
    V: Borrow<T> + 'a,
    Message: Clone,
    Theme: pick_list::Catalog + overlay::menu::Catalog,
    Renderer: core::text::Renderer,
{
    PickList::new(options, selected, on_selected)
}

实例创建pick_list使用PickList::new(options, selected, on_selected)

2、实际应用

需要先导入pick_list:

use iced::widget::{button, combo_box, text,pick_list,radio,checkbox};

我们可以这样添加一个pick_lick:

pick_list(
            HeroName::ALL, 
            self.heroname_fav.clone(), 
            Message::HeroNameSelected,
        )

如上,HeroName是options,self.heroname_fav是selected,Message::HeroNameSelected是on_selected。 以上对应参数需要提前创建:

#[derive(Debug, Clone)]
struct MyApp {
    heroname_fav:Option<HeroName>,
    heroname_select:String,
    showimgpath:String,
}

#[derive(Debug, Clone)]
enum Message {

    Null,
    HeroNameSelected(HeroName),
}

其中,HeroName我们在另一个模块中定义:

use std::fmt::Display;

#[derive(Debug,Clone,PartialEq)]
pub enum HeroName{
    Bingnv,
    JianSheng,
    QuanNeng,
    ShuiRen,
    XianZhi,
    XiaoNiu,
    XiaoXiao,
}
impl HeroName{
    pub const ALL:&'static [HeroName] = &[
        HeroName::Bingnv,
        HeroName::JianSheng,
        HeroName::QuanNeng,
        HeroName::ShuiRen,
        HeroName::XianZhi,
        HeroName::XiaoNiu,
        HeroName::XiaoXiao,

    ];
    pub fn display(&self) -> String {
        match self{
            HeroName::Bingnv => "冰女".to_string(),
            HeroName::JianSheng => "剑圣".to_string(),
            HeroName::QuanNeng => "全能".to_string(),
            HeroName::ShuiRen => "水人".to_string(),
            HeroName::XianZhi => "先知".to_string(),
            HeroName::XiaoNiu => "小牛".to_string(),
            HeroName::XiaoXiao => "小小".to_string(),
        }
    }
    pub fn getpath(&self)->String{
        match self{
            HeroName::Bingnv => "./public/冰女.jpg".to_string(),
            HeroName::JianSheng => "./public/剑圣.jpg".to_string(),
            HeroName::QuanNeng => "./public/全能.jpg".to_string(),
            HeroName::ShuiRen => "./public/水人.jpg".to_string(),
            HeroName::XianZhi => "./public/先知.jpg".to_string(),
            HeroName::XiaoNiu => "./public/小牛.jpg".to_string(),
            HeroName::XiaoXiao => "./public/小小.jpg".to_string(),
        }
    }
}
impl Display for HeroName{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self{
            HeroName::Bingnv => write!(f, "冰女"),
            HeroName::JianSheng => write!(f, "剑圣"),
            HeroName::QuanNeng => write!(f, "全能"),
            HeroName::ShuiRen => write!(f, "水人"),
            HeroName::XianZhi => write!(f, "先知"),
            HeroName::XiaoNiu => write!(f, "小牛"),
            HeroName::XiaoXiao => write!(f, "小小"),
        }
    }
}

来看下最终UI效果:

屏幕截图 2025-05-03 103457.png

我们在本例中实现这样的功能,根据选择的英雄不同,在底下显示不同的英雄图像,英雄图形我们提前下载好,放到public文件夹下:

 pub fn getpath(&self)->String{
        match self{
            HeroName::Bingnv => "./public/冰女.jpg".to_string(),
            HeroName::JianSheng => "./public/剑圣.jpg".to_string(),
            HeroName::QuanNeng => "./public/全能.jpg".to_string(),
            HeroName::ShuiRen => "./public/水人.jpg".to_string(),
            HeroName::XianZhi => "./public/先知.jpg".to_string(),
            HeroName::XiaoNiu => "./public/小牛.jpg".to_string(),
            HeroName::XiaoXiao => "./public/小小.jpg".to_string(),
        }
    }

我们为HeroName定义了一个根据选项返回图片路径的函数,然后直接调用此函数即可:

Message::HeroNameSelected(heroname)=>{
                self.heroname_fav = Some(heroname.clone());
                self.heroname_select = heroname.display();
                self.showimgpath = heroname.getpath();
            }

我们来看一下实际演示:

iced部件pick_list的演示.gif

3、pick_list部件自定义样式

pick_list与combo_box有点像,样式也分为两项,一项是选择器,一项是menu。其源码中关于样式的定义:

/// Sets the style of the [`PickList`].
    #[must_use]
    pub fn style(mut self, style: impl Fn(&Theme, Status) -> Style + 'a) -> Self
    where
        <Theme as Catalog>::Class<'a>: From<StyleFn<'a, Theme>>,
    {
        self.class = (Box::new(style) as StyleFn<'a, Theme>).into();
        self
    }

    /// Sets the style of the [`Menu`].
    #[must_use]
    pub fn menu_style(
        mut self,
        style: impl Fn(&Theme) -> menu::Style + 'a,
    ) -> Self
    where
        <Theme as menu::Catalog>::Class<'a>: From<menu::StyleFn<'a, Theme>>,
    {
        self.menu_class = (Box::new(style) as menu::StyleFn<'a, Theme>).into();
        self
    }

我们创建一个自定义结构体:MyPickListStyle,然后为其添加函数如下:

struct MyPickListStyle;
impl MyPickListStyle {
    fn pickliststyle(t:&iced::Theme,s:iced::widget::pick_list::Status) -> iced::widget::pick_list::Style{
        match s {
            iced::widget::pick_list::Status::Active =>{
                iced::widget::pick_list::Style {
                    text_color:color!(0x504D4D),  //#ECCE73FF
                    placeholder_color:color!(0x504D4D),
                    handle_color:color!(0x504D4D),
                    background:iced::Background::Color(color!(0xECCE73)),
                    border:iced::Border {
                        color:color!(0x504D4D),
                        width:1.0,
                        radius:{1.0;4}.into(),
                    },
                }
            }
            iced::widget::pick_list::Status::Hovered =>{
                iced::widget::pick_list::Style {
                    text_color:color!(0x504D4D),  //#504D4DFF
                    placeholder_color:color!(0x504D4D),
                    handle_color:color!(0x504D4D),
                    background:iced::Background::Color(color!(0xECCE73)),
                    border:iced::Border {
                        color:color!(0x504D4D),
                        width:1.0,
                        radius:{1.0;4}.into(),
                    },
                }
            }
            iced::widget::pick_list::Status::Opened =>{
                iced::widget::pick_list::Style {
                    text_color:color!(0x504D4D),  //#504D4DFF
                    placeholder_color:color!(0x504D4D),
                    handle_color:color!(0x504D4D),
                    background:iced::Background::Color(color!(0xECCE73)),
                    border:iced::Border {
                        color:color!(0x504D4D),
                        width:1.0,
                        radius:{1.0;4}.into(),
                    },
                }
            }
        }
    }
     fn menustyle(t:&iced::Theme) -> iced::widget::overlay::menu::Style{
        iced::widget::overlay::menu::Style {
            background:iced::Background::Color(color!(0xECCE73)),
            text_color:color!(0x504D4D),
            border:iced::Border {
                color:color!(0x504D4D),
                width:1.0,
                radius:{1.0;4}.into(),
            },
            selected_text_color:color!(0x504D4D),
            selected_background:iced::Background::Color(color!(0x60C6F2)), //#60C6F2FF
        }
    }
}

然后我们来调用样式,如下:

pick_list(
            HeroName::ALL, 
            self.heroname_fav.clone(), 
            Message::HeroNameSelected,
        ).style(|t,s|MyPickListStyle::pickliststyle(t, s))
        .menu_style(|t|MyPickListStyle::menustyle(t))

看一下效果演示:

iced部件pick_list的演示2.gif