赞
踩
前言
本专栏是Rust实例应用。
环境配置
平台:windows
软件:vscode
语言:rust
库:iced、iced_aw
概述
本篇构建了这样的一个实例,可以动态修改UI的主题,通过菜单栏来选择预设的自定义主题和官方主题,以及实时修改rgba色彩来动态调整主题色。
UI图示:
如上图,本实例是一个简单的应用,是利用菜单栏来选择窗口的主题色,可以选择预设值,也可以选择自定义值,还可以动态调整:
本篇内容:
1、动态调整主题色
本篇涉及到的crate有iced、iced_aw、image、rfd等,详细看toml文件:
[package]
name = "img-convert"
version = "0.1.0"
edition = "2021"
[dependencies]
iced={version ="0.12.1",features = ["svg","canvas","image","multi-window"]}
iced_widget={version = "0.12.3"}
iced_aw={version = "0.9.3",features = ["cupertino"]}
image={version = "0.25.1",features = []}
rfd={version ="0.14.1"}
文件夹结构:
其中img文件夹提供了UI所需的各种图片:
本篇所涉及的内容,有一些是公用的,如菜单栏设置、样式设置等,本篇会讲的详细些,后续篇章涉及这些基础内容,就不再赘述。
先说菜单栏的构建的,关于菜单栏的使用,在之前的部件介绍专栏已经介绍过:
RustGUI学习(iced/iced_aw)之扩展小部件(十六):如何使用菜单menu部件来创建菜单栏?
但之前只是简单说了如何使用menu,本篇将详细介绍菜单栏的构建函数。
如图,菜单的设置,是在一个单独的mod文件中,并没有放在main.rs中,主要是为了方便编写,如果都写在主函数中,显得太拥挤了,也不方便查看。
在menuset.rs中,我们将构建以下函数:
我们来分别看一下,其中,create_menubar函数,顾名思义,是创建菜单栏主bar的函数,如下:
///
/// 创建一个menu bar
///
pub fn create_menubar<'a,Message,Theme,Renderer>(
menu:Vec<Item<'a,Message,Theme,Renderer>>,
style:iced_aw::style::menu_bar::MenuBarStyle,
)->MenuBar<'a,Message,Theme,Renderer>
where
Theme:iced_aw::menu::StyleSheet+iced_aw::style::menu_bar::StyleSheet,
Renderer:iced::advanced::Renderer,<Theme as iced_aw::menu::StyleSheet>::Style: From<MenuBarStyle>
{
MenuBar::new(menu).spacing(4.0).width(Length::Fill).height(40).padding(4)
.draw_path(menu::DrawPath::Backdrop)
.style(style)
}
通过传入的menu项,返回一个MenuBar部件,传入参数中还包括自定义的style。关于函数的具体代码就不赘述了,这在之前的部件专栏博文中有说明。
接下来说下菜单项的构建函数,有两种,一个是不带子项的,一个是带子项的。
menu无子项:
/// /// 创建一个menu无子项 /// pub fn menu_no_sub<'a,Message:'a,Theme,Renderer>( label:&'a str, msg:Message, )->Item<'a,Message,Theme,Renderer> where Theme:'a+iced_aw::menu::StyleSheet+button::StyleSheet+text::StyleSheet, Renderer: 'a+iced::advanced::Renderer+iced::advanced::text::Renderer, <Theme as iced_widget::button::StyleSheet>::Style: From<iced::theme::Button>,Message: std::clone::Clone { let btn:Button<Message,Theme,Renderer>=menubtn_normal(label, msg,176,Horizontal::Left); Item::new(btn) }
可以看到,很简单,就是返回一个Item项,其中菜单button的文本和消息为自定义输入。
menu有子项
/// /// 创建一个menu有子项 /// pub fn menu_with_sub<'a,Message:'a,Theme,Renderer>( label:&'a str, msg:Message, arrow:&'a str, sub:Vec<Item<'a,Message,Theme,Renderer>> )->Item<'a,Message,Theme,Renderer> where Theme:'a+iced_aw::menu::StyleSheet+button::StyleSheet+text::StyleSheet+svg::StyleSheet, Renderer: 'a+iced::advanced::Renderer+iced::advanced::text::Renderer+iced::advanced::svg::Renderer, Message: std::clone::Clone,<Theme as iced_widget::button::StyleSheet>::Style: From<iced::theme::Button> { let item=menubtn_arrow(label, msg, arrow,176); let menu=Menu::new(sub).max_width(180.0).spacing(5.0).offset(3.0); Item::with_menu(item, menu) }
带子项的menu稍微复杂些,其中Item的项包括了button以及一个箭头(右向,svg格式):
svg构成:
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1715601105402" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3106" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M256 102.4v819.2l512-409.6L256 102.4z" fill="#333333" p-id="3107"></path></svg>
也可以去一些提供向量图标的网站下载。
通常,菜单项使用的都是button加文字,因为菜单就是用来点击的,但是也可以使用其他部件,如滑动条,本篇为了方便动态调整颜色值,添加了slider构建的menu:
/// /// 创建一个menu无子项(slider样式) /// pub fn menu_no_sub_slider<'a,Message,Theme,Renderer>( label:&'a str, msg:impl Fn(f32) -> Message + 'a, slidervalue:f32, themecolor:Color, )->Item<'a,Message,Theme,Renderer> where Theme:'a+iced_aw::menu::StyleSheet+iced_widget::slider::StyleSheet+iced_widget::text::StyleSheet, Renderer: 'a+iced::advanced::Renderer+iced::advanced::text::Renderer, Message: std::clone::Clone+'a,<Theme as iced_widget::slider::StyleSheet>::Style: From<iced::theme::Slider>, <Theme as iced_widget::text::StyleSheet>::Style: From<iced::theme::Text> { let style1=iced::theme::Slider::Custom(Box::new(MySliderStyle{color:themecolor})); let mut r1=0.0; let mut g1=0.0; let mut b1=0.0; let mut a1=1.0; if themecolor.r<=0.1 || themecolor.g<=0.1 || themecolor.b<=0.1{ r1=0.9; g1=0.9; b1=0.9; }else if themecolor.r>=0.8 || themecolor.g>=0.8 || themecolor.b>=0.8{ r1=0.02; g1=0.02; b1=0.02; } else{ r1=themecolor.r; g1=themecolor.g; b1=themecolor.b; } let style2=iced::theme::Text::Color(Color::from_rgba(r1, g1, b1, a1)); let text0=text(label).width(20).size(12).style(style2); let slider=slider(0.0..=1.0,slidervalue, msg).step(0.01).width(100).style(style1); let text1=text(format!("{}",slidervalue)).width(30).size(12).style(style2); let row1=row![ text0, horizontal_space().height(10), slider, horizontal_space().height(10), text1 ].align_items(Alignment::Center).spacing(5); Item::new(row1) }
上面的函数,菜单项中添加的是slider,其中slider的样式可以根据UI的背景色调整而调整,主要是为了背景色调整过程菜单项的可视性。
还有两个函数是构建菜单项button的,之所以将button项单独使用函数构建,也是为了方便修改参数。这两个函数不再细说,但代码会在后面全部贴出来。
菜单项的构建就完成了,然后在主函数里调用菜单项:
let arrowsvg="..\\img-convert\\img\\arrow\\arrow_right_16.svg";
let wj_open=menuset::menu_no_sub("打开",Message::WJ(menuset::WJMessage::Open));
// let wj_new_mode1=menuset::menu_no_sub("格式1",Message::WJ(menuset::WJMessage::Open) );
// let wj_new_mode2=menuset::menu_no_sub("格式2",Message::WJ(menuset::WJMessage::Open) );
let wj_new=menuset::menu_no_sub("新建", Message::WJ(menuset::WJMessage::New));
let wj_save=menuset::menu_no_sub("保存",Message::WJ(menuset::WJMessage::Save));
let wj_close=menuset::menu_no_sub("关闭",Message::WJ(menuset::WJMessage::Close));
let wj_main=menuset::menu_main("文件", Message::WJ(menuset::WJMessage::WenJian),
通常,菜单项就是菜单栏中添加主菜单项,点击主菜单项,下拉子项,子项还可以附带子项,如上面的代码,是主菜单为“文件”的菜单项,下拉菜单为【打开、新建、保存、关闭】,很常见的对吧。
注意,上述代码中,调用的函数,其中menuset为mod,需要提前导入:
mod menuset;
以此类推,如果你要构建其他菜单项,按照样式填写即可,当然,其中消息需要自己构建,可以直接写在menuset模块中:
#[derive(Debug,Clone)]
pub enum WJMessage{
WenJian,
Open,
Close,
New,
Save,
}
注意要使用pub关键字,然后其他文件里调用。
如上图,主函数调用menuset的菜单项enum。
以上,菜单项的设置就完成了,样式如下:
示例只添加了三个主菜单项,如果你想要更多的,自行添加即可。
不过,需要注意的是,本文没有为菜单添加快捷键功能,这涉及到监控键盘的行为,会在后面的高级一点的示例中说到。
下面说下主题修改涉及的函数,即uitheme.rs。
本模块中,主要包含了一个是自定义预设值,T1、T2、T3,实现的函数是theme_name。
一个是动态调整主题色,实现的函数是get_color。
至于官方预设的颜色值,就无需多次重构,直接使用即可。
来看下自定义预设主题,其实现代码如下:
impl UITheme{ pub fn theme_name(&self) -> Theme{ match self{ UITheme::T1 => { let p1=Palette{ background:iced::Color::from_rgba(0.0, 0.65, 0.6, 0.7), text:Color::BLACK, primary:Color::BLACK, success:Color::BLACK, danger:Color::BLACK, }; let g1=Extended{ background:Background{ base:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, weak:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, strong:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, }, primary:Primary{ base:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, weak:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, strong:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, }, secondary:Secondary{ base:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, weak:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, strong:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, }, success:Success{ base:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, weak:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, strong:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, }, danger:Danger{ base:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, weak:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, strong:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, }, is_dark:false, }; let cus1=Arc::new(iced::theme::Custom::new("t1".to_string(),p1)); let cus2=Arc::new(iced::theme::Custom::with_fn("t1".to_string(), p1, |_|g1)); iced::Theme::Custom(cus1) } UITheme::T2 => { let p1=Palette{ //background:iced::Color::from_rgba(0.0, 0.85, 0.8, 0.8), background:Color::from_linear_rgba(0.0, 0.5, 0.6, 0.8), text:Color::BLACK, primary:Color::BLACK, success:Color::BLACK, danger:Color::BLACK, }; let cus1=Arc::new(iced::theme::Custom::new("t1".to_string(),p1)); iced::Theme::Custom(cus1) }, UITheme::T3 => { let p1=Palette{ background:iced::Color::from_rgba(0.0, 0.85, 0.8, 0.8), text:Color::BLACK, primary:Color::BLACK, success:Color::BLACK, danger:Color::BLACK, }; let cus1=Arc::new(iced::theme::Custom::new("t1".to_string(),p1)); iced::Theme::Custom(cus1) }, } } }
需要注意的是,本文的代码只是为了演示,一些参数设置只要达到效果即可,所以,并非所有涉及的参数都做了修改。如上代码,我们只是简单的构建了三个自定义样式T1、T2、T3,然后为每个主题都设置自己的颜色,通过菜单选择相应的主题时,获得自定义的颜色效果:
再来看下动态调整实现:
pub struct UIThemeColor{ pub color:Color, } impl UIThemeColor { pub fn get_color(&self) -> Theme { let p1=Palette{ background:self.color, text:Color::BLACK, primary:Color::BLACK, success:Color::BLACK, danger:Color::BLACK, }; let cus1=Arc::new(iced::theme::Custom::new("t1".to_string(),p1)); iced::Theme::Custom(cus1) } }
代码不难,我们构建一个结构体,包含参数color,用于自定义输入,然后实现函数get_color,用于动态调整时,传入外面的颜色值,函数内部使用此color值,来构建自定义的Theme,以此实现主题色的动态调整:
menuset.rs
use iced::advanced::widget::Text; use iced::alignment::Horizontal; use iced::widget::{button, text,row,svg,horizontal_space,slider,}; use iced::{Alignment, Background, Border, Color, Length, Theme}; use iced_aw::menu::{self, Item, Menu, MenuBar}; use iced::widget::{Button, Row}; use iced_aw::style::menu_bar::MenuBarStyle; use iced::widget::slider::{Rail,HandleShape}; #[derive(Debug,Clone)] pub enum WJMessage{ WenJian, Open, Close, New, Save, } #[derive(Debug,Clone)] pub enum GJMessage{ GongJv, T1, T2, T3, } #[derive(Debug,Clone)] pub enum GYMessage{ GuanYu, GY, BZ, GX, } /// /// 创建一个menu bar /// pub fn create_menubar<'a,Message,Theme,Renderer>( menu:Vec<Item<'a,Message,Theme,Renderer>>, style:iced_aw::style::menu_bar::MenuBarStyle, )->MenuBar<'a,Message,Theme,Renderer> where Theme:iced_aw::menu::StyleSheet+iced_aw::style::menu_bar::StyleSheet, Renderer:iced::advanced::Renderer,<Theme as iced_aw::menu::StyleSheet>::Style: From<MenuBarStyle> { MenuBar::new(menu).spacing(4.0).width(Length::Fill).height(40).padding(4) .draw_path(menu::DrawPath::Backdrop) .style(style) } /// /// 创建一个menu主项 /// pub fn menu_main<'a,Message:'a,Theme,Renderer>( label:&'a str, msg:Message, sub:Vec<Item<'a,Message,Theme,Renderer>> )->Item<'a,Message,Theme,Renderer> where Theme:'a+iced_aw::menu::StyleSheet+button::StyleSheet+text::StyleSheet+svg::StyleSheet, Renderer: 'a+iced::advanced::Renderer+iced::advanced::text::Renderer+iced::advanced::svg::Renderer, Message: std::clone::Clone, <Theme as iced_widget::button::StyleSheet>::Style: From<iced::theme::Button> { let item: Button<Message, Theme, Renderer>=menubtn_normal(label, msg,60,Horizontal::Center); let menu: Menu<Message, Theme, Renderer>=Menu::new(sub).max_width(180.0).spacing(5.0).offset(5.0); Item::with_menu(item, menu) } /// /// 创建一个menu无子项(slider样式) /// pub fn menu_no_sub_slider<'a,Message,Theme,Renderer>( label:&'a str, msg:impl Fn(f32) -> Message + 'a, slidervalue:f32, themecolor:Color, )->Item<'a,Message,Theme,Renderer> where Theme:'a+iced_aw::menu::StyleSheet+iced_widget::slider::StyleSheet+iced_widget::text::StyleSheet, Renderer: 'a+iced::advanced::Renderer+iced::advanced::text::Renderer, Message: std::clone::Clone+'a,<Theme as iced_widget::slider::StyleSheet>::Style: From<iced::theme::Slider>, <Theme as iced_widget::text::StyleSheet>::Style: From<iced::theme::Text> { let style1=iced::theme::Slider::Custom(Box::new(MySliderStyle{color:themecolor})); let mut r1=0.0; let mut g1=0.0; let mut b1=0.0; let mut a1=1.0; if themecolor.r<=0.1 || themecolor.g<=0.1 || themecolor.b<=0.1{ r1=0.9; g1=0.9; b1=0.9; }else if themecolor.r>=0.8 || themecolor.g>=0.8 || themecolor.b>=0.8{ r1=0.02; g1=0.02; b1=0.02; } else{ r1=themecolor.r; g1=themecolor.g; b1=themecolor.b; } let style2=iced::theme::Text::Color(Color::from_rgba(r1, g1, b1, a1)); let text0=text(label).width(20).size(12).style(style2); let slider=slider(0.0..=1.0,slidervalue, msg).step(0.01).width(100).style(style1); let text1=text(format!("{}",slidervalue)).width(30).size(12).style(style2); let row1=row![ text0, horizontal_space().height(10), slider, horizontal_space().height(10), text1 ].align_items(Alignment::Center).spacing(5); Item::new(row1) } /// /// 创建一个menu无子项 /// pub fn menu_no_sub<'a,Message:'a,Theme,Renderer>( label:&'a str, msg:Message, )->Item<'a,Message,Theme,Renderer> where Theme:'a+iced_aw::menu::StyleSheet+button::StyleSheet+text::StyleSheet, Renderer: 'a+iced::advanced::Renderer+iced::advanced::text::Renderer, <Theme as iced_widget::button::StyleSheet>::Style: From<iced::theme::Button>,Message: std::clone::Clone { let btn:Button<Message,Theme,Renderer>=menubtn_normal(label, msg,176,Horizontal::Left); Item::new(btn) } /// /// 创建一个menu有子项 /// pub fn menu_with_sub<'a,Message:'a,Theme,Renderer>( label:&'a str, msg:Message, arrow:&'a str, sub:Vec<Item<'a,Message,Theme,Renderer>> )->Item<'a,Message,Theme,Renderer> where Theme:'a+iced_aw::menu::StyleSheet+button::StyleSheet+text::StyleSheet+svg::StyleSheet, Renderer: 'a+iced::advanced::Renderer+iced::advanced::text::Renderer+iced::advanced::svg::Renderer, Message: std::clone::Clone,<Theme as iced_widget::button::StyleSheet>::Style: From<iced::theme::Button> { let item=menubtn_arrow(label, msg, arrow,176); let menu=Menu::new(sub).max_width(180.0).spacing(5.0).offset(3.0); Item::with_menu(item, menu) } /// /// 创建一个menu按钮无箭头 /// fn menubtn_normal<'a,Message,Theme,Renderer>( label:&'a str, msg:Message, width:u16, align:Horizontal, )->Button<'a,Message,Theme,Renderer> where Theme:'a+iced_aw::menu::StyleSheet+button::StyleSheet+text::StyleSheet, Renderer: 'a+iced::advanced::Renderer+iced::advanced::text::Renderer, <Theme as iced_widget::button::StyleSheet>::Style: From<iced::theme::Button> { let tt=text(label).size(15) .vertical_alignment(iced::alignment::Vertical::Center) .horizontal_alignment(align); button(tt).width(width).height(30) .on_press(msg) .style(iced::theme::Button::Custom(Box::new(MenuButtonStyle))) } /// /// 创建一个menu按钮有箭头 /// fn menubtn_arrow<'a,Message:'a,Theme,Renderer>( label:&'a str, msg:Message, arrow:&'a str, width:u16, )->Button<'a,Message,Theme,Renderer> where Theme:'a+iced_aw::menu::StyleSheet+button::StyleSheet+text::StyleSheet+svg::StyleSheet, Renderer: 'a+iced::advanced::Renderer+iced::advanced::text::Renderer+iced::advanced::svg::Renderer, <Theme as iced_widget::button::StyleSheet>::Style: From<iced::theme::Button> { let tt:Text<Theme,Renderer>=text(label).size(14) .vertical_alignment(iced::alignment::Vertical::Center) .horizontal_alignment(iced::alignment::Horizontal::Center); let arrow_svg=svg(svg::Handle::from_path(arrow)).width(16).height(16); let rr:Row<Message,Theme,Renderer>=row![ tt, horizontal_space().height(20), arrow_svg, ].spacing(10) .align_items(iced::Alignment::Center) .into(); button(rr).width(width).height(30) .on_press(msg) .style(iced::theme::Button::Custom(Box::new(MenuButtonStyle))) } struct MenuButtonStyle; impl button::StyleSheet for MenuButtonStyle{ type Style = iced::Theme; fn active(&self, style: &Self::Style) -> button::Appearance { let backcolor=Color::from_rgb8(167, 218, 220); button::Appearance { text_color:Color::BLACK, background:Some(Background::Color(backcolor)), border:Border{ radius:[4.0;4].into(), ..Default::default() }, ..Default::default() } } fn pressed(&self, style: &Self::Style) -> button::Appearance { let backcolor=Color::from_rgb8(16, 137, 177); button::Appearance { text_color:Color::BLACK, background:Some(Background::Color(backcolor)), border:Border{ radius:[4.0;4].into(), ..Default::default() }, ..Default::default() } } fn hovered(&self, style: &Self::Style) -> button::Appearance { let backcolor=Color::from_rgb8(0, 164, 230); button::Appearance { text_color:Color::BLACK, background:Some(Background::Color(backcolor)), border:Border{ radius:[4.0;4].into(), ..Default::default() }, ..Default::default() } } fn disabled(&self, style: &Self::Style) -> button::Appearance { let backcolor=Color::from_rgb8(0, 0, 100); button::Appearance { text_color:Color::BLACK, background:Some(Background::Color(backcolor)), border:Border{ radius:[4.0;4].into(), ..Default::default() }, ..Default::default() } } } pub struct MyMenuBarStyle{ pub backcolor:Color, } impl iced_aw::style::menu_bar::StyleSheet for MyMenuBarStyle { type Style = iced::Theme; fn appearance(&self, style: &Self::Style) -> iced_aw::style::menu_bar::Appearance { let r=self.backcolor.r; let g=self.backcolor.g; let b=self.backcolor.b; let a=self.backcolor.a; let color1=Color::from_rgba(r+0.2, g+0.2, b+0.2, a+0.1); iced_aw::style::menu_bar::Appearance { //bar_background:Background::Color(Color::from_rgb8(177, 244, 251)), bar_background:Background::Color(self.backcolor), menu_background:Background::Color(color1), ..Default::default() } } } struct MySliderStyle{ color:Color, } impl slider::StyleSheet for MySliderStyle { type Style = Theme; //激活时外观 fn active(&self, style: &Self::Style) -> slider::Appearance { let mut r1=0.0; let mut g1=0.0; let mut b1=0.0; let mut a1=0.0; if self.color.r<=0.1 && self.color.g<=0.1 && self.color.b<=0.1{ r1=0.5; g1=0.5; b1=0.5; }else if self.color.r>=0.9 || self.color.g>=0.9 || self.color.b>=0.9{ r1=0.2; g1=0.2; b1=0.2; } else{ r1=self.color.r; g1=self.color.g; b1=self.color.b; } if self.color.a<=0.1{ a1=0.7; }else{ a1=self.color.a; } slider::Appearance { rail: Rail{ colors:(Color::from_rgba(r1-0.2, g1-0.2, b1-0.2,a1+0.1),Color::from_rgba(r1+0.2, g1+0.2, b1+0.2,a1+0.1)), width:5.0, border_radius:[3.0;4].into(), }, handle: slider::Handle{ shape:HandleShape::Rectangle { width: 8, border_radius: [2.0;4].into() }, color:Color::from_rgba(r1+0.1, g1+0.1,b1+0.1,a1), border_width:1.0, border_color:Color::BLACK, } } } //悬停时外观 fn hovered(&self, style: &Self::Style) -> slider::Appearance { let r1=self.color.r; let g1=self.color.g; let b1=self.color.b; let a1=self.color.a; slider::Appearance { rail: Rail{ colors:(Color::from_rgba(r1+0.1, g1+0.1, b1+0.1,a1),Color::from_rgba(r1-0.1, g1-0.1, b1-0.1,a1)), width:5.0, border_radius:[3.0;4].into(), }, handle: slider::Handle{ shape:HandleShape::Rectangle { width: 8, border_radius: [2.0;4].into()}, color:Color::from_rgba(r1+0.2, g1+0.2,b1+0.2,a1), border_width:1.0, border_color:Color::BLACK, } } } //拖拽时外观 fn dragging(&self, style: &Self::Style) -> slider::Appearance { slider::Appearance { rail: Rail{ colors:(Color::from_rgb8(20, 48, 210),Color::from_rgb8(151, 155, 175)), width:5.0, border_radius:[3.0;4].into(), }, handle: slider::Handle{ shape:HandleShape::Rectangle { width: 8, border_radius: [2.0;4].into() }, color:Color::from_rgb8(13, 248,44), border_width:1.0, border_color:Color::BLACK, } } } }
uitheme.rs
use iced::{theme::{palette::{Background, Danger, Extended, Pair, Primary, Secondary, Success}, Palette}, Color,Theme}; use iced_aw::core::color; use std::sync::Arc; pub enum UITheme{ T1, T2, T3, } impl UITheme{ pub fn theme_name(&self) -> Theme{ match self{ UITheme::T1 => { let p1=Palette{ background:iced::Color::from_rgba(0.0, 0.65, 0.6, 0.7), text:Color::BLACK, primary:Color::BLACK, success:Color::BLACK, danger:Color::BLACK, }; let g1=Extended{ background:Background{ base:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, weak:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, strong:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, }, primary:Primary{ base:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, weak:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, strong:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, }, secondary:Secondary{ base:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, weak:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, strong:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, }, success:Success{ base:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, weak:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, strong:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, }, danger:Danger{ base:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, weak:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, strong:Pair{color:Color::from_rgba(0.1, 0.7, 0.7, 0.8),text:Color::BLACK}, }, is_dark:false, }; let cus1=Arc::new(iced::theme::Custom::new("t1".to_string(),p1)); let cus2=Arc::new(iced::theme::Custom::with_fn("t1".to_string(), p1, |_|g1)); iced::Theme::Custom(cus1) } UITheme::T2 => { let p1=Palette{ //background:iced::Color::from_rgba(0.0, 0.85, 0.8, 0.8), background:Color::from_linear_rgba(0.0, 0.5, 0.6, 0.8), text:Color::BLACK, primary:Color::BLACK, success:Color::BLACK, danger:Color::BLACK, }; let cus1=Arc::new(iced::theme::Custom::new("t1".to_string(),p1)); iced::Theme::Custom(cus1) }, UITheme::T3 => { let p1=Palette{ background:iced::Color::from_rgba(0.0, 0.85, 0.8, 0.8), text:Color::BLACK, primary:Color::BLACK, success:Color::BLACK, danger:Color::BLACK, }; let cus1=Arc::new(iced::theme::Custom::new("t1".to_string(),p1)); iced::Theme::Custom(cus1) }, } } } pub struct UIThemeColor{ pub color:Color, } impl UIThemeColor { pub fn get_color(&self) -> Theme { let p1=Palette{ background:self.color, text:Color::BLACK, primary:Color::BLACK, success:Color::BLACK, danger:Color::BLACK, }; let cus1=Arc::new(iced::theme::Custom::new("t1".to_string(),p1)); iced::Theme::Custom(cus1) } }
main.rs
#![allow(dead_code)] #![allow(overflowing_literals)] //cfg可以实现debug期间无视未使用、未导入等警告信息,但不影响发布后的警告 #![cfg_attr(debug_assertions, allow(dead_code, unused_imports,unused_variables,unused_mut ,unused_assignments))] use core::prelude; use iced::widget::{button, container, slider, text,svg,image,row,column}; use iced::window::{self,Icon}; use iced::{Application, Color, Command, Element, Point, Renderer, Settings, Size, Subscription}; use iced_aw::menu; use iced_widget::graphics::text::cosmic_text::rustybuzz::ttf_parser::gdef; use iced_widget::runtime::futures::event; use rfd::{FileDialog,MessageDialog}; extern crate image as eximage; mod menuset; mod uitheme; struct Example{ slider_value:f32, slider_value2:f32, slider_value3:f32, slider_value4:f32, theme:iced::Theme, } #[derive(Debug,Clone)] enum Message{ SliderChanged(f32), SliderChanged2(f32), SliderChanged3(f32), SliderChanged4(f32), WJ(menuset::WJMessage), GJ(menuset::GJMessage), GY(menuset::GYMessage), GFTheme(iced::Theme), MenuNone, EventOcu(iced::Event), } /// /// 图片转为icon格式 fn image_to_icon(file:& str,w:u32,h:u32,) -> Icon { let img=eximage::open(file); let img_path=match img{ Ok(path)=>path, Err(e)=>panic!("{}",e), }; let img_file=img_path.to_rgba8(); let icon=iced::window::icon::from_rgba(img_file.to_vec(), w, h); let icon2=match icon{ Ok(iconfile)=>iconfile, Err(e)=>panic!("{}",e), }; icon2 } fn main() -> iced::Result { //Example::run(Settings::default()) let myfont="微软雅黑"; let icon=image_to_icon("../img-convert/img/icon1.png", 500, 500); Example::run(Settings{ window:window::Settings{ size:Size{width:800.0,height:600.0}, position:window::Position::Specific(Point{x:100.0,y:40.0}), icon:Some(icon), ..Default::default() }, default_font:iced::Font{ family:iced::font::Family::Name(myfont), ..Default::default() }, ..Default::default() }) } async fn load() -> Result<(),String> { Ok(()) } impl Application for Example { type Executor =iced::executor::Default; type Message =Message; type Theme = iced::Theme; type Flags = (); fn new(flags: Self::Flags) -> (Self, Command<Self::Message>) { (Self{ slider_value:0.5, slider_value2:0.5, slider_value3:0.5, slider_value4:0.8, theme:iced::Theme::Light, }, Command::none() )} fn title(&self) -> String { String::from("图片格式转换器") } fn theme(&self) -> Self::Theme { self.theme.clone() } fn update(&mut self, message: Self::Message) -> Command<Self::Message> { match message{ Message::SliderChanged(value)=>{ self.slider_value=value; let color1=Color::from_rgba(self.slider_value, self.slider_value2,self.slider_value3, self.slider_value4); self.theme=uitheme::UIThemeColor::get_color(&uitheme::UIThemeColor{color:color1}); } Message::SliderChanged2(value)=>{ self.slider_value2=value; let color1=Color::from_rgba(self.slider_value, self.slider_value2,self.slider_value3, self.slider_value4); self.theme=uitheme::UIThemeColor::get_color(&uitheme::UIThemeColor{color:color1}); } Message::SliderChanged3(value)=>{ self.slider_value3=value; let color1=Color::from_rgba(self.slider_value, self.slider_value2,self.slider_value3, self.slider_value4); self.theme=uitheme::UIThemeColor::get_color(&uitheme::UIThemeColor{color:color1}); } Message::SliderChanged4(value)=>{ self.slider_value4=value; let color1=Color::from_rgba(self.slider_value, self.slider_value2,self.slider_value3, self.slider_value4); self.theme=uitheme::UIThemeColor::get_color(&uitheme::UIThemeColor{color:color1}); } Message::WJ(msg)=>{ match msg{ menuset::WJMessage::New=>{ println!("new"); } menuset::WJMessage::Open=>{ println!("open") } menuset::WJMessage::Close=>{ return window::close(window::Id::MAIN) } menuset::WJMessage::Save=>{ println!("save") } menuset::WJMessage::WenJian=>{ println!("wenjian") } } } Message::GJ(msg)=>{ match msg{ menuset::GJMessage::GongJv=>{ println!("gongjv"); } menuset::GJMessage::T1=>{ println!("t1"); self.theme=uitheme::UITheme::theme_name(&uitheme::UITheme::T1); self.slider_value=self.theme.clone().palette().background.r; self.slider_value2=self.theme.clone().palette().background.g; self.slider_value3=self.theme.clone().palette().background.b; self.slider_value4=self.theme.clone().palette().background.a; } menuset::GJMessage::T2=>{ println!("t2"); //self.theme=iced::Theme::Custom(Arc::new()); let tt=uitheme::UITheme::theme_name(&uitheme::UITheme::T2); self.theme=tt; self.slider_value=self.theme.clone().palette().background.r; self.slider_value2=self.theme.clone().palette().background.g; self.slider_value3=self.theme.clone().palette().background.b; self.slider_value4=self.theme.clone().palette().background.a; } menuset::GJMessage::T3=>{ println!("t3"); self.theme=uitheme::UITheme::theme_name(&uitheme::UITheme::T3); self.slider_value=self.theme.clone().palette().background.r; self.slider_value2=self.theme.clone().palette().background.g; self.slider_value3=self.theme.clone().palette().background.b; self.slider_value4=self.theme.clone().palette().background.a; } } } Message::GY(msg)=>{ match msg{ menuset::GYMessage::GuanYu=>{ println!("guanyu"); } menuset::GYMessage::GY=>{ println!("gy"); let res=rfd::MessageDialog::new() .set_title("关于") .set_level(rfd::MessageLevel::Info) .set_description("本软件是基于rust和iced的图像处理软件") .set_buttons(rfd::MessageButtons::Ok) .show(); } menuset::GYMessage::BZ=>{ println!("bz") } menuset::GYMessage::GX=>{ println!("gx") } } } Message::GFTheme(gftheme)=>{ self.theme=gftheme; self.slider_value=self.theme.clone().palette().background.r; self.slider_value2=self.theme.clone().palette().background.g; self.slider_value3=self.theme.clone().palette().background.b; self.slider_value4=self.theme.clone().palette().background.a; } Message::MenuNone=>{ } Message::EventOcu(event)=>{ } } Command::none() } fn subscription(&self) -> Subscription<Self::Message> { event::listen().map(Message::EventOcu) //Subscription::none() } fn view(&self) -> Element<'_, Self::Message, Self::Theme, crate::Renderer> { let themecolor=self.theme.palette().clone().background; //println!("{:?}",themecolor); //menu list set let arrowsvg="..\\img-convert\\img\\arrow\\arrow_right_16.svg"; let wj_open=menuset::menu_no_sub("打开",Message::WJ(menuset::WJMessage::Open)); // let wj_new_mode1=menuset::menu_no_sub("格式1",Message::WJ(menuset::WJMessage::Open) ); // let wj_new_mode2=menuset::menu_no_sub("格式2",Message::WJ(menuset::WJMessage::Open) ); let wj_new=menuset::menu_no_sub("新建", Message::WJ(menuset::WJMessage::New)); let wj_save=menuset::menu_no_sub("保存",Message::WJ(menuset::WJMessage::Save)); let wj_close=menuset::menu_no_sub("关闭",Message::WJ(menuset::WJMessage::Close)); let wj_main=menuset::menu_main("文件", Message::WJ(menuset::WJMessage::WenJian), vec![wj_open,wj_new,wj_save,wj_close]); let gj_title1_1=menuset::menu_no_sub("CatppuccinFrapp",Message::GFTheme(iced::Theme::CatppuccinFrappe)); let gj_title1_2=menuset::menu_no_sub("CatppuccinLatte",Message::GFTheme(iced::Theme::CatppuccinLatte)); let gj_title1_3=menuset::menu_no_sub("CatppuccinMacchiato",Message::GFTheme(iced::Theme::CatppuccinMacchiato)); let gj_title1_4=menuset::menu_no_sub("CatppuccinMocha",Message::GFTheme(iced::Theme::CatppuccinMocha)); let gj_title1_5=menuset::menu_no_sub("Dark",Message::GFTheme(iced::Theme::Dark)); let gj_title1_6=menuset::menu_no_sub("Light",Message::GFTheme(iced::Theme::Light)); let gj_title1_7=menuset::menu_no_sub("Dracula",Message::GFTheme(iced::Theme::Dracula)); let gj_title1_8=menuset::menu_no_sub("Nord",Message::GFTheme(iced::Theme::Nord)); let gj_title1_9=menuset::menu_no_sub("GruvboxDark",Message::GFTheme(iced::Theme::GruvboxDark)); let gj_title1_10=menuset::menu_no_sub("GruvboxLight",Message::GFTheme(iced::Theme::GruvboxLight)); let gj_title1=menuset::menu_with_sub("官方主题",Message::MenuNone,arrowsvg,vec![ gj_title1_1,gj_title1_2,gj_title1_3,gj_title1_4,gj_title1_5,gj_title1_6,gj_title1_7,gj_title1_8,gj_title1_9,gj_title1_10, ]); let gj_title2_1=menuset::menu_no_sub("主题1",Message::GJ(menuset::GJMessage::T1)); let gj_title2_2=menuset::menu_no_sub("主题2",Message::GJ(menuset::GJMessage::T2)); let gj_title2_3=menuset::menu_no_sub("主题3",Message::GJ(menuset::GJMessage::T3)); let gj_title2=menuset::menu_with_sub("自定义主题",Message::MenuNone,arrowsvg,vec![ gj_title2_1,gj_title2_2,gj_title2_3, ]); let gj_title3_1=menuset::menu_no_sub_slider("R", Message::SliderChanged, self.slider_value,themecolor); let gj_title3_2=menuset::menu_no_sub_slider("G", Message::SliderChanged2, self.slider_value2,themecolor); let gj_title3_3=menuset::menu_no_sub_slider("B", Message::SliderChanged3,self.slider_value3,themecolor); let gj_title3_4=menuset::menu_no_sub_slider("A", Message::SliderChanged4,self.slider_value4,themecolor); let gj_title3=menuset::menu_with_sub("动态调整",Message::MenuNone,arrowsvg,vec![ gj_title3_1,gj_title3_2,gj_title3_3,gj_title3_4, ]); let gj_main=menuset::menu_main("主题", Message::GJ(menuset::GJMessage::GongJv), vec![gj_title1,gj_title2,gj_title3]); let gy_gy=menuset::menu_no_sub("关于", Message::GY(menuset::GYMessage::GY)); let gy_bz=menuset::menu_no_sub("帮助", Message::GY(menuset::GYMessage::BZ)); let gy_gx=menuset::menu_no_sub("更新", Message::GY(menuset::GYMessage::GX)); let gy_main=menuset::menu_main("帮助", Message::GY(menuset::GYMessage::GuanYu), vec![gy_gy,gy_bz,gy_gx]); let barstyle=iced_aw::style::menu_bar::MenuBarStyle::Custom(Box::new(menuset::MyMenuBarStyle{ //backcolor:Color::from_rgba8(106, 195, 251, 0.8), backcolor:themecolor, })); let menu_bar=menuset::create_menubar(vec![ wj_main,gj_main,gy_main, ],barstyle ); let cont1=container(menu_bar ).padding(5); cont1.into() } }
rustGUI动态调整演示
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。