<Rust><iced>基于rust使用iced库构建GUI实例:动态改变主题色

前言

本专栏是Rust实例应用。

环境配置

平台:windows

软件:vscode

语言:rust

库:iced、iced_aw

概述

本篇构建了这样的一个实例,可以动态修改UI的主题,通过菜单栏来选择预设的自定义主题和官方主题,以及实时修改rgba色彩来动态调整主题色。

UI图示:

如上图,本实例是一个简单的应用,是利用菜单栏来选择窗口的主题色,可以选择预设值,也可以选择自定义值,还可以动态调整:

本篇内容:

1、动态调整主题色

程序结构介绍

本篇涉及到的crate有iced、iced_aw、image、rfd等,详细看toml文件:

rust 复制代码
[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的函数,如下:

rust 复制代码
///
/// 创建一个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无子项:

rust 复制代码
///
/// 创建一个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有子项

rust 复制代码
///
/// 创建一个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 复制代码
<?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:

rust 复制代码
///
/// 创建一个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项单独使用函数构建,也是为了方便修改参数。这两个函数不再细说,但代码会在后面全部贴出来。

菜单项的构建就完成了,然后在主函数里调用菜单项:

rust 复制代码
 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,需要提前导入:

rust 复制代码
mod menuset;

以此类推,如果你要构建其他菜单项,按照样式填写即可,当然,其中消息需要自己构建,可以直接写在menuset模块中:

rust 复制代码
#[derive(Debug,Clone)]
pub enum WJMessage{
    WenJian,
    Open,
    Close,
    New,
    Save,
}

注意要使用pub关键字,然后其他文件里调用。

如上图,主函数调用menuset的菜单项enum。

以上,菜单项的设置就完成了,样式如下:

示例只添加了三个主菜单项,如果你想要更多的,自行添加即可。

不过,需要注意的是,本文没有为菜单添加快捷键功能,这涉及到监控键盘的行为,会在后面的高级一点的示例中说到。

下面说下主题修改涉及的函数,即uitheme.rs

本模块中,主要包含了一个是自定义预设值,T1、T2、T3,实现的函数是theme_name。

一个是动态调整主题色,实现的函数是get_color。

至于官方预设的颜色值,就无需多次重构,直接使用即可。

来看下自定义预设主题,其实现代码如下:

rust 复制代码
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,然后为每个主题都设置自己的颜色,通过菜单选择相应的主题时,获得自定义的颜色效果:

再来看下动态调整实现:

rust 复制代码
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

rust 复制代码
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

rust 复制代码
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

rust 复制代码
#![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动态调整演示

相关推荐
MonkeyKing_sunyuhua32 分钟前
在 Visual Studio Code (VSCode) 中配置 MCP(Model Context Protocol)
ide·vscode·编辑器
smileNicky36 分钟前
在 VSCode 中运行 Vue.js 项目
ide·vue.js·vscode
明月看潮生1 小时前
青少年编程与数学 02-019 Rust 编程基础 16课题、包、单元包及模块
开发语言·青少年编程·rust·编程与数学
wowocpp1 小时前
spring boot Controller 和 RestController 的区别
java·spring boot·后端
后青春期的诗go1 小时前
基于Rust语言的Rocket框架和Sqlx库开发WebAPI项目记录(二)
开发语言·后端·rust·rocket框架
freellf1 小时前
go语言学习进阶
后端·学习·golang
Growthofnotes1 小时前
VSCode中Node.js 使用教程
ide·vscode·node.js
全栈派森3 小时前
云存储最佳实践
后端·python·程序人生·flask
CircleMouse3 小时前
基于 RedisTemplate 的分页缓存设计
java·开发语言·后端·spring·缓存
小妖6664 小时前
VScode 的插件本地更改后怎么生效
ide·vscode·编辑器