FLTK-Rs 2
Trees
樹形結構,允許在樹中顯示項目,使用add方法發(fā)添加條目
use fltk::{prelude::*, *}; use fltk::enums::FrameType;fn main() {let a = celet mut win = window::Window::default().with_size(400, 300);let mut tree = tree::Tree::new(5,5,390,290,"");tree.set_root_label("Trceee_try");tree.add("Item 1");tree.add("Item 2");tree.add("Item 3");tree.add("Item 1/1");tree.add("Item 1/2");tree.add("Item 2/3");win.end();win.show();tree.set_callback(|tree| match tree.get_selected_items(){Some(s) => {for item in s{ println!("{} selected", tree.item_pathname(&item).unwrap());}}_ =>{},});a.run().unwrap(); }我們的樹組件初始化設定為僅單選。實際上我們可以通過一些設置設置樣式與多選
// 允許多選 tree.set_select_mode(tree::TreeSelect::Multi); // 實現(xiàn)或者虛線 tree.set_connector_style(tree::TreeConnectorStyle::Dotted); // 線條顏色 tree.set_connector_color(Color::from_rgb(127,187,135));Custom widgets
除了上述組件之外(我覺得夠用了), fltk-rs還允許我們自定義組件。我們自定義的組件可以是完全自己寫的~~(應該不會有人從零開寫吧。。)~~, 或者是從前面的組件衍生出來的。我們定義組件結構體的時候要定義兩個東西:衍生的組件,以及組件的數(shù)據(jù)的存儲。
eg.
struct MyCustomButton {// 我們的組件inner: widget::Widget,// 我們的數(shù)據(jù)num_clicks: Rc<RefCell<i32>>, }我們這里存儲數(shù)據(jù)使用的是Rc<RefCell<some>>>的結構。這個很重要, 因為我們在回調(callback)的時候我們需要移動這個數(shù)據(jù)到callback塊中。然而,我們訪問的時候卻又要改變他,所以所有權一直在我們GUI調用線程中。所以使用RC<RefCell的方式定義保證數(shù)據(jù)的可用性。舉個簡單的例子:
use std::rc::Rc; fn main() {let p = Rc::from(RefCell::from(10));// p,q指向同一塊地址let q = p.clone();// 拿走了p的所有權let h = (move ||{*p.borrow_mut() = 20;});h();// 但是q仍能訪問,而p也可以在其他塊中被更改println!("{:?}",q); } RefCell { value: 20 }所以我們的數(shù)據(jù)通過Rc 包裹 Refcell的方式去存儲。
定義好結構體后,我們就需要定義方法了。其中最重要的方法就是初始化
struct MyCustomButton {inner: widget::Widget,num_clicks: Rc<RefCell<i32>>, }impl MyCustomButton {// 我們的結構體// 我們定義的是一個圓形的按鈕,r是半徑pub fn new(radius: i32, label: &str) -> Self {// 我們的圓形站的尺寸的xy應該是直徑縮影成2,最中間let mut inner = widget::Widget::default().with_size(radius * 2, radius * 2).with_label(label).center_of_parent();// 設定幀樣式,后面我會介紹。其實就是一俺家樣式,樣式是庫里的沒啥問題inner.set_frame(enums::FrameType::OFlatBox);// 初始化我們的數(shù)據(jù)let num_clicks = 0;let num_clicks = Rc::from(RefCell::from(num_clicks));let clicks = num_clicks.clone();// 在按鈕內部寫字,協(xié)商按鈕是干啥的。這個draw有點意思inner.draw(|i| { // we need a draw implementationdraw::draw_box(i.frame(), i.x(), i.y(), i.w(), i.h(), i.color());draw::set_draw_color(enums::Color::Black); // for the textdraw::set_font(enums::Font::Helvetica, 14);draw::draw_text2("Our button", i.x(), i.y(), i.w(), i.h(), i.align());});// 這個ev 我不太清楚干嘛用的,不過是判斷點擊用的我倒是清楚inner.handle(move |i, ev| match ev {enums::Event::Push => {// 與我上面演示的變化原理相同*clicks.borrow_mut() += 1;// 這個call_back應該是默認為空i.do_callback(); // do the callback which we'll set using set_callback().true}_ => false,});Self {inner,num_clicks,}}// get the times our button was clicked// 這個功能是雞肋,應該,,pub fn num_clicks(&self) -> i32 {*self.num_clicks.borrow()} }最后我們想使用的話需要使用extren宏將其變成我們可以使用的組件:
widget_extends!(MyCustomButton, widget::Widget, inner);運行效果:
fn main() {let app = app::App::default().with_scheme(app::Scheme::Gleam);app::background(255, 255, 255); // make the background whitelet mut wind = window::Window::new(100, 100, 400, 300, "Hello from rust");let mut btn = MyCustomButton::new(50, "Click");btn.set_color(enums::Color::Cyan);btn.set_callback(|_| println!("Clicked"));wind.end();wind.show();app.run().unwrap();// print the number our button was clicked on exitprintln!("Our button was clicked {} times", btn.num_clicks()); } Clicked Our button was clicked 1 times至此,常用的組件就介紹完畢了
Dialogs
我之前以為對話框應該算是組件的一個部分,可是沒想到官方的數(shù)把它單列出來了啊,看來還是有點東西的
對話框可以分為兩種類型,原生的(操作系統(tǒng)自帶)的文件對話框(對于我Windows用戶就是win32對話框) 和FLTK自己的對話框。
關于本機的對話框倒是沒什么好說的,直接調用即可:
let mut dialog = dialog::NativeFileChooser::new(dialog::NativeFileChooserType::BrowseFile); dialog.show(); // 返回文件的路徑 println!("{:?}", dialog.filename());這里的對話框有六個接口可供選擇,單選文件,文件夾;多選文件,文件夾; 保存文件,文件夾路徑。我們還可以加文件類型過濾:
dialog.set_filter("*.{txt,rs,toml,py}");總之還是很簡單的,重點是FLTK自帶的對話框:
FLTK提供了幾個對話框方便我們構建應用:
-
Help 這個對話框可以顯示html文檔,當然也可以我們自己輸入顯示的文字
let mut help = dialog::HelpDialog::new(100, 100, 400, 300); help.set_value("<h2>Hello world</h2>"); // this takes html help.show(); // 在另一個窗口展示的時候掛起主應用 while help.shown() {app::wait(); } -
Message:
//dialog::message(坐標x,坐標y,"你要寫的話”);, 效果和下面一樣,只不過自定義了位置。不定義的話就是center默認 dialog::message_default("This is a Message\nA important message"); -
Choice:選擇對話框,不過只能由三個選項
// 設置彈窗標題 dialog::message_title_default("Save or Not"); // 選項的顯示是從左到右的0,1,2 let choice = dialog::choice2_default("Would you like to save", "No", "Yes", "Cancel"); // 這里的choice返回的是Some類型 println!("{:?}", choice);
? cancle 2, Yes 1, No 0
-
Input/ Password:
dialog::message_title_default("Input name"); let choice = dialog::input_default("Pleasr input your name", "None"); // 返回值依然是Some println!("{:?}", choice); //password就是input的銘文編程不可見的密文小點點。
Custom Dialog:
這些對話框并不是不好看,但是怎么說呢,上個時代的風格確實太濃了點,而且沒辦法更自由地定義一些東西。所以我們需要自定義對話框。對話框,本質是新開的窗口,話句話說我們定義窗口即可。
// 官網(wǎng)的小李子 use fltk::{app, button,enums::{Color, Font, FrameType},frame, group, input,prelude::*,window, };// 設置button的樣式 fn style_button(btn: &mut button::Button) {btn.set_color(Color::Cyan);btn.set_frame(FrameType::RFlatBox);btn.clear_visible_focus(); }pub fn show_dialog() -> MyDialog {MyDialog::default() }pub struct MyDialog {inp: input::Input, }impl MyDialog {pub fn default() -> Self {let mut win = window::Window::default().with_size(400, 100).with_label("My Dialog");// 這里原來是由用到布局工具的,但是我把它改了方便大家看win.set_color(Color::from_rgb(240, 240, 240));let mut inp = input::Input::new(150,35, 100, 30,"Input name");inp.set_frame(FrameType::FlatBox);let mut ok = button::Button::new(300,35,80, 30,"Ok");style_button(&mut ok);win.end();// 只允許運行當前窗體達到彈窗的效果,不允許窗體關閉win.make_modal(true);win.show();ok.set_callback({let mut win = win.clone();move |_| {// 窗體關閉,由于我們要讓主appwait, 所以我們下面海涌到了win,所以這里用的clone方法win.hide();}});while win.shown() {app::wait();}Self { inp }}pub fn value(&self) -> String {self.inp.value()} }fn main() {let a = app::App::default();app::set_font(Font::Times);let mut win = window::Window::default().with_size(600, 400);win.set_color(Color::from_rgb(240, 240, 240));let mut btn = button::Button::default().with_size(80, 30).with_label("Click").center_of_parent();style_button(&mut btn);// 這里沒想到這個frame布局還可以這么用,挺好玩的let mut frame = frame::Frame::new(btn.x() - 40, btn.y() - 100, btn.w() + 80, 30, None);frame.set_frame(FrameType::BorderBox);frame.set_color(Color::Red.inactive());win.end();win.show();btn.set_callback(move |_| {let d = show_dialog();// 同步更新frame.set_label(&d.value());});a.run().unwrap(); }Pictures
我們GUI無論樣式如何都是要使用圖片來美化我們的應用的,FLTK支持一堆圖片使用的,請參考官網(wǎng)支持的圖片種類:https://fltk-rs.github.io/fltk-book/Images.html
fn main() {let app = app::App::default().with_scheme(app::Scheme::Gleam);let mut wind = window::Window::new(100, 100, 400, 300, "Hello from rust");let mut frame = frame::Frame::new(20,20,90,160,"");let mut image = image::JpegImage::load("。。。1.jpg").unwrap();// 強轉大小image.scale(90,160,true,true);frame.set_image(Some(image));wind.make_resizable(true);wind.end();wind.show();// 當圖片被點擊的時候觸發(fā)frame.handle(|frame,ev|{match ev{enums::Event::Push => {// 與我上面演示的變化原理相同println!("Clicked");true}_ => false,}});app.run().unwrap(); }簡單代碼意思一下。此外除了set_picture, 我們還可以使用draw方法,這樣在大部分組件中都可以使用圖片了。
use fltk::{prelude::*, *}; use fltk::enums::FrameType;fn main() {let app = app::App::default().with_scheme(app::Scheme::Gleam);let mut wind = window::Window::new(100, 100, 400, 300, "Hello from rust");let mut button = button::Button::new(10,10,200,200,"");let mut image = image::JpegImage::load("C:/Users/40629/Desktop/image/1.jpg").unwrap();button.draw(move |b|{image.scale(b.w(),b.h(),true,true);image.draw(b.x(),b.y(),b.w(),b.h());});wind.make_resizable(true);wind.end();wind.show();button.set_callback(|button|{println!("clicked");});app.run().unwrap(); }Events
我們前面所接觸的大部分不見為我們提供了callback方法進行回調,但是fltk給了我們更多的操作空間,比如我之前使用handle.這一節(jié)會講解這些東西的用法:
-
callback,前面已經(jīng)講過很多了,我覺得沒必要再說了。callback可以接受閉包,也可以接收函數(shù)。閉包只是因為更方便
-
handle: handle 方法接受一個參數(shù)為 Event 的閉包,并為已處理的事件返回一個 bool。bool 讓 FLTK 知道事件是否被處理。這也是為什么我前面的代碼都要返回一個bool的原因。eg.
frame.handle(|frame,ev|{match ev{// Push單機,此外還有一些方法enums::Event::Push => {// 與我上面演示的變化原理相同println!("Clicked");true}_ => false,}}); -
emit: 使用sender/ receiver的消息使用消費者模型,我們在菜單組件中也有介紹過哦:
fn main() {let app = app::App::default();let mut my_window = window::Window::new(100, 100, 400, 300, "My Window");let mut but = button::Button::new(160, 200, 80, 40, "Click me!");my_window.end();my_window.show();// 這里是用app_cahhel定義,而不是我們生產(chǎn)者消費者模型的channel定義// 當然,這里用生產(chǎn)者消費者模型也完全沒問題let (s, r) = app::channel();but.emit(s, true);// 循環(huán)監(jiān)聽while app.wait() {// 收到消息(沒被阻塞)就完成下面的操作if let Some(msg) = r.recv() {match msg {true => println!("Clicked"),false => (), // Here we basically do nothing}}} } -
自定義事件:講道理我覺得已有的29個事件夠用了,我反正是沒啥遺憾啦,如果想看自定義事件參見:https://fltk-rs.github.io/fltk-book/Events.html
總結
- 上一篇: 游戏模型提取工具ninjaripper_
- 下一篇: uniapp 安卓/ios 录音授权,录