前言
本专栏是学习Rust的GUI库iced的合集,将介绍iced涉及的各个小部件分别介绍,最后会汇总为一个总的程序。
iced是RustGUI中比较强大的一个,目前处于发展中(即版本可能会改变),本专栏基于版本0.12.1.
概述
这是本专栏的第三篇,主要讲述下拉列表pick_list部件的使用,会结合实例来说明。
系列博文链接:
1、RustGUI学习(iced)之小部件(一):如何使用按钮和文本标签部件
2、RustGUI学习(iced)之小部件(二):如何使用滑动条部件
环境配置:
系统:windows
平台:visual studio code
语言:rust
库:iced
注:iced是一个受Elm启发而编写,适用于rust语言的跨平台的GUI库。
本篇内容:
1、pick_list
下拉列表部件
pick_list部件在iced中的定义如下:
/// Creates a new [`PickList`].
///
/// [`PickList`]: crate::PickList
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,
Renderer: core::text::Renderer,
Theme: pick_list::StyleSheet
+ scrollable::StyleSheet
+ overlay::menu::StyleSheet
+ container::StyleSheet,
<Theme as overlay::menu::StyleSheet>::Style:
From<<Theme as pick_list::StyleSheet>::Style>,
{
PickList::new(options, selected, on_selected)
}
其由PickList来创建:
/// A widget for selecting a single value from a list of options.
#[allow(missing_debug_implementations)]
pub struct PickList<
'a,
T,
L,
V,
Message,
Theme = crate::Theme,
Renderer = crate::Renderer,
> where
T: ToString + PartialEq + Clone,
L: Borrow<[T]> + 'a,
V: Borrow<T> + 'a,
Theme: StyleSheet,
Renderer: text::Renderer,
{
on_select: Box<dyn Fn(T) -> Message + 'a>,
on_open: Option<Message>,
on_close: Option<Message>,
options: L,
placeholder: Option<String>,
selected: Option<V>,
width: Length,
padding: Padding,
text_size: Option<Pixels>,
text_line_height: text::LineHeight,
text_shaping: text::Shaping,
font: Option<Renderer::Font>,
handle: Handle<Renderer::Font>,
style: Theme::Style,
}
我们来看以下pick_list涉及的参数或属性,首先是on_select,从其定义可知这是一个动态指针,在实际使用时,on_select表示当选择下拉列表时,会触发这个事件,其反馈当前选择的项。
然后是on_open和on_close两个属性,分别表示下拉列表在打开和关闭时返回的消息,因为通常来说,下拉列表并不太关注打开和关闭时的状态,所以此参数可以不设置,使用默认即可。
然后是options参数,顾名思义,此参数即是下拉列表部件的下拉选项,从其定义来说,是一个借用类型且具有相同的生命周期(与借用的数组一致)。在实际使用来说,此处应该是一个枚举数组。
placeholder参数比较简单,是一个枚举字符,实际表示下拉列表没有选项时显示的一个提示文字。如下:
接下来是selected,其定义也是枚举类型,但枚举的参数是借用类型,且具有借用数组一样的生命周期。此参数是实时显示选择的下拉项。需要在update函数里更新,否则下拉列表选择后并不会更新UI。
width、padding、text_size、text_line_height、text_shaping、font这些都是一些基本参数,比较简单,主要设置一些尺寸或者文字。
handle是下拉列表右侧的箭头的属性,默认是箭头,可以修改为其他预设,如:
修改了箭头的尺寸,显示如上,可以看到和默认的有明显区别。除了箭头外,还可以使用自定义侧icon来替换,只不过需要自行设计。如下:
使用字符作为handle,但通常来说,下拉列表常见的都是箭头。
再来看最后一个属性,style,和之前的部件一样,style用于设计下拉列表部件的外观,其在iced中的定义如下:
/// A set of rules that dictate the style of a container.
pub trait StyleSheet {
/// The supported style of the [`StyleSheet`].
type Style: Default + Clone;
/// Produces the active [`Appearance`] of a pick list.
fn active(&self, style: &<Self as StyleSheet>::Style) -> Appearance;
/// Produces the hovered [`Appearance`] of a pick list.
fn hovered(&self, style: &<Self as StyleSheet>::Style) -> Appearance;
}
其中,Appearance定义如下:
/// The appearance of a pick list.
#[derive(Debug, Clone, Copy)]
pub struct Appearance {
/// The text [`Color`] of the pick list.
pub text_color: Color,
/// The placeholder [`Color`] of the pick list.
pub placeholder_color: Color,
/// The handle [`Color`] of the pick list.
pub handle_color: Color,
/// The [`Background`] of the pick list.
pub background: Background,
/// The [`Border`] of the pick list.
pub border: Border,
}
可以看到,pick_list部件的Appearance有五个可设置的属性,其中,text_color是文字颜色,placeholder_color是空闲时的文字颜色,handle_color顾名思义是右侧箭头的颜色,background指背景设置,而border是边框设置。
下面我们设置一下自定义pick_list,首先新建一个pick_list的外观结构体:
struct MyPickListStyle;
然后对结构体实现StyleSheet:
impl pick_list::StyleSheet for MyPickListStyle {
type Style = Theme;
//激活时外观
fn active(&self, style: &Self::Style) -> pick_list::Appearance {
pick_list::Appearance {
text_color:Color::from_rgb8(142, 204, 240),
placeholder_color:Color::BLACK,
handle_color:Color::from_rgb8(18, 27, 155),
background:Background::Color(Color::from_rgb8(215, 217, 249)),
border:Border{color:Color::BLACK,width:1.0,radius:[3.0;4].into()},
}
}
fn hovered(&self, style: &Self::Style) -> pick_list::Appearance {
pick_list::Appearance {
text_color:Color::from_rgb8(76, 87, 240),
placeholder_color:Color::BLACK,
handle_color:Color::from_rgb8(18, 27, 155),
background:Background::Color(Color::from_rgb8(215, 217, 249)),
border:Border { color: Color::BLACK, width: 1.0, radius: [3.0;4].into() },
}
}
}
需要注意的是,pick_list的动态样式有些特别,看一下官方定义:
/// The style of a pick list.
#[derive(Clone, Default)]
pub enum PickList {
/// The default style.
#[default]
Default,
/// A custom style.
Custom(
Rc<dyn pick_list::StyleSheet<Style = Theme>>,
Rc<dyn menu::StyleSheet<Style = Theme>>,
),
}
可以看到,其Custom选项有2个参数,除了pick_list样式外,还要求了menu样式,虽然不明白作者为什么这么设置,但如果要实现pick_list的自定义样式,我们必须为其添加第二个参数即menu部件的动态样式,所以我们还需要新建menu样式结构体:
struct MyMenuStyle;
然后设置StyleSheet:
impl menu::StyleSheet for MyMenuStyle {
type Style = Theme;
fn appearance(&self, style: &Self::Style) -> menu::Appearance {
menu::Appearance{
text_color:Color::BLACK,
background:Background::Color(Color::from_rgb8(215, 217, 249)),
border:Border { color: Color::BLACK, width: 1.0, radius: [3.0;4].into() },
selected_text_color:Color::BLACK,
selected_background:Background::Color(Color::from_rgb8(180, 184, 248)),
}
}
}
当自定义样式设置好后,就可以调用了:
let style9=theme::PickList::Custom(Rc::new(MyPickListStyle),Rc::new(MyMenuStyle));
然后设置pick_list的style属性即可,我们来看一下实际效果:
active:
hovered:
可以看到,自定义样式还是非常明显的不同的。
好了,关于属性的设置就说到这,接下来说一下options、selected、on_select这三个选项在实际中如何设置:
先来看一下三个参数的定义:
options: L,
其中,L为:
L: Borrow<[T]> + 'a,
T:
T: ToString + PartialEq + Clone,
此处,T是泛型,官方定义加了约束,T是可以复制且能转为字符并且可比较的类型。而L是T的借用,且生命周期一致。
selected: Option<V>,
selected是枚举,枚举的类型是V:
V: Borrow<T> + 'a,
与options不同的是,V借用T,而L借用的是数组[T].
on_select: Box<dyn Fn(T) -> Message + 'a>,
on_select是和事件绑定的,是消息的触发,它被定义为一个指针,指向的是pick_list的选项,并接收反馈的消息内容。综合起来,Box<dyn Fn(T) -> Message + 'a> 表示一个指向实现了Fn(T) -> Message trait的对象的堆上分配的智能指针,且这个对象的生命周期至少为’a。
以上是关于官方定义的解释,下面我们来看一下实际应用。
pick_list(&Item::ALL[..],self.selected_item,Message::ItemSelected)
此处,&Item::ALL[…]对应的是options,selected对应的是self.selected_item,而on_select对应的是Message::ItemSelected。
分别来看一下各个变量是如何创建的。
先创建了枚举Item,此处为其添加了Default特性。
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
enum Item{
#[default]
Rust,
Elm,
Ruby,
Haskell,
C,
JS,
Other,
}
然后,Item作为pick_list的选项的数据,其必须符合定义时泛型T的定义:
T: ToString + PartialEq + Clone,
T可以转为字符,所以,我们需要为Item实现Display特性:
impl std::fmt::Display for Item{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f,"{}",match self{
Item::Rust => "Rust",
Item::Elm => "Elm",
Item::Ruby => "Ruby",
Item::Haskell => "Haskell",
Item::C => "C",
Item::JS => "Javascript",
Item::Other => "Other",
})
}
}
Display可以很方便的设置参数转为字符的格式化设置。
而为了使Item的选项能够作为数组[T]传入options,需要将Item实现数组定义:
impl Item{
const ALL:[Item;7]=[
Item::Rust,
Item::Elm,
Item::Ruby,
Item::Haskell,
Item::C,
Item::JS,
Item::Other,
];
}
此处定义了一个常量ALL,ALL包含了Item的所有项,且是数组。
而selected的参数selected_item 则定义在结构体中:
struct Example{
value:i64,
value_sld:f32,
value_sld2:f32,
default:f32,
step:f32,
shift_step:f32,
selected_item:Option<Item>,
}
注意selected_item的类型是枚举。
而on_select的参数则是消息:
#[derive(Debug,Clone,Copy)]
enum Message{
Clicked,
SliderChanged(f32),
ItemSelected(Item),
}
这样一来,Item中定义的项,将会按照Display设置的显示格式,显示在下拉列表中:
当我们选择其中一项时,会触发on_select,此时,选中的项的内容将作为消息传递给Message::ItemSelected(Item),其中item就是选中项。
我们只需要在update函数中处理消息即可:
fn update(&mut self,message:Message){
match message{
Message::Clicked => {
self.value +=1;
}
Message::SliderChanged(value)=>{
self.value_sld=value;
}
Message::ItemSelected(item)=>{
self.selected_item=Some(item);
println!("{:?}",item);
}
}
}
可以看到,我们将选中项作为值赋给了变量selected_item,也就是只要我们选择任何项,selected_item都会跟随改变,也就是下拉列表选择完成后显示的就是我们选择的项。
我们也可以将selected_item 赋予其他部件,如text,这样我们选择什么项,就可以在UI上显示出来:
综上,我们已经基本了解了pick_list作为小部件是如何定义和使用的,其属性和参数又是如何设置的。
下面看一下动态演示: