├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md ├── examples ├── 7guis_counter.rs ├── alarm.rs ├── button.rs ├── hello0.rs ├── hello1.rs ├── label.rs ├── led.rs ├── text.rs └── toggle.rs └── src ├── callback ├── button.rs ├── callbacks.rs ├── macros.rs └── mod.rs ├── clipboard.rs ├── control ├── button.rs ├── frame.rs ├── label.rs ├── list.rs ├── mod.rs ├── progress.rs ├── text.rs └── toggle.rs ├── dialog ├── alarm.rs ├── dialog.rs ├── file.rs ├── message.rs └── mod.rs ├── element ├── guard.rs ├── hierarchy.rs ├── mod.rs └── widget.rs ├── image.rs ├── layout ├── fill.rs ├── hbox.rs ├── mod.rs ├── radio.rs └── vbox.rs ├── led.rs ├── lib.rs ├── macros.rs ├── prelude.rs └── timer.rs /.gitignore: -------------------------------------------------------------------------------- 1 | *.bak 2 | *.swp 3 | target/ 4 | Cargo.lock 5 | examples/test.rs 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | env: 2 | global: 3 | - secure: "CB2priUBlpzv2aMQcYg7hYajUt2iC2kE/OZaWXAS3tfH+nXZO4Mz8O6sR4ScXYw7V//xeadf7hoKtds1sIK7I+3aQK3fzVi7oLH/Qc68iQQ1pC8eBhPqSgDi0EbUHl9qIMvy+8UbjwW3qUWrdzWA8yrMkOFf+8SxtDdAOAMFnB4feEa1ozyqxZrnj4FoaeaxROulSCgRhyQY2LGlr0kMPxxdAQbfbHYMGvl/+/F/T4evZTt+h5pgTHClUe9f5EKVUERiBovEBX7scAUnBNrT6w9GKIILuvF+6PMzxP31yxUbGukmko1D5AtdogVATZEy1NfyR4u280TUgSOQl9BX8psJ2kdJp06po9s+t2/CuLHsoT1KoFiHhzWv588hF11+pLNxoBxtboEwhndf8idUTRRkPfGui4/nPpSFH1yBuDTPq3lXokqjb+b16DcBIQXqLDM4N95LyrGHn739rWkuwau+wHmOZOUosUYQsIgvzOL+ZKN2ma+7NpAC05hrCVKz3h/fS1pxK6Vrb2F25UOq/L743/z2TVHeSNjgGtc43VHwENSDZJIpKfDlVbjZ4V5ysHM/zr/BHPjnBvc3ZGVtnMTCq0z/Lrn4p+SBn/jtjoJn+vsWNFt2Ku0bHlLOOH4JCpG8dRYseuCwVcERAkF5Cdqr5Jb1xOyQPSiGKVpI2MY=" 4 | - IUP_DL='http://sourceforge.net/projects/iup/files/3.14/Linux%20Libraries/iup-3.14_Linux32_64_lib.tar.gz' 5 | 6 | language: rust 7 | 8 | install: 9 | # Download and install IUP 10 | - mkdir iup_libs/ 11 | - wget $IUP_DL -O iup_libs.tar.gz 12 | - tar -xzvf iup_libs.tar.gz -C iup_libs/ 13 | # By piping a newline to ./install, we skip the enter prompt 14 | - (cd iup_libs/ && echo -ne '\n' | sudo ./install) 15 | - rm -rf iup_libs/ 16 | 17 | script: 18 | - cargo build -v 19 | - cargo test -v 20 | - cargo doc -v 21 | 22 | after_success: | 23 | [ $TRAVIS_BRANCH = master ] && 24 | [ $TRAVIS_PULL_REQUEST = false ] && 25 | echo '' > target/doc/index.html && 26 | sudo pip install ghp-import && 27 | ghp-import -n target/doc && 28 | git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages 29 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iup" 3 | version = "0.0.3" 4 | authors = ["David Campbell ", 5 | "Denilson das Mercês Amorim "] 6 | 7 | [lib] 8 | name = "iup" 9 | 10 | [dependencies] 11 | iup-sys = "0.0" 12 | libc = "0.1" 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 David Campbell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | IUP Rust [![Build Status](https://travis-ci.org/dcampbell24/iup-rust.svg)](https://travis-ci.org/dcampbell24/iup-rust)[![Join the chat at https://gitter.im/dcampbell24/iup-rust](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/dcampbell24/iup-rust?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 2 | ======================================= 3 | 4 | This library provides a high level wrapper around [IUP][1], a multi-platform 5 | toolkit for building graphical user interfaces. See [rust-iup-sys](https://github.com/dcampbell24/rust-iup-sys) for low level bindings. 6 | 7 | [IUP][1] is a multi-platform toolkit for building graphical user interfaces. 8 | 9 | It's purpose is to allow a program to run in different systems without changes - the toolkit 10 | provides the application portability. Supported systems include: GTK+, Motif and Windows. 11 | 12 | IUP has some advantages over other interface toolkits available: 13 | 14 | + **Simplicity:** due to the small number of functions and to its attribute mechanism, 15 | the learning curve for a new user is often faster. 16 | + **Portability:** the same functions are implemented in each one of the platforms, thus 17 | assuring the interface system's portability. 18 | + **Customization:** the dialog specification language (LED) is a mechanisms in which it 19 | is possible to customize an application for a specific user with a simple-syntax text file. 20 | + **Flexibility:** its abstract layout mechanism provides flexibility to dialog creation. 21 | + **Extensibility:** the programmer can create new interface elements as needed. 22 | 23 | The Rust binding provides a way to do things in a more Rustic way but without moving out of 24 | IUP base nameclatures and philosophy in such a way that one can program on this binding by reading the 25 | original [IUP documentation][1]. 26 | 27 | [Documentation](http://dcampbell24.github.io/iup-rust/) 28 | --------------- 29 | 30 | Click the link above or run `cargo doc` on this repository to view the documentation locally. 31 | 32 | Installation 33 | ------------ 34 | 35 | See [rust-iup-sys](https://github.com/dcampbell24/rust-iup-sys) for 36 | information on installing the IUP system libraries needed to use this library. 37 | After you have the IUP system libraries just add this to your `Cargo.toml`: 38 | 39 | [dependencies.iup] 40 | git = "https://github.com/dcampbell24/iup-rust" 41 | 42 | Contribute 43 | ---------- 44 | 45 | Contributions are welcome both in the form of ideas and of code. If you want to work on something, please open a issue to let others know what you are working on. If you are not sure what to work on, check our issues to see what must be worked on. 46 | 47 | If you find any issues with the library, please create a GitHub issue for it. 48 | 49 | [1]: http://www.tecgraf.puc-rio.br/iup/ 50 | -------------------------------------------------------------------------------- /examples/7guis_counter.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate iup; 3 | 4 | use iup::prelude::*; 5 | use iup::layout::HBox; 6 | use iup::control::{Button, Text}; 7 | 8 | fn main() { 9 | iup::with_iup(|| { 10 | 11 | let mut text = Text::new() 12 | .set_attrib("VALUE", "0") 13 | .set_attrib("READONLY", "YES"); 14 | 15 | let button = Button::with_title("Count") 16 | .set_action(move |_| { 17 | let count = text.attrib("VALUE").unwrap().parse::().unwrap(); 18 | text.set_attrib("VALUE", (count + 1).to_string()); 19 | }); 20 | 21 | let mut dialog = Dialog::new( 22 | HBox::new(elements![text, button]) 23 | .set_attrib("ALIGNMENT", "ACENTER") 24 | .set_attrib("MARGIN", "10x10") 25 | .set_attrib("GAP", "10") 26 | ); 27 | 28 | dialog.show() 29 | 30 | }).unwrap(); 31 | } 32 | -------------------------------------------------------------------------------- /examples/alarm.rs: -------------------------------------------------------------------------------- 1 | //! Example based on http://sourceforge.net/p/iup/iup/HEAD/tree/trunk/iup/html/examples/Lua/alarm.lua 2 | 3 | #[macro_use] 4 | extern crate iup; 5 | use iup::dialog::AlarmButton; 6 | 7 | fn main () { 8 | iup::with_iup(|| { 9 | match iup::dialog::alarm("Alarm Example", "File not saved! Save it now?", "Yes".into(), 10 | Some("No".into()), Some("Cancel".into())) { 11 | AlarmButton::Button1 => iup::dialog::message("Save file", "File saved successfully - leaving program"), 12 | AlarmButton::Button2 => iup::dialog::message("Save file", "File not saved - leaving program anyway"), 13 | AlarmButton::Button3 => iup::dialog::message("Save file", "Operation canceled"), 14 | } 15 | Err("don't let the main loop run or we'll get frozen because we didn't get any dialog!".into()) 16 | }).unwrap(); 17 | } 18 | -------------------------------------------------------------------------------- /examples/button.rs: -------------------------------------------------------------------------------- 1 | //! Example based on http://sourceforge.net/p/iup/iup/HEAD/tree/trunk/iup/html/examples/Lua/button.lua 2 | //! 3 | //! Creates four buttons. The first uses images, the second turns the first on and off, the third 4 | //! exits the application and the last does nothing 5 | //! 6 | 7 | #[macro_use] 8 | extern crate iup; 9 | 10 | use iup::prelude::*; 11 | use iup::control::{Button, Text}; 12 | use iup::layout::{VBox, HBox, Fill}; 13 | use iup::image::Image; 14 | use iup::callback::button::{MouseButton, MouseButtonState}; 15 | 16 | fn main() { 17 | iup::with_iup(|| { 18 | 19 | // We can create a image inline to be embedded in a control... 20 | let img_release = Image::with(pixels![ 21 | [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], 22 | [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2], 23 | [1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2], 24 | [1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2], 25 | [1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2], 26 | [1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2], 27 | [1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2], 28 | [1,1,3,3,3,3,3,3,4,4,3,3,3,3,2,2], 29 | [1,1,3,3,3,3,3,4,4,4,4,3,3,3,2,2], 30 | [1,1,3,3,3,3,3,4,4,4,4,3,3,3,2,2], 31 | [1,1,3,3,3,3,3,3,4,4,3,3,3,3,2,2], 32 | [1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2], 33 | [1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2], 34 | [1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2], 35 | [1,1,3,3,3,3,3,3,3,3,3,3,3,3,2,2], 36 | [1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2], 37 | [2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2] 38 | ]).set_colors([(0, 0, 0), (215,215,215), (40, 40, 40), (30, 50, 210), (240, 0, 0)]); 39 | 40 | // Creates a text box 41 | let mut text = Text::new() 42 | .set_attrib("READONLY", "YES") 43 | .set_attrib("EXPAND", "YES"); 44 | 45 | // Creates a button with image 46 | let mut btn_image = Button::with_title("Button with image") 47 | .set_attrib_handle("IMAGE", img_release) 48 | .set_button_cb(move |(_, button, state, _, _, _)| { 49 | if button == MouseButton::Button1 { 50 | text.set_attrib("VALUE", format!("Odd button {}", match state { 51 | MouseButtonState::Pressed => "pressed", 52 | MouseButtonState::Released => "released", 53 | })); 54 | } 55 | }); 56 | 57 | // Creates a [useless] button 58 | let btn_big = Button::with_title("Big useless button") 59 | .set_attrib("SIZE", "0.125x0.125"); 60 | 61 | // Creates a button entitled Exit 62 | let btn_exit = Button::with_title("Exit") 63 | .set_action(|_| CallbackReturn::Close); 64 | 65 | // Creates a button entitled 'Activate' 66 | let btn_on_off = Button::with_title("Activate") 67 | .set_action(move |_| { 68 | if btn_image.attrib("ACTIVE").unwrap() == "YES" { 69 | btn_image.set_attrib("ACTIVE", "NO"); 70 | } else { 71 | btn_image.set_attrib("ACTIVE", "YES"); 72 | } 73 | }); 74 | 75 | // Creates a dialog window 76 | Dialog::new( 77 | VBox::new(elements![ 78 | HBox::new(elements![ 79 | Fill::new(), btn_image, btn_on_off, btn_exit, Fill::new() 80 | ]), 81 | text, btn_big 82 | ]) 83 | ).set_attrib("TITLE", "Button") 84 | .set_attrib("RESIZE", "NO") // Turn of resize 85 | .set_attrib("MENUBOX", "NO") // ... menu box 86 | .set_attrib("MAXBOX", "NO") // ... maximize 87 | .set_attrib("MINBOX", "NO") // and minimize 88 | .show() 89 | 90 | }).unwrap(); 91 | } 92 | -------------------------------------------------------------------------------- /examples/hello0.rs: -------------------------------------------------------------------------------- 1 | //! Example based on hello0 from http://wiki.call-cc.org/iup-tutor 2 | 3 | extern crate iup; 4 | 5 | use iup::prelude::*; 6 | use iup::control::Label; 7 | 8 | fn main() { 9 | iup::with_iup(|| Dialog::new(Label::with_title("Hello, world!")).show()).unwrap() 10 | } 11 | -------------------------------------------------------------------------------- /examples/hello1.rs: -------------------------------------------------------------------------------- 1 | //! Example based on hello1 from http://wiki.call-cc.org/iup-tutor 2 | #[macro_use] 3 | extern crate iup; 4 | 5 | use iup::prelude::*; 6 | use iup::layout::VBox; 7 | use iup::control::{Button, Label}; 8 | 9 | fn main () { 10 | iup::with_iup(|| { 11 | let button = Button::with_title("Ok") 12 | .set_attrib("EXPAND", "YES") 13 | .set_attrib("TIP", "Exit button") 14 | .set_action(|_| CallbackReturn::Close); 15 | 16 | let label = Label::with_title("Hello, world!"); 17 | 18 | let vbox = VBox::new(elements![label, button]) 19 | .set_attrib("GAP", "10") 20 | .set_attrib("MARGIN", "10x10") 21 | .set_attrib("ALIGNMENT", "ACENTER"); 22 | 23 | Dialog::new(vbox) 24 | .set_attrib("TITLE", "Hello") 25 | .show() 26 | 27 | }).unwrap(); 28 | } 29 | -------------------------------------------------------------------------------- /examples/label.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate iup; 3 | 4 | use iup::prelude::*; 5 | use iup::layout::VBox; 6 | use iup::control::Label; 7 | 8 | fn main () { 9 | iup::with_iup(|| { 10 | let phrase = "The quick brown fox jumps over the lazy dog"; 11 | Dialog::new( 12 | VBox::new(elements![ 13 | 14 | Label::with_title(phrase), 15 | Label::new_separator(Orientation::Horizontal), 16 | 17 | Label::with_title(phrase) 18 | .set_attrib("FONT", "COURIER_NORMAL_14"), 19 | Label::new_separator(Orientation::Horizontal), 20 | 21 | VBox::new(elements![ 22 | Label::with_title(phrase), 23 | Label::with_title(phrase), 24 | ]).set_attrib("FONT", "COURIER_ITALIC_14"), 25 | 26 | ]).set_attrib("GAP", "5x") 27 | .set_attrib("MARGIN", "10x10") 28 | 29 | ).set_attrib("TITLE", "Label") 30 | .set_attrib("RESIZE", "NO") 31 | .show() 32 | 33 | }).unwrap(); 34 | } 35 | -------------------------------------------------------------------------------- /examples/led.rs: -------------------------------------------------------------------------------- 1 | // hello1.rs example written in LED. 2 | #[macro_use] 3 | extern crate iup; 4 | 5 | use iup::prelude::*; 6 | use iup::Handle; 7 | use iup::control::Button; 8 | use iup::led; 9 | 10 | fn main () { 11 | iup::with_iup(|| { 12 | // See also led::load(path) to load from a file 13 | led::load_buffer(r######" 14 | # This is a LED comment. 15 | btn = button[EXPAND=YES, TIP="Exit button"]("Ok", 0) 16 | dlg = dialog[TITLE="Hello"] 17 | ( 18 | vbox[GAP=10, MARGIN=10x10, ALIGNMENT=ACENTER] 19 | ( 20 | label("Hello, world!"), 21 | btn 22 | ) 23 | ) 24 | "######).unwrap(); 25 | 26 | let mut dialog = Dialog::from_handle(Handle::from_named("dlg").unwrap()).unwrap(); 27 | let mut button = Button::from_handle(Handle::from_named("btn").unwrap()).unwrap(); 28 | button.set_action(|_| CallbackReturn::Close); 29 | 30 | dialog.show() 31 | 32 | }).unwrap(); 33 | } 34 | -------------------------------------------------------------------------------- /examples/text.rs: -------------------------------------------------------------------------------- 1 | //! Creates a bunch of useless text controls. 2 | 3 | #[macro_use] 4 | extern crate iup; 5 | 6 | use iup::prelude::*; 7 | use iup::layout::{HBox, VBox}; 8 | use iup::control::Text; 9 | 10 | fn main () { 11 | iup::with_iup(|| { 12 | 13 | let mut info = Text::new() 14 | .set_attrib("READONLY", "YES") 15 | .set_attrib("EXPAND", "HORIZONTAL") 16 | .set_attrib("VALUE", "You can read, but you can't edit."); 17 | 18 | Dialog::new( 19 | VBox::new(elements![ 20 | 21 | info, 22 | Text::new() 23 | .set_attrib("MULTILINE", "YES") 24 | .set_attrib("EXPAND", "YES") 25 | .set_caret_cb(move |(_, lin, col, pos)| { 26 | info.set_attrib("VALUE", format!("Text changed at {}:{}, {}", lin, col, pos)); 27 | }), 28 | 29 | HBox::new(elements![ 30 | Text::new_spin() 31 | .set_spin_cb(move |(_, pos)| { 32 | info.set_attrib("VALUE", format!("Spin changed to '{}'", pos)); 33 | }), 34 | Text::new().set_attrib("PASSWORD", "YES") 35 | .set_attrib("VALUE", "123456789") 36 | .set_attrib("EXPAND", "HORIZONTAL"), 37 | ]) 38 | ]).set_attrib("EXPAND", "YES") 39 | 40 | ).set_attrib("TITLE", "Text") 41 | .set_attrib("SIZE", "200x200") 42 | .show() 43 | 44 | }).unwrap(); 45 | } 46 | -------------------------------------------------------------------------------- /examples/toggle.rs: -------------------------------------------------------------------------------- 1 | //! An example on toggle and radio controls. 2 | //! 3 | //! This example contains: 4 | //! + One 3 state button that does nothing. 5 | //! + Four toggles that switch the color of the dialog window. 6 | //! + One button that activates or deactivates the coloring of the dialog window. 7 | //! 8 | 9 | #[macro_use] 10 | extern crate iup; 11 | 12 | use std::rc::Rc; 13 | use std::cell::Cell; 14 | 15 | use iup::prelude::*; 16 | use iup::layout::{Radio, VBox}; 17 | use iup::control::{Frame, Toggle, Label}; 18 | 19 | const RED: (u8, u8, u8) = (255, 0, 0); 20 | const GREEN: (u8, u8, u8) = (0, 255, 0); 21 | const BLUE: (u8, u8, u8) = (0, 0, 255); 22 | 23 | fn change_color(mut dialog: Dialog, state: bool, color: Option<(u8, u8, u8)>) 24 | -> Option<(u8, u8, u8)> { 25 | if state { 26 | match color { 27 | Some(rgb) => dialog.set_attrib_rgb("BGCOLOR", rgb), 28 | None => dialog.clear_attrib("BGCOLOR"), 29 | }; 30 | } 31 | dialog.attrib_rgb("BGCOLOR") 32 | } 33 | 34 | fn main() { 35 | iup::with_iup(|| { 36 | 37 | // Since we are going to share the state of this color in our callbacks we need to put it 38 | // in a reference counted cell, then clone a instance for each callback. 39 | let color = Rc::new(Cell::new(None)); 40 | let (color_d, color_r) = (color.clone(), color.clone()); 41 | let (color_g, color_b) = (color.clone(), color.clone()); 42 | 43 | // We instantiate the dialog up here with no child so we can access it from the toggle 44 | // callbacks... 45 | let mut dialog = Dialog::new_empty(); 46 | 47 | // Setup the useless toggle 48 | let toggle3s = Toggle::with_title("Useless Toggle") 49 | .set_attrib("3STATE", "YES"); 50 | 51 | // Setup the coloring toggles 52 | let toggle_d = Toggle::with_title("Default Color") 53 | .set_action(move |(_, state)| color_d.set(change_color(dialog, state, None))); 54 | let toggle_r = Toggle::with_title("Red Color") 55 | .set_action(move |(_, state)| color_r.set(change_color(dialog, state, Some(RED)))); 56 | let toggle_g = Toggle::with_title("Green Color") 57 | .set_action(move |(_, state)| color_g.set(change_color(dialog, state, Some(GREEN)))); 58 | let toggle_b = Toggle::with_title("Blue Color") 59 | .set_action(move |(_, state)| color_b.set(change_color(dialog, state, Some(BLUE)))); 60 | 61 | // Setup the radio of mutually exclusive toggles 62 | let mut radio = Radio::new( 63 | Frame::new( 64 | VBox::new(elements![ 65 | toggle_d, 66 | toggle_r, 67 | toggle_g, 68 | toggle_b, 69 | ]) 70 | ).set_attrib("TITLE", "Colors") 71 | ); 72 | 73 | // Setup the allow colors toggle 74 | let toggle = Toggle::with_title("Allow Colors") 75 | .set_attrib("VALUE", "YES") 76 | .set_action(move |(_, state)| { 77 | if state { 78 | radio.set_attrib("ACTIVE", "YES"); 79 | color.set(change_color(dialog, true, color.get())); 80 | } else { 81 | radio.set_attrib("ACTIVE", "NO"); 82 | change_color(dialog, true, None); 83 | } 84 | }); 85 | 86 | // Add a layout to the dialog hierarchy 87 | dialog.append( 88 | VBox::new(elements![ 89 | toggle3s, 90 | Label::new_separator(Orientation::Horizontal), 91 | toggle, 92 | radio, 93 | ]) 94 | ).unwrap(); 95 | 96 | // Setup the dialog and show it up 97 | dialog 98 | .set_attrib("TITLE", "Toggle") 99 | .set_attrib("MARGIN", "5x5") 100 | .set_attrib("GAP", "5") 101 | .set_attrib("RESIZE", "NO") 102 | .show() 103 | 104 | }).unwrap(); 105 | } 106 | -------------------------------------------------------------------------------- /src/callback/button.rs: -------------------------------------------------------------------------------- 1 | //! Mouse button presses callback. 2 | use iup_sys; 3 | use std::fmt; 4 | use libc::{c_char, c_int}; 5 | use callback::IntoRust; 6 | 7 | /// Mouse buttons. 8 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 9 | pub enum MouseButton { 10 | /// Left mouse button. 11 | Button1, 12 | /// Middle mouse button. 13 | Button2, 14 | /// Right mouse button. 15 | Button3, 16 | /// Additional mouse button. 17 | Button4, 18 | /// Additional mouse button. 19 | Button5, 20 | } 21 | 22 | impl MouseButton { 23 | #[doc(hidden)] 24 | pub fn from_id(id: c_int) -> MouseButton { 25 | match id { 26 | 1 => MouseButton::Button1, 27 | 2 => MouseButton::Button2, 28 | 3 => MouseButton::Button3, 29 | 4 => MouseButton::Button4, 30 | 5 => MouseButton::Button5, 31 | _ => unreachable!(), 32 | } 33 | } 34 | } 35 | 36 | /// Specifies what happened to the mouse button in the `ButtonCb`. 37 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 38 | pub enum MouseButtonState { 39 | Released, 40 | Pressed, 41 | } 42 | 43 | // TODO a way to unallow the user to move/copy instances of this type as the char buffer will 44 | // be invalid after the callback exits. 45 | /// The state of mouse buttons and some keyboard buttons. 46 | pub struct KeyStates(*const c_char); 47 | 48 | impl KeyStates { 49 | /// Whether this state have a SHIFT key pressed. 50 | #[inline(always)] 51 | pub fn is_shift(&self) -> bool { 52 | unsafe { iup_sys::iup_isshift(self.0) } 53 | } 54 | /// Whether this state have a CONTROL key pressed. 55 | #[inline(always)] 56 | pub fn is_control(&self) -> bool { 57 | unsafe { iup_sys::iup_iscontrol(self.0) } 58 | } 59 | /// Whether this state have a ALT key pressed. 60 | #[inline(always)] 61 | pub fn is_alt(&self) -> bool { 62 | unsafe { iup_sys::iup_isalt(self.0) } 63 | } 64 | /// Whether this state have the system key pressed. 65 | /// 66 | /// The system key in Windows is the *Windows key* and in Mac is the *Apple key*. 67 | #[inline(always)] 68 | pub fn is_sys(&self) -> bool { 69 | unsafe { iup_sys::iup_issys(self.0) } 70 | } 71 | /// Whether this state have the specified button in the callback doubly pressed. 72 | #[inline(always)] 73 | pub fn is_double(&self) -> bool { 74 | unsafe { iup_sys::iup_isdouble(self.0) } 75 | } 76 | /// Whether this state have the left mouse button pressed. 77 | #[inline(always)] 78 | pub fn is_button1(&self) -> bool { 79 | unsafe { iup_sys::iup_isbutton1(self.0) } 80 | } 81 | /// Whether this state have the middle mouse button pressed. 82 | #[inline(always)] 83 | pub fn is_button2(&self) -> bool { 84 | unsafe { iup_sys::iup_isbutton2(self.0) } 85 | } 86 | /// Whether this state have the right mouse button pressed. 87 | #[inline(always)] 88 | pub fn is_button3(&self) -> bool { 89 | unsafe { iup_sys::iup_isbutton3(self.0) } 90 | } 91 | /// Whether this state have the additional mouse button 1 pressed. 92 | #[inline(always)] 93 | pub fn is_button4(&self) -> bool { 94 | unsafe { iup_sys::iup_isbutton4(self.0) } 95 | } 96 | /// Whether this state have the additional mouse button 2 pressed. 97 | #[inline(always)] 98 | pub fn is_button5(&self) -> bool { 99 | unsafe { iup_sys::iup_isbutton5(self.0) } 100 | } 101 | } 102 | 103 | impl fmt::Debug for KeyStates { 104 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 105 | fmt.write_fmt(format_args!( 106 | "KeyStates(Shift={}, Control={}, Alt={}, Sys={}, Double={},\ 107 | Button1={}, Button2={}, Button3={}, Button4={}, Button5={})", 108 | self.is_shift(), self.is_control(), self.is_alt(), self.is_sys(), self.is_double(), 109 | self.is_button1(), self.is_button2(), self.is_button3(), self.is_button4(), self.is_button5() 110 | )) 111 | } 112 | } 113 | 114 | impl IntoRust for *mut c_char { 115 | fn into_rust(self) -> KeyStates { 116 | KeyStates(self) 117 | } 118 | } 119 | 120 | impl IntoRust for c_int { 121 | fn into_rust(self) -> MouseButton { 122 | match self { 123 | iup_sys::IUP_BUTTON1 => MouseButton::Button1, 124 | iup_sys::IUP_BUTTON2 => MouseButton::Button2, 125 | iup_sys::IUP_BUTTON3 => MouseButton::Button3, 126 | iup_sys::IUP_BUTTON4 => MouseButton::Button4, 127 | iup_sys::IUP_BUTTON5 => MouseButton::Button5, 128 | _ => unreachable!(), 129 | } 130 | } 131 | } 132 | 133 | impl IntoRust for c_int { 134 | fn into_rust(self) -> MouseButtonState { 135 | if self != 0 { MouseButtonState::Pressed } else { MouseButtonState::Released } 136 | } 137 | } 138 | 139 | 140 | impl_callback! { 141 | /// Action generated when a mouse button is pressed or released. 142 | /// 143 | /// The `Button` parameter identifies the activated mouse button that triggered the action. 144 | /// 145 | /// The `i32` parameters are the x,y position in the canvas where the event has occurred, 146 | /// in pixels. 147 | /// 148 | /// The `KeyStates` parameter is the state of the mouse buttons and some keyboard keys at 149 | //// the moment the event is generated. 150 | /// 151 | /// `CallbackReturn::Close` will be processed. On some controls if `CallbackReturn::Ignore` 152 | /// is returned the action is ignored *(this is system dependent)*. 153 | /// 154 | /// This callback can be used to customize a button behavior. For a standard button behavior 155 | /// use the `Action` callback. 156 | /// 157 | /// [Learn more](http://webserver2.tecgraf.puc-rio.br/iup/en/call/iup_button_cb.html). 158 | pub trait ButtonCb where Self: Element { 159 | let name = "BUTTON_CB"; 160 | extern fn listener(ih: *mut iup_sys::Ihandle, button: c_int, pressed: c_int, 161 | x: c_int, y: c_int, status: *mut c_char) -> CallbackReturn; 162 | fn set_button_cb(&mut self, cb: F) -> Self; 163 | fn remove_button_cb(&mut self) -> Option>; 164 | } 165 | } 166 | 167 | impl_callback! { 168 | /// Action generated when the mouse moves. 169 | /// 170 | /// The `i32` parameters are the x,y position in the canvas where the event has occurred, 171 | /// in pixels. 172 | /// 173 | /// The `KeyStates` parameter is the state of the mouse buttons and some keyboard keys at 174 | //// the moment the event is generated. 175 | pub trait MotionCb where Self: Element { 176 | let name = "MOTION_CB"; 177 | extern fn listener(ih: *mut iup_sys::Ihandle, x: c_int, y: c_int, status: *mut c_char) -> CallbackReturn; 178 | fn set_motion_cb(&mut self, cb: F) -> Self; 179 | fn remove_motion_cb(&mut self) -> Option>; 180 | } 181 | } 182 | 183 | -------------------------------------------------------------------------------- /src/callback/callbacks.rs: -------------------------------------------------------------------------------- 1 | use libc::c_char; 2 | use std::path::PathBuf; 3 | 4 | // 5 | // The following regex can be used to convert from doc comments to attrib comments: 6 | // ([\t ]*)\/\/\/[ ]?(.*) 7 | // $1#[doc="$2"] 8 | // 9 | // See also PR #26 10 | // 11 | 12 | // Note: This callback is IUP-Rust specific, evaluated manually in `with_iup`. 13 | impl_callback! { 14 | let name = "_IUPRUST_CLOSE_CB"; 15 | extern fn listener() -> CallbackReturn; 16 | #[doc="Action generated when IUP closes (i.e. at the end of `with_iup`)."] 17 | pub fn set_close_cb(cb: F); 18 | #[doc="Removes a previosly set up close callback."] 19 | pub fn remove_close_cb() -> Option>; 20 | } 21 | 22 | impl_callback! { 23 | let name = "IDLE_ACTION"; 24 | extern fn listener() -> CallbackReturn; 25 | #[doc="Action generated when there are no events or messages to be processed."] 26 | #[doc=""] 27 | #[doc="Often used to perform background operations."] 28 | pub fn set_idle(cb: F); 29 | #[doc="Removes a previosly set up idle_action callback."] 30 | pub fn remove_idle() -> Option>; 31 | } 32 | 33 | // Common Callbacks 34 | // ---------------------------- 35 | 36 | // This is the common version of the ACTION callback, any so called ACTION that does not 37 | // have the `(*mut iup_sys::Ihandle)` signature should be another trait. 38 | impl_callback! { 39 | #[doc="Action generated when the element is activated. Affects each element differently."] 40 | #[doc=""] 41 | #[doc="See the documentation of the `Self` object for the effect of this callback on it."] 42 | pub trait Action where Self: Element { 43 | let name = "ACTION"; 44 | extern fn listener(ih: *mut iup_sys::Ihandle) -> CallbackReturn; 45 | fn set_action(&mut self, cb: F) -> Self; 46 | fn remove_action(&mut self) -> Option>; 47 | } 48 | } 49 | 50 | impl_callback! { 51 | #[doc="Action generated when the element is activated. Affects each element differently."] 52 | #[doc=""] 53 | #[doc="See the documentation of the `Self` object for the effect of this callback on it."] 54 | #[doc=""] 55 | #[doc="Note: This is different from the `ACTION` callback."] 56 | pub trait ActionCb where Self: Element { 57 | let name = "ACTION_CB"; 58 | extern fn listener(ih: *mut iup_sys::Ihandle) -> CallbackReturn; 59 | fn set_action_cb(&mut self, cb: F) -> Self; 60 | fn remove_action_cb(&mut self) -> Option>; 61 | } 62 | } 63 | 64 | // Note on using LDESTROY_CB instead of DESTROY_CB: 65 | // 66 | // IUP calls LDESTROY_CB and then calls DESTROY_CB. It was thought LDESTROY_CB should be used 67 | // for our binding to free things up, but we're doing it the other way around for simplicity. 68 | // 69 | // The binding free needs to be called after LDESTROY_CB (i.e. at DESTROY_CB) because 70 | // the LDESTROY_CB event call depends on the binding boxed data to work properly. 71 | impl_callback! { 72 | #[doc="Called right before an element is destroyed."] 73 | pub trait DestroyCb where Self: Element { 74 | let name = "LDESTROY_CB"; // See comments above for reason behind LDESTROY_CB. 75 | extern fn listener(ih: *mut iup_sys::Ihandle) -> CallbackReturn; 76 | fn set_destroy_cb(&mut self, cb: F) -> Self; 77 | fn remove_destroy_cb(&mut self) -> Option>; 78 | } 79 | } 80 | 81 | impl_callback! { 82 | #[doc="Called right after an element is mapped and its attributes updated in `Widget::map`."] 83 | #[doc=""] 84 | #[doc="When the element is a dialog, it is called after the layout is updated. For all other"] 85 | #[doc="elements is called before the layout is updated, so the element current size will still"] 86 | #[doc="be 0x0 during MAP_CB."] 87 | pub trait MapCb where Self: Element { 88 | let name = "MAP_CB"; 89 | extern fn listener(ih: *mut iup_sys::Ihandle) -> CallbackReturn; 90 | fn set_map_cb(&mut self, cb: F) -> Self; 91 | fn remove_map_cb(&mut self) -> Option>; 92 | } 93 | } 94 | 95 | impl_callback! { 96 | #[doc="Called right before an element is unmapped."] 97 | pub trait UnmapCb where Self: Element { 98 | let name = "UNMAP_CB"; 99 | extern fn listener(ih: *mut iup_sys::Ihandle) -> CallbackReturn; 100 | fn set_unmap_cb(&mut self, cb: F) -> Self; 101 | fn remove_unmap_cb(&mut self) -> Option>; 102 | } 103 | } 104 | 105 | impl_callback! { 106 | #[doc="Action generated when an element is given keyboard focus."] 107 | #[doc=""] 108 | #[doc="This callback is called after the `KillFocusCb` of the element that loosed the focus."] 109 | #[doc="The IupGetFocus (TODO) function during the callback returns the element that loosed the focus."] 110 | pub trait GetFocusCb where Self: Element { 111 | let name = "GETFOCUS_CB"; 112 | extern fn listener(ih: *mut iup_sys::Ihandle) -> CallbackReturn; 113 | fn set_getfocus_cb(&mut self, cb: F) -> Self; 114 | fn remove_getfocus_cb(&mut self) -> Option>; 115 | } 116 | } 117 | 118 | impl_callback! { 119 | #[doc="Action generated when an element loses keyboard focus."] 120 | #[doc=""] 121 | #[doc="This callback is called before the `GetFocusCb` of the element that gets the focus."] 122 | #[doc=""] 123 | #[doc="While processing this message, do not make any function calls that display or activate a"] 124 | #[doc="window. This causes the thread to yield control and can cause the application to stop"] 125 | #[doc="responding to messages."] 126 | pub trait KillFocusCb where Self: Element { 127 | let name = "KILLFOCUS_CB"; 128 | extern fn listener(ih: *mut iup_sys::Ihandle) -> CallbackReturn; 129 | fn set_killfocus_cb(&mut self, cb: F) -> Self; 130 | fn remove_killfocus_cb(&mut self) -> Option>; 131 | } 132 | } 133 | 134 | impl_callback! { 135 | #[doc="Action generated when the mouse enters the native element."] 136 | #[doc=""] 137 | #[doc="When the cursor is moved from one element to another, the call order in all platforms will"] 138 | #[doc="be first the `LeaveWindowCb` callback of the old control followed by the `EnterWindowCb`"] 139 | #[doc="callback of the new control."] 140 | pub trait EnterWindowCb where Self: Element { 141 | let name = "ENTERWINDOW_CB"; 142 | extern fn listener(ih: *mut iup_sys::Ihandle) -> CallbackReturn; 143 | fn set_enterwindow_cb(&mut self, cb: F) -> Self; 144 | fn remove_enterwindow_cb(&mut self) -> Option>; 145 | } 146 | } 147 | 148 | impl_callback! { 149 | #[doc="Action generated when the mouse leaves the native element."] 150 | #[doc=""] 151 | #[doc="When the cursor is moved from one element to another, the call order in all platforms will"] 152 | #[doc="be first the `LeaveWindowCb` callback of the old control followed by the `EnterWindowCb`"] 153 | #[doc="callback of the new control."] 154 | pub trait LeaveWindowCb where Self: Element { 155 | let name = "LEAVEWINDOW_CB"; 156 | extern fn listener(ih: *mut iup_sys::Ihandle) -> CallbackReturn; 157 | fn set_leavewindow_cb(&mut self, cb: F) -> Self; 158 | fn remove_leavewindow_cb(&mut self) -> Option>; 159 | } 160 | } 161 | 162 | impl_callback! { 163 | #[doc="Action generated when the user press F1 at a control."] 164 | #[doc=""] 165 | #[doc="`CallbackReturn::Close` will be processed."] 166 | pub trait HelpCb where Self: Element { 167 | let name = "HELP_CB"; 168 | extern fn listener(ih: *mut iup_sys::Ihandle) -> CallbackReturn; 169 | fn set_help_cb(&mut self, cb: F) -> Self; 170 | fn remove_help_cb(&mut self) -> Option>; 171 | } 172 | } 173 | 174 | // Other Callbacks 175 | // ---------------------------- 176 | 177 | impl_callback! { 178 | #[doc="Action generated when the caret/cursor position is changed."] 179 | #[doc=""] 180 | #[doc="The second and third parameters are the line and column number (start at 1)."] 181 | #[doc="The fourth parameter is a 0 based character position."] 182 | pub trait CaretCb where Self: Element { 183 | let name = "CARET_CB"; 184 | extern fn listener(ih: *mut iup_sys::Ihandle, lin: c_int, col: c_int, pos: c_int) -> CallbackReturn; 185 | fn set_caret_cb(&mut self, cb: F) -> Self; 186 | fn remove_caret_cb(&mut self) -> Option>; 187 | } 188 | } 189 | 190 | impl_callback! { 191 | #[doc="Action generated when a spin button is pressed."] 192 | pub trait SpinCb where Self: Element { 193 | let name = "SPIN_CB"; 194 | extern fn listener(ih: *mut iup_sys::Ihandle, i: c_int) -> CallbackReturn; 195 | fn set_spin_cb(&mut self, cb: F) -> Self; 196 | fn remove_spin_cb(&mut self) -> Option>; 197 | } 198 | } 199 | 200 | impl_callback! { 201 | #[doc="Usually called after the value of a control changed."] 202 | #[doc=""] 203 | #[doc="See the specific control documentation for more details."] 204 | pub trait ValueChangedCb where Self: Element { 205 | let name = "VALUECHANGED_CB"; 206 | extern fn listener(ih: *mut iup_sys::Ihandle) -> CallbackReturn; 207 | fn set_valuechanged_cb(&mut self, cb: F) -> Self; 208 | fn remove_valuechanged_cb(&mut self) -> Option>; 209 | } 210 | } 211 | 212 | 213 | impl_callback! { 214 | #[doc="Action called when a file is *dropped* into the control."] 215 | #[doc=""] 216 | #[doc="When several files are dropped at once, the callback is called several times, once for"] 217 | #[doc="each file. "] 218 | #[doc=""] 219 | #[doc="If defined after the element is mapped then the attribute DROPFILESTARGET must be set"] 220 | #[doc="to YES."] 221 | #[doc=""] 222 | #[doc="The third parameter of the callback is the number index of the dropped file. If several"] 223 | #[doc="files are dropped, it is the index of the dropped file starting from *total-1* to *0*."] 224 | #[doc="The fourth and fifth parameters are x,y coordinate of the point where the user"] 225 | #[doc="released the mouse button."] 226 | #[doc=""] 227 | #[doc="if `CallbackReturn::Ignore` is returned the callback will **not** be called for the"] 228 | #[doc="next dropped files, and the processing of dropped files will be interrupted."] 229 | #[doc=""] 230 | #[doc="\\[Windows and GTK Only\\] (GTK 2.6)"] 231 | pub trait DropFilesCb where Self: Element { 232 | let name = "DROPFILES_CB"; 233 | extern fn listener(ih: *mut iup_sys::Ihandle, filename: *const c_char, 234 | num: c_int, x: c_int, y: c_int) -> CallbackReturn; 235 | fn set_dropfiles_cb(&mut self, cb: F) -> Self; 236 | fn remove_dropfiles_cb(&mut self) -> Option>; 237 | // TODO should it be plural (dropfiles_cb) just like IUP or singular (dropfile_cb)? 238 | } 239 | } 240 | 241 | impl_callback! { 242 | #[doc="Called just before a dialog is closed when the user clicks the close button of the title bar"] 243 | #[doc="or an equivalent action."] 244 | #[doc=""] 245 | #[doc="`CallbackReturn::Close` will be processed. If `CallbackReturn::Ignore`, it prevents the dialog"] 246 | #[doc="from being closed. If you destroy the dialog in this callback, you must"] 247 | #[doc="return `CallbackReturn::Ignore`."] 248 | pub trait CloseCb where Self: Element { 249 | let name = "CLOSE_CB"; 250 | extern fn listener(ih: *mut iup_sys::Ihandle) -> CallbackReturn; 251 | fn set_move_cb(&mut self, cb: F) -> Self; 252 | fn remove_move_cb(&mut self) -> Option>; 253 | } 254 | } 255 | 256 | impl_callback! { 257 | #[doc="Called after the widget is moved, see it's documentation for more details."] 258 | pub trait MoveCb where Self: Element { 259 | let name = "MOVE_CB"; 260 | extern fn listener(ih: *mut iup_sys::Ihandle, x: c_int, y: c_int) -> CallbackReturn; 261 | fn set_move_cb(&mut self, cb: F) -> Self; 262 | fn remove_move_cb(&mut self) -> Option>; 263 | } 264 | } 265 | 266 | 267 | impl_callback! { 268 | #[doc="Action generated when the canvas or dialog size is changed."] 269 | pub trait ResizeCb where Self: Element { 270 | let name = "RESIZE_CB"; 271 | extern fn listener(ih: *mut iup_sys::Ihandle, w: c_int, h: c_int) -> CallbackReturn; 272 | fn set_move_cb(&mut self, cb: F) -> Self; 273 | fn remove_move_cb(&mut self) -> Option>; 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /src/callback/macros.rs: -------------------------------------------------------------------------------- 1 | 2 | /// Turns a string literal into a boxed closure attribute. 3 | macro_rules! fbox_c_str { 4 | ($cb_name:expr) => { 5 | // It's important to use the prefix '_IUP*', it's reserved by IUP for internal use and bindings. 6 | // So we use '_IUPRUST_*' prefix to refer to data reserved for the Rust binding. 7 | cstr!(concat!("_IUPRUST_FBOX_", $cb_name)) 8 | } 9 | } 10 | 11 | /// Sets a closure as a callback to IUP. 12 | /// 13 | /// Note: `$ih` can be a `ptr::null_mut` to set a callback in the global enviroment. 14 | macro_rules! set_fbox_callback { 15 | ($ih:expr, $cb_name:expr, $clistener:expr, $rcb:expr, Callback<$($rargs:ty),*>) => {{ 16 | 17 | use $crate::iup_sys; 18 | 19 | // TODO remove this in favour to std::boxed::into_raw when it gets stable. 20 | unsafe fn box_into_raw(b: Box) -> *mut T { 21 | transmute(b) 22 | } 23 | 24 | clear_fbox_callback!($ih, $cb_name, Callback<$($rargs),*>); 25 | 26 | let ih = $ih; 27 | let fb: Box>> = Box::new(Box::new($rcb)); 28 | iup_sys::IupSetAttribute(ih, fbox_c_str!($cb_name), box_into_raw(fb) as *const _); 29 | if ih.is_null() { 30 | iup_sys::IupSetFunction(cstr!($cb_name), transmute($clistener as *const ())); 31 | } else { 32 | iup_sys::IupSetCallback(ih, cstr!($cb_name), transmute($clistener as *const ())); 33 | } 34 | 35 | }} 36 | } 37 | 38 | /// Clears a closure as a callback to IUP. 39 | /// 40 | /// Returns a Option> with the previosly set closure. 41 | /// 42 | /// Note: `$ih` can be a `ptr::null_mut` to set a callback in the global enviroment. 43 | macro_rules! clear_fbox_callback { 44 | ($ih:expr, $cb_name:expr, Callback<$($rargs:ty),*>) => {{ 45 | use $crate::iup_sys; 46 | use std::mem::transmute; 47 | use std::ptr; 48 | 49 | let ih = $ih; 50 | let capsule_box = iup_sys::IupGetAttribute(ih, fbox_c_str!($cb_name)) 51 | as *mut Box>; 52 | if capsule_box.is_null() { 53 | None 54 | } else { 55 | 56 | // TODO when Box::from_raw gets stable use it instead of transmute here. 57 | let inner_box: Box>> = transmute(capsule_box); 58 | 59 | iup_sys::IupSetAttribute(ih, fbox_c_str!($cb_name), ptr::null()); 60 | 61 | if ih.is_null() { 62 | iup_sys::IupSetFunction(cstr!($cb_name), transmute(ptr::null::())); 63 | } else { 64 | iup_sys::IupSetCallback(ih, cstr!($cb_name), transmute(ptr::null::())); 65 | } 66 | 67 | Some(*inner_box) 68 | // inner_box itself gets freed now 69 | } 70 | }} 71 | } 72 | 73 | macro_rules! get_fbox_callback { 74 | ($ih:expr, $cb_name:expr, Callback<$($rargs:ty),*>) => {{ 75 | let fbox_ptr = unsafe { 76 | iup_sys::IupGetAttribute($ih, fbox_c_str!($cb_name)) 77 | as *mut Box> 78 | }; 79 | assert!(fbox_ptr.is_null() == false); 80 | let fbox: &mut Box<_> = unsafe { &mut (*(fbox_ptr)) }; 81 | fbox 82 | }} 83 | } 84 | 85 | /// Implements a callback binding between C IUP and Rust which accepts closures. 86 | /// 87 | /// After this macro is executed the trait `$trait_name` is implemented with the following 88 | /// default methods: 89 | //// 90 | /// + `$set_method` to set associate a closure with the callback `$cb_name`. 91 | /// The `F` (macro captured) constraint defines the type of high-level callback. 92 | /// + `$remove_method` to remove a previosly associated callback `$cb_name`. 93 | /// + `listener` is **not** defined. It is the native C callback signature (macro captured). 94 | /// + `resolve_args` is optional should have a code body, and is also not defined. 95 | /// It is responsible for translating the C arguments into Rust arguments. By default it just 96 | /// calls the `IntoRust` trait for each argument. 97 | /// 98 | /// **Note**: Don't forget to add a dropper for the event in `drop_callbacks` after using this 99 | /// macro. You **must** do so to free allocations associated with closures. 100 | /// 101 | macro_rules! impl_callback { 102 | 103 | // Used for element callbacks. 104 | // (no resolve_args version) 105 | ( 106 | $(#[$trait_attr:meta])* // allow doc comments here 107 | pub trait $trait_name:ident where Self: Element { 108 | let name = $cb_name:expr; 109 | extern fn listener(ih: *mut iup_sys::Ihandle $(, $ls_arg:ident: $ls_arg_ty:ty)*) -> CallbackReturn; 110 | 111 | fn $set_method:ident(&mut self, cb: F) -> Self; 112 | fn $remove_method:ident(&mut self) -> Option>; 113 | } 114 | 115 | ) => { 116 | impl_callback! { 117 | $(#[$trait_attr])* 118 | pub trait $trait_name where Self: Element { 119 | let name = $cb_name; 120 | extern fn listener(ih: *mut iup_sys::Ihandle $(, $ls_arg: $ls_arg_ty)*) -> CallbackReturn; 121 | 122 | fn $set_method(&mut self, cb: F) -> Self; 123 | fn $remove_method(&mut self) -> Option>; 124 | 125 | fn resolve_args(elem: Self, $($ls_arg: $ls_arg_ty),*) -> (Self, $($fn_arg_ty),*) { 126 | (elem, $($ls_arg.into_rust()),*) 127 | } 128 | } 129 | } 130 | }; 131 | 132 | // Used for element callbacks. 133 | // (resolve args version) 134 | ( 135 | $(#[$trait_attr:meta])* // allow doc comments here 136 | pub trait $trait_name:ident where Self: Element { 137 | let name = $cb_name:expr; 138 | extern fn listener(ih: *mut iup_sys::Ihandle $(, $ls_arg:ident: $ls_arg_ty:ty)*) -> CallbackReturn; 139 | 140 | fn $set_method:ident(&mut self, cb: F) -> Self; 141 | fn $remove_method:ident(&mut self) -> Option>; 142 | 143 | fn resolve_args($aa_argself:ident: Self, $($aa_arg:ident: $aa_arg_ty:ty),*) 144 | -> (Self, $($aa_ret_ty:ty),*) 145 | $resolve_args:expr 146 | } 147 | 148 | ) => { 149 | 150 | $(#[$trait_attr])* 151 | pub trait $trait_name where Self: $crate::Element + 'static { 152 | 153 | fn $set_method(&mut self, cb: F) -> Self 154 | where F: $crate::callback::Callback<(Self, $($fn_arg_ty),*)> { 155 | 156 | use std::mem::transmute; 157 | use libc::c_int; 158 | use $crate::iup_sys; 159 | #[allow(unused_imports)] 160 | use $crate::callback::IntoRust; 161 | 162 | fn resolve_args($aa_argself: Self0, $($aa_arg: $aa_arg_ty),*) 163 | -> (Self0, $($aa_ret_ty),*) { 164 | $resolve_args 165 | } 166 | 167 | extern "C" fn listener(ih: *mut iup_sys::Ihandle, $($ls_arg: $ls_arg_ty),*) -> c_int { 168 | let fbox: &mut Box<_> = get_fbox_callback!(ih, $cb_name, Callback<(Self0, $($fn_arg_ty),*)>); 169 | let element = unsafe { ::from_raw_unchecked(ih) }; 170 | fbox.on_callback(resolve_args::(element, $($ls_arg),*)) 171 | } 172 | 173 | unsafe { 174 | set_fbox_callback!(self.raw(), $cb_name, listener::, cb, 175 | Callback<(Self, $($fn_arg_ty),*)>); 176 | } 177 | 178 | self.clone() 179 | } 180 | 181 | fn $remove_method(&mut self) 182 | -> Option>> { 183 | unsafe { 184 | let old_cb = clear_fbox_callback!(self.raw(), $cb_name, 185 | Callback<(Self, $($fn_arg_ty),*)>); 186 | old_cb 187 | } 188 | } 189 | } 190 | }; 191 | 192 | // Used for global callbacks. 193 | ( 194 | let name = $cb_name:expr; 195 | extern fn listener($($ls_arg:ident: $ls_arg_ty:ty),*) -> CallbackReturn; 196 | $(#[$set_func_attr:meta])* 197 | pub fn $set_func:ident(cb: F); 198 | $(#[$rem_func_attr:meta])* 199 | pub fn $remove_func:ident() -> Option>; 200 | ) => { 201 | 202 | $(#[$set_func_attr])* 203 | pub fn $set_func(cb: F) 204 | where F: $crate::callback::Callback<($($fn_arg_ty),*)> { 205 | 206 | use std::mem::transmute; 207 | use std::ptr; 208 | use libc::c_int; 209 | use $crate::iup_sys; 210 | #[allow(unused_imports)] 211 | use $crate::callback::IntoRust; 212 | 213 | extern "C" fn listener($($ls_arg: $ls_arg_ty),*) -> c_int { 214 | let fbox: &mut Box<_> = get_fbox_callback!(ptr::null_mut(), $cb_name, Callback<($($fn_arg_ty),*)>); 215 | fbox.on_callback(($($ls_arg.into_rust()),*)) 216 | } 217 | 218 | unsafe { 219 | set_fbox_callback!(ptr::null_mut(), $cb_name, listener, cb, 220 | Callback<($($fn_arg_ty),*)>); 221 | } 222 | } 223 | 224 | $(#[$rem_func_attr])* 225 | pub fn $remove_func() 226 | -> Option>> { 227 | unsafe { 228 | //use std::ptr; 229 | let old_cb = clear_fbox_callback!(ptr::null_mut(), $cb_name, 230 | Callback<($($fn_arg_ty),*)>); 231 | old_cb 232 | } 233 | } 234 | }; 235 | } 236 | 237 | /// Drops the closure associated with the `$cb_name` (literal) callback in the element `$ih`. 238 | /// 239 | /// This is a **very hacky** method to free boxed closures, it takes advantage of the layout 240 | /// of the dynamic dispatching of TraitObject to the destructor and also the fact our closures 241 | /// are 'static (thus `Any`). 242 | /// 243 | /// For this very reason this may not work on future versions of Rust since the language provides 244 | /// no binary-compatibility guarantances between versions. 245 | /// 246 | /// It was implemented this way to avoid [too much] extra work for freeing each closure, but as 247 | /// soon as the library gets more mature it's recommended to find a replacement for this method. 248 | macro_rules! drop_callback { 249 | ($ih:ident, $cb_name:expr) => {{ 250 | use std::mem::transmute; 251 | use std::any::Any; 252 | let capsule_box = iup_sys::IupGetAttribute($ih, fbox_c_str!($cb_name)) 253 | as *mut Box; // HACK HACK HACK!!!! 254 | if !capsule_box.is_null() { 255 | // TODO when Box::from_raw gets stable use it instead of transmute here. 256 | let inner_box: Box> = transmute(capsule_box); 257 | drop(inner_box); 258 | } 259 | }} 260 | } 261 | -------------------------------------------------------------------------------- /src/callback/mod.rs: -------------------------------------------------------------------------------- 1 | //! Event-driven communication. 2 | 3 | use iup_sys; 4 | use libc::{c_char, c_int}; 5 | use std::path::PathBuf; 6 | use std::char; 7 | 8 | #[macro_use] 9 | mod macros; 10 | pub mod callbacks; 11 | pub use self::callbacks::*; 12 | 13 | pub mod button; 14 | 15 | // This is called right when a IUP element is being destroyed and it should free up all data 16 | // associated with callbacks. Just use the `drop_callback!` macro for each callback implemented. 17 | #[doc(hidden)] 18 | pub unsafe fn drop_callbacks(ih: *mut iup_sys::Ihandle) { 19 | // Prehaps this isn't the best way to get on it as we might forget to add things... 20 | 21 | // button.rs 22 | drop_callback!(ih, "BUTTON_CB"); 23 | drop_callback!(ih, "MOTION_CB"); 24 | 25 | // callbacks.rs 26 | drop_callback!(ih, "ACTION"); 27 | drop_callback!(ih, "ACTION_CB"); 28 | drop_callback!(ih, "LDESTROY_CB"); 29 | drop_callback!(ih, "MAP_CB"); 30 | drop_callback!(ih, "UNMAP_CB"); 31 | drop_callback!(ih, "GETFOCUS_CB"); 32 | drop_callback!(ih, "KILLFOCUS_CB"); 33 | drop_callback!(ih, "ENTERWINDOW_CB"); 34 | drop_callback!(ih, "LEAVEWINDOW_CB"); 35 | drop_callback!(ih, "HELP_CB"); 36 | drop_callback!(ih, "CARET_CB"); 37 | drop_callback!(ih, "SPIN_CB"); 38 | drop_callback!(ih, "VALUECHANGED_CB"); 39 | drop_callback!(ih, "DROPFILES_CB"); 40 | drop_callback!(ih, "CLOSE_CB"); 41 | drop_callback!(ih, "MOVE_CB"); 42 | drop_callback!(ih, "RESIZE_CB"); 43 | 44 | // dialog.rs 45 | drop_callback!(ih, "COPYDATA_CB"); 46 | drop_callback!(ih, "MDIACTIVATE_CB"); 47 | drop_callback!(ih, "SHOW_CB"); 48 | drop_callback!(ih, "TRAYCLICK_CB"); 49 | } 50 | 51 | 52 | 53 | 54 | /// Return this from a callback to tell the framework a non-default action to be performed. 55 | /// 56 | /// Not all callbacks accepts `Close`, `Ignore` or `Continue`, check their respective docs. 57 | #[derive(Copy, Clone, PartialEq, Eq)] 58 | pub enum CallbackReturn { 59 | /// The default `CallbackReturn`, does nothing when returned. 60 | Default, 61 | /// If this is returned from a callback, then when the callback returns the dialog containing 62 | /// the element on which the callback was invoked will be closed. 63 | Close, 64 | /// Callback specific, check the callback documentation to see if it accepts this return value 65 | /// and it's effect. 66 | Ignore, 67 | /// Callback specific, check the callback documentation to see if it accepts this return value 68 | /// and it's effect. 69 | Continue, 70 | /// Callback specific, check the callback documentation to see if it accepts this return value 71 | /// and it's effect. 72 | Char(char), 73 | } 74 | 75 | impl CallbackReturn { 76 | fn to_raw(self) -> c_int { 77 | use self::CallbackReturn::*; 78 | match self { 79 | Close => iup_sys::IUP_CLOSE, 80 | Default => iup_sys::IUP_DEFAULT, 81 | Ignore => iup_sys::IUP_IGNORE, 82 | Continue => iup_sys::IUP_CONTINUE, 83 | Char(c) => c as c_int, 84 | } 85 | } 86 | } 87 | 88 | // This allows returning '()' from a callback instead of CallbackReturn. 89 | impl From<()> for CallbackReturn { 90 | fn from(_: ()) -> CallbackReturn { 91 | CallbackReturn::Default 92 | } 93 | } 94 | 95 | pub trait Callback : 'static { 96 | fn on_callback(&mut self, args: Args) -> c_int; 97 | } 98 | 99 | impl, F: 'static> Callback for F where F: FnMut(Args) -> Out { 100 | /// Because of the `impl From<()> for CallbackReturn`, closures that return `()` can be 101 | /// accepted by this impl. 102 | fn on_callback(&mut self, args: Args) -> c_int { 103 | let r = self(args).into(); 104 | r.to_raw() 105 | } 106 | } 107 | 108 | /// This is a internal trait used to convert IUP C types into IUP Rust types in callbacks. 109 | /// 110 | /// For instance BUTTON_CB has a `char* status` parameter that must be abstracted into another 111 | /// type (e.g. `KeyStatus`). 112 | /// 113 | /// This trait method `into_rust` is called from the `impl_callback!` macro. 114 | #[doc(hidden)] 115 | pub trait IntoRust { 116 | fn into_rust(self) -> T; 117 | } 118 | 119 | impl IntoRust for c_int { 120 | fn into_rust(self) -> i32 { 121 | self as i32 122 | } 123 | } 124 | 125 | impl IntoRust for c_int { 126 | fn into_rust(self) -> u32 { 127 | self as u32 128 | } 129 | } 130 | 131 | impl IntoRust for c_int { 132 | fn into_rust(self) -> usize { 133 | self as usize 134 | } 135 | } 136 | 137 | impl IntoRust for c_int { 138 | fn into_rust(self) -> bool { 139 | self != 0 140 | } 141 | } 142 | 143 | impl IntoRust for *const c_char { 144 | fn into_rust(self) -> PathBuf { 145 | PathBuf::from(string_from_cstr!(self)) 146 | } 147 | } 148 | 149 | impl IntoRust for *const c_char { 150 | fn into_rust(self) -> String { 151 | string_from_cstr!(self) 152 | } 153 | } 154 | 155 | impl IntoRust> for c_int { 156 | fn into_rust(self) -> Option { 157 | if self == 0 { None } else { Some(char::from_u32(self as u32).unwrap()) } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/clipboard.rs: -------------------------------------------------------------------------------- 1 | //! Access to the system clipboard. 2 | //! 3 | //! The clipboard is a software facility used for short-term data storage and/or data transfer 4 | //! between documents or applications, via copy and paste operations. 5 | //! 6 | //! The clipboard is user-driven. A window should transfer data to or from the clipboard only in 7 | //! response to a command from the user. A window must not use the clipboard to transfer data 8 | //! without the user's knowledge. 9 | //! 10 | //! # Clipboard Data Formats 11 | //! 12 | //! A window can place more than one object on the clipboard, each representing the same 13 | //! information in a different clipboard format. Users need not be aware of the clipboard formats 14 | //! used for an object on the clipboard. 15 | //! 16 | //! Many applications work with data that cannot be translated into a standard clipboard format 17 | //! without loss of information. These applications can create their own clipboard formats. 18 | //! A clipboard format that is defined by an application, is called a registered clipboard format. 19 | //! For example, if a word-processing application copied formatted text to the clipboard using a 20 | //! standard text format, the formatting information would be lost. 21 | //! The solution would be to register a new clipboard format, such as Rich Text Format (RTF). 22 | //! 23 | //! To register a new clipboard format, use the `Clipboard::add_format` function. This format can 24 | //! then be used by `Clipboard::data` and `Clipboard::set_data` functions. 25 | //! 26 | //! ## Standard Formats 27 | //! 28 | //! IUP gives the user two platform-dependent formats, text and image. If any other is required 29 | //! the user may need to use a custom data format. 30 | //! 31 | //! 32 | //! ## Multiple Clipboard Formats 33 | //! 34 | //! A window can place more than one clipboard object on the clipboard, each representing the same 35 | //! information in a different clipboard format. When placing information on the clipboard, the 36 | //! window should provide data in as many formats as possible. 37 | //! 38 | //! It's a recommended that clipboard formats that contain the most information should be placed on 39 | //! the clipboard first, followed by less descriptive formats. This is because OS-specific functi- 40 | //! onalities usually iterate on the clipboard data in the same order it was placed and uses the 41 | //! first it is actually able to handle. 42 | //! 43 | //! For example, suppose a user copies styled text from a word-processing document. The window 44 | //! containing the document might first place data on the clipboard in a registered format, such 45 | //! as RTF. Subsequently, the window would place data on the clipboard in a less descriptive 46 | //! format, such as text. 47 | //! 48 | //! When the content of the clipboard is pasted into another window, the window retrieves data in 49 | //! the most descriptive format it recognizes. If the window recognizes RTF, the corresponding data 50 | //! is pasted into the document. Otherwise, the text data is pasted into the document and the 51 | //! formatting information is lost. 52 | //! 53 | //! Explanation borrowed off [MSDN][MSDN]. 54 | //! [MSDN]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms649013%28v=vs.85%29.aspx 55 | //! 56 | //! ## Example 57 | //! 58 | //! ### Copying 59 | //! 60 | //! ```no_run 61 | //! # use iup::clipboard::Clipboard; 62 | //! // Sets the user clipboard to have a text in two possible formats, HTML (as a custom format 63 | //! // using it's MIME type) and text. If the application that receives the paste supports 64 | //! // any of these it'll use it. 65 | //! Clipboard::new() 66 | //! .clear() 67 | //! .add_format("text/html") 68 | //! .set_data("text/html", r"This is my awesome text") 69 | //! .set_text("This is my awesome text"); 70 | //! ``` 71 | //! 72 | //! ### Pasting 73 | //! 74 | //! ```no_run 75 | //! # use iup::clipboard::Clipboard; 76 | //! let mut clipboard = Clipboard::new(); 77 | //! if let Some(html) = clipboard.add_format("text/html").data("text/html") { 78 | //! // Use HTML pasted content. 79 | //! } else if let Some(text) = clipboard.text() { 80 | //! // Use text pasted content. 81 | //! } 82 | //! ``` 83 | //! 84 | use iup_sys; 85 | use std::ptr; 86 | use std::slice; 87 | 88 | use Element; 89 | use Guard; 90 | use image::ImageElement; 91 | 92 | /// An element that allows access to the clipboard. 93 | /// 94 | /// You can use only one for the entire application because it does not store any data inside. 95 | /// Or you can simply create and drop every time you need to copy or paste. 96 | /// 97 | /// See the clipboard module documentation for more details on how the system clipboard works. 98 | /// 99 | /// Other platform-dependent attributes can be found on the [IUP Clipboard Documentation][1]. 100 | /// [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/elem/iupclipboard.html 101 | /// 102 | /// # Ownership 103 | /// 104 | /// The clipboard must be manually destroyed, thus for the user safety it returns a guarded object 105 | /// on the `new` constructor. 106 | /// 107 | /// Please refer to the crate level documentation of IUP-Rust (the main doc page) for details on 108 | /// ownership of elements. 109 | pub struct Clipboard(*mut iup_sys::Ihandle); 110 | 111 | impl Clipboard { 112 | /// Creates a new clipboard operarator. 113 | pub fn new() -> Guard { 114 | Guard::new( 115 | Clipboard::from_raw(unsafe { iup_sys::IupClipboard() }) 116 | ) 117 | } 118 | 119 | /// Clears any data on the clipboard. 120 | pub fn clear(&mut self) -> Self { 121 | self.set_attrib_data("FORMATDATA", ptr::null()) 122 | } 123 | 124 | /// Register a custom format for clipboard data given its name. 125 | pub fn add_format>(&mut self, format: S) -> Self { 126 | self.set_attrib("ADDFORMAT", format) 127 | } 128 | 129 | /// Informs if there is a text available at the clipboard. 130 | pub fn has_text(&mut self) -> bool { 131 | self.attrib_bool("TEXTAVAILABLE").unwrap() 132 | } 133 | 134 | /// Copy text into the clipboard. 135 | pub fn set_text>(&mut self, text: S) -> Self { 136 | self.set_attrib("TEXT", text) 137 | } 138 | 139 | /// Paste text off the clipboard. 140 | pub fn text(&mut self) -> Option { 141 | self.attrib("TEXT") 142 | } 143 | 144 | /// Informs if there is a image available at the clipboard. 145 | pub fn has_image(&mut self) -> bool { 146 | self.attrib_bool("IMAGEAVAILABLE").unwrap() 147 | } 148 | 149 | /// Copy text into the clipboard. 150 | pub fn set_image(&mut self, image: &I) -> Self { 151 | self.set_attrib_handle("IMAGE", *image) 152 | } 153 | 154 | 155 | /// Informs if there is data of the specified format available at the clipboard. 156 | pub fn has_data>(&mut self, format: S) -> bool { 157 | self.set_attrib("FORMAT", format); 158 | self.attrib_bool("FORMATAVAILABLE").unwrap() 159 | } 160 | 161 | /// Copy data from the specified format into the clipboard. 162 | pub fn set_data(&mut self, format: S, data: D) -> Self 163 | where S: Into, D: AsRef<[u8]> { 164 | let data = data.as_ref(); 165 | self.set_attrib("FORMAT", format); 166 | self.set_attrib("FORMATDATASIZE", data.len().to_string()); 167 | self.set_attrib_data("FORMATDATA", data.as_ptr() as *const _) 168 | } 169 | 170 | /// Paste data from the specified format off the clipboard. 171 | pub fn data>(&mut self, format: S) -> Option> { 172 | self.set_attrib("FORMAT", format); 173 | match self.attrib_data("FORMATDATA") as *const u8 { 174 | ptr if !ptr.is_null() => unsafe { 175 | let len: usize = self.attrib_parse("FORMATDATASIZE").unwrap(); 176 | Some(slice::from_raw_parts(ptr, len).into_iter().cloned().collect()) 177 | }, 178 | _ => None, 179 | } 180 | } 181 | } 182 | 183 | impl_element!(Clipboard, "clipboard"); 184 | 185 | 186 | // TODO please someone test the HTML copying example of the module documentation! In my experience 187 | // when I paste it in another application that supports HTML pasting it gives me some weirdo 188 | // chinese characters, after some testing it seems to require a UTF-16 stream... A place that 189 | // allows pasting HTML is this editor: http://versadus.com/flavius/editor/_samples/editor-html-online.html 190 | -------------------------------------------------------------------------------- /src/control/button.rs: -------------------------------------------------------------------------------- 1 | use iup_sys; 2 | use std::ptr; 3 | use std::ffi::CString; 4 | 5 | use Element; 6 | 7 | /// See the [IUP Button Documentation][1]. 8 | /// [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/elem/iupbutton.html 9 | pub struct Button(*mut iup_sys::Ihandle); 10 | 11 | impl Button { 12 | /// Creates a button with no text. 13 | pub fn new() -> Button { 14 | unsafe { Button::from_raw(iup_sys::IupButton(ptr::null_mut(), ptr::null_mut())) } 15 | } 16 | 17 | /// Creates a button with the specified text. 18 | pub fn with_title>(title: S) -> Button { 19 | let ctitle = CString::new(title.into()).unwrap(); 20 | unsafe { Button::from_raw(iup_sys::IupButton(ctitle.as_ptr(), ptr::null_mut())) } 21 | } 22 | 23 | // TODO with_image 24 | } 25 | 26 | impl_widget!(Button, "button"); 27 | impl ::callback::MapCb for Button {} 28 | impl ::callback::UnmapCb for Button {} 29 | impl ::callback::GetFocusCb for Button {} 30 | impl ::callback::KillFocusCb for Button {} 31 | impl ::callback::EnterWindowCb for Button {} 32 | impl ::callback::LeaveWindowCb for Button {} 33 | impl ::callback::HelpCb for Button {} 34 | // TODO impl K_ callbacks when it's implemented. 35 | 36 | /// Action generated when the button 1 (usually left) is selected. 37 | /// 38 | /// This callback is called only after the mouse is released and when it is released 39 | /// inside the button area. 40 | /// 41 | /// `CallbackReturn::Close` will be processed. 42 | impl ::callback::Action for Button {} 43 | 44 | /// Action generated when any mouse button is pressed and released. 45 | impl ::callback::button::ButtonCb for Button {} 46 | -------------------------------------------------------------------------------- /src/control/frame.rs: -------------------------------------------------------------------------------- 1 | use iup_sys; 2 | use std::ptr; 3 | 4 | use Element; 5 | 6 | /// See the [IUP Frame Documentation][1]. 7 | /// [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/elem/iupframe.html 8 | pub struct Frame(*mut iup_sys::Ihandle); 9 | 10 | impl Frame { 11 | /// Creates a frame with a child element. 12 | pub fn new(child: E) -> Frame { 13 | unsafe { Frame::from_raw(iup_sys::IupFrame(child.raw())) } 14 | } 15 | 16 | /// Creates a frame with no elements. 17 | pub fn new_empty() -> Frame { 18 | unsafe { Frame::from_raw(iup_sys::IupFrame(ptr::null_mut())) } 19 | } 20 | } 21 | 22 | impl_widget_container!(Frame, "frame"); 23 | impl ::callback::MapCb for Frame {} 24 | impl ::callback::UnmapCb for Frame {} 25 | -------------------------------------------------------------------------------- /src/control/label.rs: -------------------------------------------------------------------------------- 1 | use iup_sys; 2 | use std::ptr; 3 | use std::ffi::CString; 4 | 5 | use Element; 6 | use Orientation; 7 | 8 | /// See the [IUP Label Documentation][1]. 9 | /// [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/elem/iuplabel.html 10 | pub struct Label(*mut iup_sys::Ihandle); 11 | 12 | impl Label { 13 | /// Creates a label with no predefined text, image or separator. 14 | pub fn new() -> Label { 15 | unsafe { Label::from_raw(iup_sys::IupLabel(ptr::null_mut())) } 16 | } 17 | 18 | /// Creates a label separator in the specified orientation. 19 | pub fn new_separator(orient: Orientation) -> Label { 20 | Label::new().set_attrib_data("SEPARATOR", orient.as_cstr() as *const _) 21 | } 22 | 23 | // TODO with_image(...) 24 | 25 | /// Creates a label interface element which displays a text. 26 | pub fn with_title>(label: S) -> Label { 27 | let clabel = CString::new(label.into()).unwrap(); 28 | unsafe { Label::from_raw(iup_sys::IupLabel(clabel.as_ptr())) } 29 | } 30 | } 31 | 32 | impl_widget!(Label, "label"); 33 | impl ::callback::MapCb for Label {} 34 | impl ::callback::UnmapCb for Label {} 35 | impl ::callback::EnterWindowCb for Label {} 36 | impl ::callback::LeaveWindowCb for Label {} 37 | 38 | /// Action generated when any mouse button is pressed or released. 39 | impl ::callback::button::ButtonCb for Label {} 40 | 41 | /// Action generated when one or more files are dropped in the element. 42 | impl ::callback::DropFilesCb for Label {} 43 | 44 | // TODO impl future DragSource and DragTarget traits. 45 | -------------------------------------------------------------------------------- /src/control/list.rs: -------------------------------------------------------------------------------- 1 | use iup_sys; 2 | use libc::{c_char, c_int}; 3 | use std::ptr; 4 | 5 | use Element; 6 | use callback::IntoRust; 7 | 8 | /// See the [IUP List Documentation][1]. 9 | /// [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/elem/iuplist.html 10 | pub struct List(*mut iup_sys::Ihandle); 11 | 12 | impl List { 13 | /// Creates an interface element that displays a list of items. 14 | pub fn new() -> List { 15 | unsafe { List::from_raw(iup_sys::IupList(ptr::null())) } 16 | } 17 | 18 | /// Creates an interface element that displays a list of items in a dropdown. 19 | pub fn new_dropdown() -> List { 20 | List::new().set_attrib_data("DROPDOWN", cstr!("YES") as *const _) 21 | } 22 | 23 | /// Creates an interface element that displays a list of items with a edit box for text input. 24 | pub fn new_editbox() -> List { 25 | List::new().set_attrib_data("EDITBOX", cstr!("YES") as *const _) 26 | } 27 | 28 | // TODO how to call the fusion of dropbox and editbox? 29 | 30 | /// Sets the list of items. 31 | pub fn set_items(&mut self, items: A) -> Self where A: AsRef<[String]> { 32 | self.clear(); 33 | for (i, value) in items.as_ref().iter().enumerate() { 34 | self.set_attrib((i+1).to_string(), value.clone()); 35 | } 36 | *self 37 | } 38 | 39 | /// Gets the item at the specified id (starts from 1). 40 | /// 41 | /// # Panics 42 | /// Panics if id is less than 1. 43 | pub fn item(&self, id: u32) -> Option { 44 | assert!(id > 0); 45 | self.attrib(id.to_string()) 46 | } 47 | 48 | /// Clears the list of items. Ignored if called before being mapped. 49 | pub fn clear(&mut self) -> Self { 50 | self.set_attrib("REMOVEITEM", "ALL") 51 | } 52 | } 53 | 54 | impl_widget!(List, "list"); 55 | 56 | /// Returns a list item position from it's xy coordinate. 57 | impl ::element::ConvertXYToPos for List {} 58 | 59 | impl ::callback::MapCb for List {} 60 | impl ::callback::UnmapCb for List {} 61 | impl ::callback::GetFocusCb for List {} 62 | impl ::callback::KillFocusCb for List {} 63 | impl ::callback::EnterWindowCb for List {} 64 | impl ::callback::LeaveWindowCb for List {} 65 | impl ::callback::HelpCb for List {} 66 | // TODO impl K_ callbacks when it's implemented. 67 | 68 | // TODO impl future DragSource and DragTarget traits. 69 | 70 | /// Action generated when any mouse button is pressed or released inside the list. 71 | /// 72 | /// Called only when DROPDOWN=NO. If the list has an editbox the message is called when cursor 73 | /// is at the listbox only (ignored at the editbox). 74 | /// 75 | /// Use `convert_xy_to_pos` to convert (x,y) coordinates in item position. 76 | impl ::callback::button::ButtonCb for List {} 77 | 78 | /// Action generated when the caret/cursor position is changed. Valid only when EDITBOX=YES. 79 | /// 80 | /// For lists `lin` (2nd param) is always *1*, and pos (3rd param) is always *col-1*. 81 | impl ::callback::CaretCb for List {} 82 | 83 | /// Action generated when one or more files are dropped in the element. 84 | impl ::callback::DropFilesCb for List {} 85 | 86 | /// Action generated when the mouse is moved over the list. Called only when DROPDOWN=NO. 87 | /// 88 | /// If the list has an editbox the message is called when cursor is at the listbox only (ignored 89 | /// at the editbox). 90 | /// 91 | /// Use `convert_xy_to_pos` to convert (x,y) coordinates in item position. 92 | impl ::callback::button::MotionCb for List {} 93 | 94 | /// Called after the value was interactively changed by the user. Called when the selection is 95 | /// changed or when the text is edited. 96 | impl ::callback::ValueChangedCb for List {} 97 | 98 | // TODO: 99 | // DBLCLICK_CB 100 | // MULTISELECT_CB 101 | // EDIT_CB 102 | // DROPDOWN_CB 103 | // DRAGDROP_CB 104 | 105 | /// See the `ListAction` documentation. 106 | impl self::ListAction for List {} 107 | impl_callback! { 108 | #[doc="Action generated when the state of an item in the list is changed."] 109 | #[doc="Also provides information on the changed item."] 110 | #[doc=""] 111 | #[doc="The `String` parameter is the text of the changed item."] 112 | #[doc="The `u32` parameter is the number of the changed item starting at 1."] 113 | #[doc="The `ListItemState` parameter is whether the item was selected or deselected. "] 114 | #[doc=""] 115 | #[doc="The `ListItemState::Deselected` is simulated internally by IUP in all systems."] 116 | #[doc="If you add or remove items to/from the list and you count on the `ListItemState::Deselected`"] 117 | #[doc="value, then after adding/removing items set the VALUE attribute to ensure proper"] 118 | #[doc="`ListItemState::Deslected` value."] 119 | pub trait ListAction where Self: Element { 120 | let name = "ACTION"; 121 | extern fn listener(ih: *mut iup_sys::Ihandle, text: *const c_char, item: c_int, state: c_int) -> CallbackReturn; 122 | fn set_action(&mut self, cb: F) -> Self; 123 | fn remove_action(&mut self) -> Option>; 124 | } 125 | } 126 | 127 | pub enum ListItemState { 128 | Deselected, 129 | Selected, 130 | } 131 | 132 | impl IntoRust for c_int { 133 | fn into_rust(self) -> ListItemState { 134 | if self != 0 { ListItemState::Selected } else { ListItemState::Deselected } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/control/mod.rs: -------------------------------------------------------------------------------- 1 | //! See [IUP Controls][1]. 2 | //! [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/controls.html 3 | 4 | pub mod label; 5 | pub mod text; 6 | pub mod button; 7 | pub mod progress; 8 | pub mod toggle; 9 | pub mod frame; 10 | pub mod list; 11 | 12 | pub use self::text::{Text, TextAction}; 13 | pub use self::label::Label; 14 | pub use self::button::Button; 15 | pub use self::progress::ProgressBar; 16 | pub use self::toggle::{Toggle, ToggleAction}; 17 | pub use self::frame::Frame; 18 | pub use self::list::{List, ListAction}; 19 | -------------------------------------------------------------------------------- /src/control/progress.rs: -------------------------------------------------------------------------------- 1 | use iup_sys; 2 | 3 | use Element; 4 | 5 | /// See the [IUP Progress Bar Documentation][1]. 6 | /// [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/elem/iupprogressbar.html 7 | pub struct ProgressBar(*mut iup_sys::Ihandle); 8 | 9 | impl ProgressBar { 10 | /// Creates a progress bar control. 11 | pub fn new() -> ProgressBar { 12 | unsafe { ProgressBar::from_raw(iup_sys::IupProgressBar()) } 13 | } 14 | } 15 | 16 | impl_widget!(ProgressBar, "progressbar"); 17 | impl ::callback::MapCb for ProgressBar {} 18 | impl ::callback::UnmapCb for ProgressBar {} 19 | -------------------------------------------------------------------------------- /src/control/text.rs: -------------------------------------------------------------------------------- 1 | use iup_sys; 2 | use libc::{c_char, c_int}; 3 | use std::{ptr}; 4 | 5 | use Element; 6 | 7 | /// See the [IUP Text Documentation][1]. 8 | /// [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/elem/iuptext.html 9 | pub struct Text(*mut iup_sys::Ihandle); 10 | 11 | impl Text { 12 | /// Creates a editable text-field. 13 | pub fn new() -> Text { 14 | unsafe { Text::from_raw(iup_sys::IupText(ptr::null_mut())) } 15 | } 16 | 17 | /// Creates a spin control. 18 | /// 19 | /// The spin increments and decrements an integer number. 20 | pub fn new_spin() -> Text { 21 | Text::new().set_attrib_data("SPIN", cstr!("YES") as *const _) 22 | } 23 | 24 | /// Converts a (lin, col) character positioning into an absolute position. 25 | /// 26 | /// lin and col starts at 1, pos starts at 0. For single line controls pos is always *col-1*. 27 | pub fn convert_lincol_to_pos(&self, lin: i32, col: i32) -> usize { 28 | unsafe { 29 | let mut r: c_int = 0; 30 | iup_sys::IupTextConvertLinColToPos(self.raw(), lin, col, &mut r); 31 | r as usize 32 | } 33 | } 34 | 35 | /// Converts an absolute position into a (lin, col) character positioning. 36 | /// 37 | /// lin and col starts at 1, pos starts at 0. 38 | /// For single line controls lin is always 1, and col is always *pos+1*. 39 | pub fn convert_pos_to_lincol(&self, pos: usize) -> (i32, i32) { 40 | unsafe { 41 | let (mut lin, mut col): (c_int, c_int) = (0, 0); 42 | iup_sys::IupTextConvertPosToLinCol(self.raw(), pos as c_int, &mut lin, &mut col); 43 | (lin as i32, col as i32) 44 | } 45 | } 46 | } 47 | 48 | impl_widget!(Text, "text"); 49 | 50 | /// Returns a position in the string. 51 | impl ::element::ConvertXYToPos for Text {} 52 | 53 | impl ::callback::MapCb for Text {} 54 | impl ::callback::UnmapCb for Text {} 55 | impl ::callback::GetFocusCb for Text {} 56 | impl ::callback::KillFocusCb for Text {} 57 | impl ::callback::EnterWindowCb for Text {} 58 | impl ::callback::LeaveWindowCb for Text {} 59 | impl ::callback::HelpCb for Text {} 60 | // TODO impl K_ callbacks when it's implemented. 61 | 62 | // TODO impl future DragSource and DragTarget traits. 63 | 64 | /// Action generated when any mouse button is pressed or released. 65 | /// 66 | /// Use `convert_xy_to_pos` to convert (x,y) coordinates in character positioning. 67 | impl ::callback::button::ButtonCb for Text {} 68 | 69 | /// Action generated when the caret/cursor position is changed. 70 | /// 71 | /// For single line controls `lin` (2nd param) is always *1*, and `pos` (3rd param) is always *col-1*. 72 | impl ::callback::CaretCb for Text {} 73 | 74 | /// Action generated when one or more files are dropped in the element. 75 | impl ::callback::DropFilesCb for Text {} 76 | 77 | /// Action generated when the mouse is moved. 78 | /// 79 | /// Use `convert_xy_to_pos` to convert (x,y) coordinates in character positioning. 80 | impl ::callback::button::MotionCb for Text {} 81 | 82 | /// Action generated when a spin button is pressed. 83 | /// 84 | /// Valid only when the element is a spin (`Text::new_spin` or SPIN=YES attribute). 85 | /// When this callback is called the ACTION callback is not called. 86 | /// The VALUE attribute can be changed during this callback only if SPINAUTO=NO. 87 | /// 88 | /// The `i32` parameter received by the callback is the value of the spin (after incremented). 89 | /// 90 | /// May return `CallbackReturn::Ignore` but that is only allowed on Windows and Motif. 91 | impl ::callback::SpinCb for Text {} 92 | 93 | /// Called after the value was interactively changed by the user. 94 | impl ::callback::ValueChangedCb for Text {} 95 | 96 | /// See the `TextAction` documentation. 97 | impl self::TextAction for Text {} 98 | impl_callback! { 99 | #[doc="Action generated when the text is edited, but before its value is actually changed."] 100 | #[doc=""] 101 | #[doc="Can be generated when using the keyboard, undo system or from the clipboard."] 102 | #[doc=""] 103 | #[doc="The following values can be returned from the callback:"] 104 | #[doc=" + `CallbackReturn::Default` or `()` for the default reaction."] 105 | #[doc=" + `CallbackReturn::Close` will be processed, but the change will be ignored."] 106 | #[doc=" + `CallbackReturn::Ignore` ignores the new value."] 107 | #[doc=" + An `CallbackReturn::Char`, if the received `c` is `None` or NUL is returned it'll act"] 108 | #[doc=" just like `CallbackReturn::Default` otherwise the returned character will be used"] 109 | #[doc=" instead of the one sent to the callback."] 110 | #[doc=""] 111 | #[doc="The VALUE attribute can be changed only if `CallbackReturn::Ignore` is returned."] 112 | #[doc=""] 113 | #[doc="**NOTE:** The **character** received and returned must be in the ASCII range of"] 114 | #[doc=" UTF-8 (0-127). No restriction on the `newvalue` string, when the value added to the VALUE"] 115 | #[doc="is a non-ASCII character the `c` parameter will be `None` but the value is still used for"] 116 | #[doc="updating the VALUE attribute."] 117 | pub trait TextAction where Self: Element { 118 | let name = "ACTION"; 119 | extern fn listener(ih: *mut iup_sys::Ihandle, c: c_int, newvalue: *const c_char) -> CallbackReturn; 120 | fn set_action, String)>(&mut self, cb: F) -> Self; 121 | fn remove_action(&mut self) -> Option>; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/control/toggle.rs: -------------------------------------------------------------------------------- 1 | use iup_sys; 2 | use std::ptr; 3 | use std::ffi::CString; 4 | 5 | use Element; 6 | 7 | /// See the [IUP Toggle Documentation][1]. 8 | /// [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/elem/iuptoggle.html 9 | pub struct Toggle(*mut iup_sys::Ihandle); 10 | 11 | impl Toggle { 12 | /// Creates a toggle with no text. 13 | pub fn new() -> Toggle { 14 | unsafe { Toggle::from_raw(iup_sys::IupToggle(ptr::null_mut(), ptr::null_mut())) } 15 | } 16 | 17 | /// Creates a toggle with the specified text. 18 | pub fn with_title>(title: S) -> Toggle { 19 | let ctitle = CString::new(title.into()).unwrap(); 20 | unsafe { Toggle::from_raw(iup_sys::IupToggle(ctitle.as_ptr(), ptr::null_mut())) } 21 | } 22 | } 23 | 24 | impl_widget!(Toggle, "toggle"); 25 | impl ::callback::MapCb for Toggle {} 26 | impl ::callback::UnmapCb for Toggle {} 27 | impl ::callback::GetFocusCb for Toggle {} 28 | impl ::callback::KillFocusCb for Toggle {} 29 | impl ::callback::EnterWindowCb for Toggle {} 30 | impl ::callback::LeaveWindowCb for Toggle {} 31 | impl ::callback::HelpCb for Toggle {} 32 | // TODO impl K_ callbacks when it's implemented. 33 | 34 | /// Called after the value was interactively changed by the user. 35 | /// 36 | /// Called after the ACTION callback, but under the same context. 37 | impl ::callback::ValueChangedCb for Toggle {} 38 | 39 | /// See the `ToggleAction` documentation. 40 | impl self::ToggleAction for Toggle {} 41 | impl_callback! { 42 | #[doc="Action generated when the toggle's state (on/off) was changed."] 43 | #[doc=""] 44 | #[doc="The callback boolean parameter represents the state the toggle was switched to."] 45 | #[doc=""] 46 | #[doc="`CallbackReturn::Close` will be processed"] 47 | pub trait ToggleAction where Self: Element { 48 | let name = "ACTION"; 49 | extern fn listener(ih: *mut iup_sys::Ihandle, state: c_int) -> CallbackReturn; 50 | fn set_action(&mut self, cb: F) -> Self; 51 | fn remove_action(&mut self) -> Option>; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/dialog/alarm.rs: -------------------------------------------------------------------------------- 1 | use iup_sys; 2 | use std::ptr; 3 | use std::ffi::CString; 4 | 5 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 6 | pub enum AlarmButton { 7 | Button1, 8 | Button2, 9 | Button3, 10 | } 11 | 12 | /// Shows a modal dialog containing a message and up to three buttons. 13 | /// 14 | /// Returns the pressed button. 15 | /// 16 | /// See the [IUP Alarm Documentation][1]. 17 | /// [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/dlg/iupalarm.html 18 | pub fn alarm(title: S1, message: S2, button1: String, 19 | button2: Option, button3: Option) -> AlarmButton 20 | where S1: Into, S2: Into { 21 | 22 | let ctitle = CString::new(title.into()).unwrap(); 23 | let cmessage = CString::new(message.into()).unwrap(); 24 | let cbutton1 = CString::new(button1).unwrap(); 25 | let cbutton2 = button2.map(|s| CString::new(s).unwrap()); 26 | let cbutton3 = button3.map(|s| CString::new(s).unwrap()); 27 | 28 | let pressed = unsafe { 29 | iup_sys::IupAlarm(ctitle.as_ptr(), cmessage.as_ptr(), 30 | cbutton1.as_ptr(), 31 | cbutton2.map_or(ptr::null(), |cs| cs.as_ptr()), 32 | cbutton3.map_or(ptr::null(), |cs| cs.as_ptr())) 33 | }; 34 | 35 | match pressed { 36 | 1 => AlarmButton::Button1, 37 | 2 => AlarmButton::Button2, 38 | 3 => AlarmButton::Button3, 39 | _ => unreachable!(), 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/dialog/dialog.rs: -------------------------------------------------------------------------------- 1 | use iup_sys; 2 | use libc::{c_char, c_int}; 3 | use std::ptr; 4 | 5 | use Element; 6 | use callback::IntoRust; 7 | use callback::button::{MouseButton, MouseButtonState}; 8 | 9 | 10 | /// Dialog elements manages user interaction with the interface elements. 11 | /// For any interface element to be shown, it must be encapsulated in a dialog. 12 | /// 13 | /// See the [IUP Dialog Documentation][1]. 14 | /// [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/elem/iupdialog.html 15 | pub struct Dialog(*mut iup_sys::Ihandle); 16 | 17 | impl Dialog { 18 | /// Creates a dialog with a child element. 19 | pub fn new(child: E) -> Dialog { 20 | unsafe { Dialog::from_raw(iup_sys::IupDialog(child.raw())) } 21 | } 22 | 23 | /// Creates a dialog with no elements. 24 | pub fn new_empty() -> Dialog { 25 | unsafe { Dialog::from_raw(iup_sys::IupDialog(ptr::null_mut())) } 26 | } 27 | } 28 | 29 | impl_dialog!(Dialog, "dialog"); 30 | impl ::callback::MapCb for Dialog {} 31 | impl ::callback::UnmapCb for Dialog {} 32 | impl ::callback::GetFocusCb for Dialog {} 33 | impl ::callback::KillFocusCb for Dialog {} 34 | impl ::callback::EnterWindowCb for Dialog {} 35 | impl ::callback::LeaveWindowCb for Dialog {} 36 | impl ::callback::HelpCb for Dialog {} 37 | // TODO impl K_ callbacks when it's implemented. 38 | 39 | /// Called right before the dialog is closed. 40 | impl ::callback::CloseCb for Dialog {} 41 | 42 | /// Action generated when one or more files are dropped in the dialog. 43 | impl ::callback::DropFilesCb for Dialog {} 44 | 45 | /// Called after the dialog was moved on screen. 46 | /// 47 | /// The coordinates are the same as the SCREENPOSITION attribute. 48 | impl ::callback::MoveCb for Dialog {} 49 | 50 | /// Action generated when the dialog size is changed. 51 | /// 52 | /// This action is also generated when the dialog is mapped, after the map and before the show. 53 | /// 54 | /// If returns `CallbackReturn::Ignore` the dialog layout is **not** recalculated. 55 | impl ::callback::ResizeCb for Dialog {} 56 | 57 | /// See the `CopyDataCb` documentation. 58 | impl self::CopyDataCb for Dialog {} 59 | 60 | /// See the `MdiActivateCb` documentation. 61 | impl self::MdiActivateCb for Dialog {} 62 | 63 | /// See the `ShowCb` documentation. 64 | impl self::ShowCb for Dialog {} 65 | 66 | /// See the `TrayClickCb` documentation. 67 | impl self::TrayClickCb for Dialog {} 68 | 69 | 70 | 71 | impl_callback! { 72 | #[doc="[Windows Only]: Called at the first instance, when a second instance is running."] 73 | #[doc=""] 74 | #[doc="Must set the global attribute SINGLEINSTANCE to be called."] 75 | #[doc=""] 76 | #[doc="The `String` parameter is the command line of the second instance. "] 77 | pub trait CopyDataCb where Self: Element { 78 | let name = "COPYDATA_CB"; 79 | extern fn listener(ih: *mut iup_sys::Ihandle, cmdline: *const c_char, size: c_int) -> CallbackReturn; 80 | fn set_copydata_cb(&mut self, cb: F) -> Self; 81 | fn remove_copydata_cb(&mut self) -> Option>; 82 | 83 | fn resolve_args(elem: Self, cmdline: *const c_char, _size: c_int) -> (Self, String) { 84 | (elem, string_from_cstr!(cmdline)) 85 | } 86 | } 87 | } 88 | 89 | impl_callback! { 90 | #[doc="[Windows Only]: Called when a MDI child window is activated."] 91 | #[doc=""] 92 | #[doc="Only the MDI child receive this message. It is not called when the child is shown for the"] 93 | #[doc="first time."] 94 | #[doc=""] 95 | #[doc="See the [IUP Dialog Documentation][1] for information on MDI dialogs."] 96 | #[doc="[1]: http://webserver2.tecgraf.puc-rio.br/iup/en/elem/iupdialog.html"] 97 | pub trait MdiActivateCb where Self: Element { 98 | let name = "MDIACTIVATE_CB"; 99 | extern fn listener(ih: *mut iup_sys::Ihandle) -> CallbackReturn; 100 | fn set_mdiactivate_cb(&mut self, cb: F) -> Self; 101 | fn remove_mdiactivate_cb(&mut self) -> Option>; 102 | } 103 | } 104 | 105 | impl_callback! { 106 | #[doc="Called right after the dialog is showed, hidden, maximized, minimized or restored from"] 107 | #[doc="minimized/maximized."] 108 | #[doc=""] 109 | #[doc="This callback is called when those actions were performed by the user or programmatically"] 110 | #[doc="by the application."] 111 | #[doc=""] 112 | #[doc="`CallbackReturn::Close` will be processed."] 113 | pub trait ShowCb where Self: Element { 114 | let name = "SHOW_CB"; 115 | extern fn listener(ih: *mut iup_sys::Ihandle, state: c_int) -> CallbackReturn; 116 | fn set_move_cb(&mut self, cb: F) -> Self; 117 | fn remove_move_cb(&mut self) -> Option>; 118 | } 119 | } 120 | 121 | impl_callback! { 122 | #[doc="[Windows and GTK Only]: Called right after the mouse button is pressed or released over"] 123 | #[doc="the tray icon."] 124 | #[doc=""] 125 | #[doc="The fourth callback parameter is a bool indicating whether the button was double pressed."] 126 | #[doc=""] 127 | #[doc="`CallbackReturn::Close` will be processed."] 128 | pub trait TrayClickCb where Self: Element { 129 | let name = "TRAYCLICK_CB"; 130 | extern fn listener(ih: *mut iup_sys::Ihandle, but: c_int, pressed: c_int, dclick: c_int) -> CallbackReturn; 131 | fn set_trayclick_cb(&mut self, cb: F) -> Self; 132 | fn remove_trayclick_cb(&mut self) -> Option>; 133 | 134 | fn resolve_args(elem: Self, but: c_int, pressed: c_int, dclick: c_int) 135 | -> (Self, MouseButton, MouseButtonState, bool) { 136 | (elem, MouseButton::from_id(but), pressed.into_rust(), dclick.into_rust()) 137 | } 138 | } 139 | } 140 | 141 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 142 | pub enum ShowState { 143 | Hide, 144 | Show, 145 | /// Was minimized or maximized. 146 | Restore, 147 | Minimize, 148 | /// **Note:** Not received in Motif when activated from the maximize button 149 | Maximize, 150 | } 151 | 152 | impl IntoRust for c_int { 153 | fn into_rust(self) -> ShowState { 154 | match self { 155 | iup_sys::IUP_HIDE => ShowState::Hide, 156 | iup_sys::IUP_SHOW => ShowState::Show, 157 | iup_sys::IUP_RESTORE => ShowState::Restore, 158 | iup_sys::IUP_MINIMIZE => ShowState::Minimize, 159 | iup_sys::IUP_MAXIMIZE => ShowState::Maximize, 160 | _ => unreachable!(), 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/dialog/file.rs: -------------------------------------------------------------------------------- 1 | use iup_sys; 2 | use std::path::{Path, PathBuf}; 3 | 4 | use Element; 5 | 6 | pub struct FileDlg(*mut iup_sys::Ihandle); 7 | 8 | impl FileDlg { 9 | pub fn new() -> FileDlg { 10 | unsafe { FileDlg::from_raw(iup_sys::IupFileDlg()) } 11 | } 12 | 13 | pub fn new_open() -> FileDlg { 14 | FileDlg::new().set_attrib_data("DIALOGTYPE", cstr!("OPEN") as *const _) 15 | } 16 | 17 | pub fn new_save() -> FileDlg { 18 | FileDlg::new().set_attrib_data("DIALOGTYPE", cstr!("SAVE") as *const _) 19 | } 20 | 21 | pub fn new_dir() -> FileDlg { 22 | FileDlg::new().set_attrib_data("DIALOGTYPE", cstr!("DIR") as *const _) 23 | } 24 | 25 | pub fn files(&self) -> Option> { 26 | self.attrib("VALUE").and_then(|value| { 27 | let values = value.split_terminator('|').collect::>(); 28 | match values.len() { 29 | 0 => None, 30 | 1 => Some(vec![PathBuf::from(values[0])].to_owned()), 31 | _ => { 32 | // When multiple files are selected, the first value is the path the files are 33 | // contained in and then the filenames, let's build paths based on this. 34 | let path = Path::new(values[0]); 35 | Some(values.into_iter().skip(1) 36 | .map(|file| path.join(file)) 37 | .collect()) 38 | }, 39 | } 40 | }) 41 | } 42 | 43 | pub fn path(&self) -> Option { 44 | self.attrib("VALUE").map(|value| value.into()) 45 | } 46 | 47 | } 48 | 49 | impl_dialog!(FileDlg, "filedlg"); 50 | impl ::callback::HelpCb for FileDlg {} 51 | -------------------------------------------------------------------------------- /src/dialog/message.rs: -------------------------------------------------------------------------------- 1 | use iup_sys; 2 | use std::ffi::CString; 3 | 4 | use Element; 5 | 6 | /// Shows a modal dialog containing a message. It simply creates and popup a `MessageDlg`. 7 | pub fn message(title: S1, message: S2) 8 | where S1: Into, S2: Into { 9 | let ctitle = CString::new(title.into()).unwrap(); 10 | let cmessage = CString::new(message.into()).unwrap(); 11 | unsafe { iup_sys::IupMessage(ctitle.as_ptr(), cmessage.as_ptr()) }; 12 | } 13 | 14 | /// An predefined dialog for displaying a message. 15 | /// 16 | /// This dialog can be shown with the `popup` method only. 17 | /// 18 | /// See the [IUP MessageDlg Documentation][1]. 19 | /// [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/dlg/iupmessagedlg.html 20 | pub struct MessageDlg(*mut iup_sys::Ihandle); 21 | 22 | impl MessageDlg { 23 | pub fn new() -> MessageDlg { 24 | unsafe { MessageDlg::from_raw(iup_sys::IupMessageDlg()) } 25 | } 26 | } 27 | 28 | impl_dialog!(MessageDlg, "messagedlg"); 29 | impl ::callback::HelpCb for MessageDlg {} 30 | -------------------------------------------------------------------------------- /src/dialog/mod.rs: -------------------------------------------------------------------------------- 1 | //! See [IUP Dialogs][1]. 2 | //! [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/dialogs.html 3 | 4 | use iup_sys; 5 | use libc::c_int; 6 | use std::result::Result; 7 | 8 | use element::{Element, Widget, Container}; 9 | 10 | macro_rules! impl_dialog { 11 | ($ty_path:path, $classname:expr) => { 12 | impl_widget_container!($ty_path, $classname); 13 | impl $crate::dialog::DialogElement for $ty_path {} 14 | } 15 | } 16 | 17 | pub mod dialog; 18 | pub mod alarm; 19 | pub mod message; 20 | pub mod file; 21 | 22 | pub use self::dialog::{Dialog, ShowState, CopyDataCb, MdiActivateCb, ShowCb, TrayClickCb}; 23 | pub use self::alarm::{AlarmButton, alarm}; 24 | pub use self::message::{MessageDlg, message}; 25 | pub use self::file::{FileDlg}; 26 | 27 | // An dialog is a top-level container. 28 | pub trait DialogElement : Element + Widget + Container { 29 | /// Displays a dialog in a given position on the screen. 30 | /// 31 | /// Will call `Widget::map` for the element. 32 | /// 33 | /// This function can be executed more than once for the same dialog. This will make the 34 | /// dialog be placed above all other dialogs in the application, changing its Z-order, and 35 | /// update its position and/or size on screen. 36 | /// 37 | /// The string wrapped in `Err` may be meaningless, it is this way so that the returned value 38 | /// of this call can be passed directly to the closure return of `with_iup`. 39 | /// 40 | /// # Panics 41 | /// Panics if `x` is either `Bottom` or `Top` or if `y` is either `Left` or `Right`. 42 | fn showxy(&mut self, x: DialogPos, y: DialogPos) -> Result<(), String> { 43 | match unsafe { iup_sys::IupShowXY(self.raw(), x.to_raw_x(), y.to_raw_y()) } { 44 | iup_sys::IUP_NOERROR => Ok(()), 45 | iup_sys::IUP_ERROR => Err("showxy:IUP_ERROR".into()), 46 | _ => unreachable!(), 47 | } 48 | } 49 | 50 | /// Shows a dialog or menu and restricts user interaction only to the specified element. 51 | /// 52 | /// It is equivalent of creating a *modal* dialog is some toolkits. 53 | /// 54 | /// If another dialog is shown after `popup` using `show`, then its interaction will not be 55 | /// inhibited. Every `popup` call creates a new popup level that inhibits all previous dialogs 56 | /// interactions, but does not disable new ones. IMPORTANT: The popup levels must be closed in 57 | /// the reverse order they were created or unpredictable results will occur. 58 | /// 59 | /// For a dialog this function will only return the control to the application after a callback 60 | /// returns `CallbackReturn::Close`, IupExitLoop (TODO) is called, or when the popup dialog is 61 | /// hidden, for example using `Widget::hide`. For a menu it returns automatically after a menu 62 | /// item is selected. IMPORTANT: If a menu item callback returns `CallbackReturn::Close`, 63 | /// it will ends the current popup level dialog. 64 | /// 65 | /// # Panics 66 | /// Panics if `x` is either `Bottom` or `Top` or if `y` is either `Left` or `Right`. 67 | fn popup(&mut self, x: DialogPos, y: DialogPos) -> Result { 68 | match unsafe { iup_sys::IupPopup(self.raw(), x.to_raw_x(), y.to_raw_y()) } { 69 | iup_sys::IUP_NOERROR => Ok(*self), 70 | iup_sys::IUP_ERROR => Err(*self), 71 | // This should NEVER happen as DialogElement is supposed to be impl'ed only by dialogs. 72 | iup_sys::IUP_INVALID => panic!("`DialogElement::popup` called on a non-dialog!"), 73 | _ => unreachable!(), 74 | } 75 | } 76 | } 77 | 78 | /// The position a dialog should be positioned. 79 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 80 | pub enum DialogPos { 81 | /// Positions the element at the specified coordinate. 82 | At(i32), 83 | /// Use the current position of the dialog. Not valid for menus. 84 | /// This should be used most of the time. 85 | Current, 86 | /// Centers the element on the screen 87 | Center, 88 | /// Centralizes the dialog relative to its parent. 89 | CenterParent, 90 | /// Positions the element on the mouse cursor. 91 | MousePos, 92 | /// Positions the element on the left corner of the screen. Valid only for the x axis. 93 | Left, 94 | /// Positions the element on the right corner of the screen. Valid only for the x axis. 95 | Right, 96 | /// Positions the element on the top of the screen. Valid only for the y axis. 97 | Top, 98 | /// Positions the element on the bottom of the screen. Valid only for the y axis. 99 | Bottom, 100 | } 101 | 102 | impl DialogPos { 103 | fn to_raw(&self) -> c_int { 104 | use self::DialogPos::*; 105 | match *self { 106 | At(i) => i, 107 | Top => iup_sys::IUP_TOP, 108 | Bottom => iup_sys::IUP_BOTTOM, 109 | Left => iup_sys::IUP_LEFT, 110 | Right => iup_sys::IUP_RIGHT, 111 | Current => iup_sys::IUP_CURRENT, 112 | MousePos => iup_sys::IUP_MOUSEPOS, 113 | Center => iup_sys::IUP_CENTER, 114 | CenterParent => iup_sys::IUP_CENTERPARENT, 115 | } 116 | } 117 | 118 | fn to_raw_x(&self) -> c_int { 119 | use self::DialogPos::*; 120 | assert!(*self != Top && *self != Bottom); 121 | self.to_raw() 122 | } 123 | 124 | fn to_raw_y(&self) -> c_int { 125 | use self::DialogPos::*; 126 | assert!(*self != Right && *self != Left); 127 | self.to_raw() 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/element/guard.rs: -------------------------------------------------------------------------------- 1 | //! Guard for automatically destroying dettached elements on drop. 2 | use std::mem::forget; 3 | use Element; 4 | use std::ops::{Deref, DerefMut}; 5 | 6 | /// Guards an element by effectively destroying it on drop. 7 | /// 8 | /// This **does not** provide ownership semantics *(like cell types or smart pointers)*, just a 9 | /// simple dropper for the wrapped element. 10 | /// 11 | /// The programmer must make there's no copies of the element flying around the program. 12 | /// 13 | /// It's recommended to use this only for resource elements (those elements that cannot be attached 14 | /// to a dialog in any meaningful way). 15 | /// 16 | /// It's undefined behaviour to manually destroy a element that is wrapped within a guard, unless 17 | /// the elements gets `unwrap`ped before `Drop` gets called. Consequently it is also undefined 18 | /// behaviour to have two guards guarding the same element. 19 | /// 20 | /// Please refer to the crate level documentation of IUP-Rust (the main doc page) for details on 21 | /// ownership of elements. 22 | #[derive(Debug)] 23 | pub struct Guard(E); 24 | 25 | impl Guard { 26 | /// Creates a guard for the specified element. 27 | pub fn new(element: E) -> Guard { 28 | Guard(element) 29 | } 30 | 31 | /// Forgets this guard and unwraps out the contained element. 32 | pub fn unwrap(self) -> E { 33 | let inner = self.0; 34 | forget(self); // Don't drop me or I'll destroy `inner`! 35 | inner 36 | } 37 | } 38 | 39 | impl Drop for Guard { 40 | fn drop(&mut self) { 41 | self.0.destroy() 42 | } 43 | } 44 | 45 | /// Be careful on deferecing so you don't store another copy of the element somewhere. 46 | impl Deref for Guard { 47 | type Target = E; 48 | fn deref(&self) -> &E { 49 | &self.0 50 | } 51 | } 52 | 53 | /// Be careful on deferecing so you don't store another copy of the element somewhere. 54 | impl DerefMut for Guard { 55 | fn deref_mut(&mut self) -> &mut E { 56 | &mut self.0 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/element/hierarchy.rs: -------------------------------------------------------------------------------- 1 | //! Hierarchy operations on elements. 2 | use iup_sys; 3 | use libc::c_int; 4 | use std::ffi::CString; 5 | use std::result::Result; 6 | 7 | use element::{Element, Handle}; 8 | 9 | /// Containers are elements that can store childs. 10 | pub trait Container : Node { 11 | 12 | /// Inserts an interface element at the end of the container, after the last element on it. 13 | /// 14 | /// This function can be used when elements that will compose a container are not known a *priori* 15 | /// and should be *dynamically* constructed. 16 | /// 17 | /// Valid for any element that contains other elements like dialog, frame, hbox, vbox, 18 | /// zbox or menu. 19 | /// 20 | /// The `new_child` can **not** be mapped. It will **not** map the `new_child` into the native 21 | /// system. If the parent is already mapped you must explicitly call `Widget::map` for the new child. 22 | /// 23 | /// The elements are **not** immediately repositioned. Call `Node::refresh` for the container 24 | /// (or any other element in the dialog) to update the dialog layout. 25 | /// 26 | /// If the actual parent is a layout box (`VBox`, `HBox` or `ZBox`) and you try to append a 27 | /// child that it is already at the parent child list, then the child is moved to the last 28 | /// child position. 29 | /// 30 | /// Returns the actual parent if the interface element was successfully inserted. Otherwise 31 | /// returns the desired `new_child`. Failed can happen for instance if this element is not 32 | /// a container for other elements or the `new_child` is already a child (except on layout boxes). 33 | /// Notice that the desired parent can contains a set of elements and containers where the 34 | /// child will be actually attached so the function returns the actual parent of the element. 35 | fn append(&mut self, new_child: E) -> Result { 36 | match unsafe { iup_sys::IupAppend(self.raw(), new_child.raw()) } { 37 | ptr if ptr.is_null() => Err(new_child), 38 | ptr => Ok(Handle::from_raw(ptr)), 39 | } 40 | } 41 | 42 | /// Inserts an interface element before another child of the container. 43 | /// 44 | /// TODO ref_child NULL doc. See #23. 45 | /// 46 | /// See `Container::append` for more details on the semantics of this method. 47 | fn insert(&mut self, ref_child: &E1, new_child: E2) -> Result 48 | where E1: Node, E2: Node { 49 | match unsafe { iup_sys::IupInsert(self.raw(), ref_child.raw(), new_child.raw()) } { 50 | ptr if ptr.is_null() => Err(new_child), 51 | ptr => Ok(Handle::from_raw(ptr)), 52 | } 53 | } 54 | 55 | /// Returns the a child of the element given its position. 56 | /// 57 | /// The position `pos` starts from 0. 58 | /// 59 | /// This function will return the children of the element in the exact same order in 60 | /// which they were assigned. 61 | fn child(&self, pos: usize) -> Option { 62 | match unsafe { iup_sys::IupGetChild(self.raw(), pos as c_int) } { 63 | ptr if ptr.is_null() => None, 64 | ptr => Some(Handle::from_raw(ptr)), 65 | } 66 | } 67 | 68 | /// Returns the position of a child of the given control. 69 | /// 70 | /// See `Container::child` for additional details on the semantics of child positions. 71 | fn child_pos(&self, child: &E) -> Option { 72 | match unsafe { iup_sys::IupGetChildPos(self.raw(), child.raw()) } { 73 | -1 => None, 74 | id => Some(id as usize), 75 | } 76 | } 77 | 78 | /// Returns the number of children of the given element. 79 | fn child_count(&self) -> usize { 80 | unsafe { iup_sys::IupGetChildCount(self.raw()) as usize } 81 | } 82 | } 83 | 84 | /// Nodes are elements that can be part of a hierarchical structure. 85 | pub trait Node : Element { 86 | 87 | /// Detaches an interface element from its parent. 88 | /// 89 | /// It will automatically call `Widget::unmap` to unmap the element if necessary, 90 | /// and then detach the element. 91 | /// 92 | /// If left detached it is still **necessary to call `Element::destroy`** to destroy the 93 | /// detached element. 94 | /// 95 | /// The elements are **not** immediately repositioned. Call `Node::refresh` for the 96 | /// container (or any other element in the dialog) to update the dialog layout. 97 | fn detach(&mut self) -> Self { 98 | unsafe { iup_sys::IupDetach(self.raw()) }; 99 | self.clone() 100 | } 101 | 102 | /// Moves an interface element from one position in the hierarchy tree to another. 103 | /// 104 | /// TODO ref_child NULL doc. See #23. 105 | /// 106 | /// See `Container::append` for more details on the semantics of this method. 107 | fn reparent(&mut self, new_parent: E1, ref_child: E2) -> Result 108 | where E1: Container, E2: Node { 109 | match unsafe { iup_sys::IupReparent(self.raw(), new_parent.raw(), ref_child.raw()) } { 110 | iup_sys::IUP_NOERROR => Ok(*self), 111 | iup_sys::IUP_ERROR => Err(*self), 112 | _ => unreachable!(), 113 | } 114 | } 115 | 116 | /// Returns the parent of a element. 117 | fn parent(&self) -> Option { 118 | match unsafe { iup_sys::IupGetParent(self.raw()) } { 119 | ptr if ptr.is_null() => None, 120 | ptr => Some(Handle::from_raw(ptr)), 121 | } 122 | } 123 | 124 | /// Returns the brother of an element. 125 | fn brother(&self) -> Option { 126 | match unsafe { iup_sys::IupGetBrother(self.raw()) } { 127 | ptr if ptr.is_null() => None, 128 | ptr => Some(Handle::from_raw(ptr)), 129 | } 130 | } 131 | 132 | /// Returns the handle of the dialog that contains that interface element. 133 | /// 134 | /// Works also for children of a menu that is associated with a dialog. 135 | fn dialog(&self) -> Option { 136 | match unsafe { iup_sys::IupGetDialog(self.raw()) } { 137 | ptr if ptr.is_null() => None, 138 | ptr => Some(Handle::from_raw(ptr)), 139 | } 140 | } 141 | 142 | /// Returns the identifier of the child element that has the NAME attribute equals to the 143 | /// given value on the same dialog hierarchy. 144 | /// 145 | /// Works also for children of a menu that is associated with a dialog. 146 | /// 147 | /// This function will only found the child if the **NAME attribute** is set at the control. 148 | /// 149 | /// The function returns immediatelly with the result (not needing to traverse the hierarchy) 150 | /// after the child is mapped. 151 | fn dialog_child>(&self, name: S) -> Option { 152 | let cname = CString::new(name.into()).unwrap(); 153 | match unsafe { iup_sys::IupGetDialogChild(self.raw(), cname.as_ptr()) } { 154 | ptr if ptr.is_null() => None, 155 | ptr => Some(Handle::from_raw(ptr)), 156 | } 157 | } 158 | 159 | /// Updates the size and layout of all controls in the same dialog. 160 | /// 161 | /// Can be called even if the dialog is not mapped. 162 | /// Can be used for any control, but it will always affect the whole dialog, to refresh the 163 | /// layout of only a subset of the dialog use `Node::refresh_children`. 164 | /// 165 | /// This function will **not** change the size of the dialog, except if the SIZE or RASTERSIZE 166 | /// attributes of the dialog where changed before the call. Changing the size of elements 167 | /// without changing the dialog size may position some controls outside the dialog area at the 168 | /// left or bottom borders (the elements will be cropped at the dialog borders by the native system). 169 | /// 170 | /// `Widget::map` also updates the dialog layout, but only when called for the dialog itself, 171 | /// even if the dialog is already mapped. Since `Widget::show`, `DialogElement::showxy` and 172 | /// `DialogElement::popup` call `Widget::map`, then they all will always update the dialog 173 | /// layout before showing it, even also if the dialog is already visible. 174 | fn refresh(&mut self) { 175 | unsafe { iup_sys::IupRefresh(self.raw()) }; 176 | } 177 | 178 | /// Updates the size and layout of controls after changing size attributes, 179 | /// or attributes that affect the size of the control. 180 | /// 181 | /// Can be used for any element inside a dialog, only its children will be updated. 182 | /// 183 | /// The given element must be a container. It must be inside a dialog hierarchy and must be 184 | /// mapped. It can not be a dialog. For dialogs use `Node::refresh`. 185 | /// 186 | /// This function will **not** change the size of the given element, even if the natural size of 187 | /// its children would increase its natural size. 188 | fn refresh_children(&mut self) { // XXX container specific, maybe move to a container trait 189 | unsafe { iup_sys::IupRefreshChildren(self.raw()) }; 190 | } 191 | 192 | /// Mark the element to be redraw when the control returns to the system. 193 | fn update(&self) { 194 | unsafe { iup_sys::IupUpdate(self.raw()) }; 195 | } 196 | 197 | /// Mark the element children to be redraw when the control returns to the system. 198 | fn update_children(&self) { 199 | unsafe { iup_sys::IupUpdateChildren(self.raw()) }; 200 | } 201 | 202 | /// Force the element and its children to be redraw immediately. 203 | fn redraw(&self, also_redraw_children: bool) { 204 | unsafe { iup_sys::IupRedraw(self.raw(), also_redraw_children as c_int) }; 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/element/mod.rs: -------------------------------------------------------------------------------- 1 | //! Common operations between objects. 2 | //! 3 | //! Every IUP object is so called an element and can be encapsulated in a `Handle`. 4 | use iup_sys; 5 | use libc::{c_void, c_char, c_int}; 6 | use std::ptr; 7 | use std::ffi::{CStr, CString}; 8 | use std::result::Result; 9 | use std::iter::repeat; 10 | use std::str::FromStr; 11 | 12 | pub mod guard; 13 | pub use self::guard::Guard; 14 | 15 | pub mod hierarchy; 16 | pub use self::hierarchy::{Container, Node}; 17 | 18 | #[macro_use] 19 | pub mod widget; 20 | pub use self::widget::Widget; 21 | 22 | /// Makes a Vec of `Element` trait objects. 23 | /// 24 | /// This actually uses the `Handle` wrapper instead of `Element` due to the Sized requirement. 25 | /// 26 | /// This should be passed to functions that expect a list of elements in the constructor. 27 | #[macro_export] 28 | macro_rules! elements { 29 | () => { vec! [] }; 30 | ($($elem:expr),+,) => { elements! [ $($elem),+ ] }; 31 | ($($elem:expr),*) => { vec! [ $($crate::element::Handle::from($elem)),* ] }; 32 | } 33 | 34 | /// This macro should be used for every type binding IUP handles. 35 | /// 36 | /// See applicable `$classname`s [here][1]. Some classes aren't on the list and should be 37 | /// picked up manually by looking at the IUP source code or by looking at the result 38 | /// of `Element::classname`. 39 | /// [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/func/iupgetclassname.html 40 | macro_rules! impl_element { 41 | ($ty_path:path, $classname:expr) => { 42 | impl_element_nofrom!($ty_path, $classname); 43 | 44 | impl From<$ty_path> for $crate::element::Handle { 45 | fn from(elem: $ty_path) -> $crate::element::Handle { 46 | unsafe { $crate::element::Handle::from_raw_unchecked(elem.raw()) } 47 | } 48 | } 49 | }; 50 | } 51 | 52 | /// This is called from impl_element! to do all the work. 53 | /// 54 | /// This is a necessary thing because if we implemented `From<$ty_path> for Handle` here it'd cause 55 | /// a compilation error during `From for Handle`. 56 | macro_rules! impl_element_nofrom { 57 | ($ty_path:path, $classname:expr) => { 58 | 59 | impl $crate::Element for $ty_path { 60 | #[inline(always)] 61 | fn raw(&self) -> *mut iup_sys::Ihandle { 62 | self.0 63 | } 64 | #[inline(always)] 65 | unsafe fn from_raw_unchecked(ih: *mut iup_sys::Ihandle) -> Self { 66 | $ty_path(ih) 67 | } 68 | #[inline] 69 | unsafe fn target_classname() -> &'static str { 70 | $classname 71 | } 72 | } 73 | 74 | impl ::std::fmt::Debug for $ty_path { 75 | fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 76 | fmt.write_fmt(format_args!("{}({:p})", stringify!($ty_path), self.raw())) 77 | } 78 | } 79 | 80 | impl Copy for $ty_path {} 81 | 82 | impl Clone for $ty_path { 83 | fn clone(&self) -> $ty_path { 84 | *self 85 | } 86 | } 87 | 88 | impl $crate::callback::DestroyCb for $ty_path {} 89 | }; 90 | } 91 | 92 | /// An object that can wrap **any** IUP element. 93 | /// 94 | /// The handle also provides implementation for traits that mayn't be implemented for the 95 | /// contained element, so be careful when using it. 96 | pub struct Handle(*mut iup_sys::Ihandle); 97 | 98 | impl Handle { 99 | 100 | /// Constructs from a name associated with a element handle (with `Element::add_handle_name` or LED). 101 | pub fn from_named>(name: S) -> Option { 102 | let cname = CString::new(name.into()).unwrap(); 103 | match unsafe { iup_sys::IupGetHandle(cname.as_ptr()) } { 104 | ptr if ptr.is_null() => None, 105 | ptr => Some(Handle::from_raw(ptr)), 106 | } 107 | } 108 | 109 | /// Converts this handle object into a element object if they are compatible. 110 | pub fn try_downcast(self) -> Result { 111 | if self.can_downcast::() { 112 | // Since a Handle must be obtained also by using `from_raw` we can assume the handle 113 | // has reached the Rust binding thought it and thus using `from_raw_unchecked` here. 114 | Ok(unsafe { E::from_raw_unchecked(self.raw()) }) 115 | } else { 116 | Err(self) 117 | } 118 | } 119 | 120 | /// Checks if this Element type can be downcasted to the type E. 121 | fn can_downcast(&self) -> bool { 122 | let lhs = unsafe { self.classname().to_bytes() }; 123 | let rhs = unsafe { E::target_classname().as_bytes() }; 124 | lhs == rhs || rhs == b"__iuprusthandle" 125 | // In case self/lhs (a Handle) is trying to cast to a target object of Handle, let it go. 126 | } 127 | } 128 | 129 | impl_element_nofrom!(Handle, "__iuprusthandle"); 130 | 131 | /// Note: The wrapped element may not support `Container`. 132 | impl Container for Handle {} 133 | /// Note: The wrapped element may not support `Node`. 134 | impl Node for Handle {} 135 | /// Note: The wrapped element may not support `Widget`. 136 | impl Widget for Handle {} 137 | /// Note: The wrapped element may not support `MapCb`. 138 | impl ::callback::MapCb for Handle {} 139 | /// Note: The wrapped element may not support `UnmapCb`. 140 | impl ::callback::UnmapCb for Handle {} 141 | /// Note: The wrapped element may not support `GetFocusCb`. 142 | impl ::callback::GetFocusCb for Handle {} 143 | /// Note: The wrapped element may not support `KillFocusCb`. 144 | impl ::callback::KillFocusCb for Handle {} 145 | /// Note: The wrapped element may not support `EnterWindowCb`. 146 | impl ::callback::EnterWindowCb for Handle {} 147 | /// Note: The wrapped element may not support `LeaveWindowCb`. 148 | impl ::callback::LeaveWindowCb for Handle {} 149 | /// Note: The wrapped element may not support `HelpCb`. 150 | impl ::callback::HelpCb for Handle {} 151 | // TODO impl K_ callbacks when it's implemented. 152 | 153 | 154 | /// Every IUP object is an `Element`. 155 | pub trait Element : Sized + Copy + Clone { 156 | 157 | /// Constructs a specialized Element object from a general Handle if they are compatible. 158 | fn from_handle(handle: Handle) -> Result { 159 | handle.try_downcast::() 160 | } 161 | 162 | /// Constructs from a name associated with a element handle (with `Element::add_handle_name` or LED). 163 | fn from_name>(name: S) -> Option { 164 | let cname = CString::new(name.into()).unwrap(); 165 | match unsafe { iup_sys::IupGetHandle(cname.as_ptr()) } { 166 | ptr if ptr.is_null() => None, 167 | ptr => Some(Handle::from_raw(ptr)), 168 | } 169 | } 170 | 171 | /// Constructs an Element from a raw IUP handle. 172 | /// 173 | /// # Safety 174 | /// The `from_raw_unchecked` method is faster than `from_raw` but must be used with care. 175 | /// 176 | /// The Rust binding performs important operations and checks when a raw IUP handle reaches 177 | /// the bounds of safe Rust binding, that only happens when `from_raw` is used. Be sure the 178 | /// raw handle has reached safe Rust bounds at least once before using this method. 179 | /// 180 | /// It's undefined behaviour if the raw handle is incompatible with `Self` bindings. 181 | /// Instead use the `Element::from_handle` to perform safe downcasting. 182 | unsafe fn from_raw_unchecked(ih: *mut iup_sys::Ihandle) -> Self; 183 | 184 | /// Constructs an Element from a raw IUP handle. 185 | /// 186 | /// It's undefined behaviour if the raw handle is incompatible with `Self` bindings. 187 | /// Instead use the `Element::from_handle` to perform safe downcasting. 188 | /// 189 | /// # Panics 190 | /// Panics if the raw handle is a null pointer. 191 | fn from_raw(ih: *mut iup_sys::Ihandle) -> Self { 192 | if ih.is_null() { 193 | panic!("Failed to create IUP element from raw handle because the handle is null.") 194 | } else { 195 | unsafe { 196 | // Note: DESTROY_CB is used here instead of LDESTROY_CB because the DESTROY_CB 197 | // is called later. LDESTROY_CB is used in callbacks.rs, see it for more details. 198 | iup_sys::IupSetCallback(ih, cstr!("DESTROY_CB"), on_element_destroy); 199 | Element::from_raw_unchecked(ih) 200 | } 201 | } 202 | } 203 | 204 | /// Gets the raw IUP handle associated with this element. 205 | fn raw(&self) -> *mut iup_sys::Ihandle; 206 | 207 | /// Gets the [class name][1] of this element. 208 | /// [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/func/iupgetclassname.html 209 | unsafe fn classname(&self) -> &CStr { 210 | CStr::from_ptr(iup_sys::IupGetClassName(self.raw())) 211 | } 212 | 213 | /// Gets the [class name][1] the derived object should be targeting. 214 | /// [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/func/iupgetclassname.html 215 | unsafe fn target_classname() -> &'static str; 216 | 217 | /// Destroys an interface element and all its children. 218 | /// 219 | /// Only dialogs, timers, popup menus and images should be normally destroyed, but **detached** 220 | /// controls can also be destroyed. 221 | /// 222 | /// Menu bars associated with dialogs are automatically destroyed when the dialog is destroyed. 223 | /// 224 | /// Images associated with controls are **NOT** automatically destroyed. The application must 225 | /// destroy them when they are not used anymore. 226 | fn destroy(self) { 227 | unsafe { iup_sys::IupDestroy(self.raw()) }; 228 | } 229 | 230 | /// Checks if a specific attribute exists in the element. 231 | fn does_attrib_exist(&self, cname: &CString) -> bool { 232 | let attrib = unsafe { iup_sys::IupGetAttribute(self.raw(), cname.as_ptr()) }; 233 | !attrib.is_null() 234 | } 235 | 236 | /// Returns the names of all attributes of an element that are set in its internal 237 | /// hash table only. 238 | fn attribs(&self) -> Vec { 239 | let max = unsafe { iup_sys::IupGetAllAttributes(self.raw(), ptr::null_mut(), 0) }; 240 | let mut vec = repeat(ptr::null_mut()).take(max as usize).collect::>(); 241 | let len = unsafe { iup_sys::IupGetAllAttributes(self.raw(), vec.as_mut_ptr(), max) }; 242 | vec.into_iter().take(len as usize).map(|cstr| string_from_cstr!(cstr)).collect() 243 | } 244 | 245 | /// Sets an interface element attribute. 246 | /// 247 | /// See also the [IUP Attributes Guide][1]. 248 | /// [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/attrib_guide.html 249 | fn set_attrib(&mut self, name: S1, value: S2) -> Self 250 | where S1: Into, S2: Into { 251 | // The way IupSetAttribute works is infeasible to safety. Use IupSetStrAttribute. 252 | let cname = CString::new(name.into()).unwrap(); 253 | let cvalue = CString::new(value.into()).unwrap(); 254 | unsafe { iup_sys::IupSetStrAttribute(self.raw(), cname.as_ptr(), cvalue.as_ptr()) }; 255 | self.clone() 256 | } 257 | 258 | /// Gets an interface element attribute. 259 | /// 260 | /// See also the [IUP Attributes Guide][1]. 261 | /// [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/attrib_guide.html 262 | fn attrib>(&self, name: S) -> Option { 263 | // Notice IupGetAttribute does not really give strings but pointers (that may be anything) 264 | // most (if not all) the default IUP attributes are string values, so we are safe by 265 | // defaulting to IupGetAttribute. A method should be defined to deal with raw attributes. 266 | let cname = CString::new(name.into()).unwrap(); 267 | match unsafe { iup_sys::IupGetAttribute(self.raw(), cname.as_ptr()) } { 268 | cvalue if cvalue.is_null() => None, 269 | cvalue => Some(string_from_cstr!(cvalue)), 270 | } 271 | } 272 | 273 | /// Sets a raw interface element attribute. 274 | /// 275 | /// # Safety 276 | /// While this function is not unsafe, care must be taken while using it, prefer to use 277 | /// `Element::set_attrib` instead. The `data` pointer must live long enough (most of the time 278 | /// statically). 279 | fn set_attrib_data(&mut self, name: S1, data: *const c_void) -> Self 280 | where S1: Into { 281 | let cname = CString::new(name.into()).unwrap(); 282 | unsafe { iup_sys::IupSetAttribute(self.raw(), cname.as_ptr(), data as *const c_char) }; 283 | self.clone() 284 | } 285 | 286 | /// Gets a raw interface element attribute. 287 | fn attrib_data(&mut self, name: S1) -> *mut c_void 288 | where S1: Into { 289 | let cname = CString::new(name.into()).unwrap(); 290 | unsafe { iup_sys::IupGetAttribute(self.raw(), cname.as_ptr()) as *mut c_void } 291 | } 292 | 293 | /// Associates a element with an attribute. 294 | /// 295 | /// Instead of using `Element::add_handle_name` and `Element::set_attrib` with a new creative 296 | /// name, this function automatically creates a non conflict handle name and associates that 297 | /// with the attribute. 298 | fn set_attrib_handle(&mut self, name: S1, elem: E) -> Self 299 | where S1: Into, E: Element { 300 | let cname = CString::new(name.into()).unwrap(); 301 | unsafe { iup_sys::IupSetAttributeHandle(self.raw(), cname.as_ptr(), elem.raw()) }; 302 | self.clone() 303 | } 304 | 305 | /// Gets the handle associated with an attribute. 306 | fn attrib_handle(&mut self, name: S1) -> Option 307 | where S1: Into { 308 | let cname = CString::new(name.into()).unwrap(); 309 | match unsafe { iup_sys::IupGetAttributeHandle(self.raw(), cname.as_ptr()) } { 310 | ptr if ptr.is_null() => None, 311 | ptr => Some(Handle::from_raw(ptr)), 312 | } 313 | } 314 | 315 | /// Clears the value associated with an attribute and use the default value. 316 | fn clear_attrib>(&mut self, name: S) -> Self { 317 | let cname = CString::new(name.into()).unwrap(); 318 | unsafe { iup_sys::IupSetAttribute(self.raw(), cname.as_ptr(), ptr::null()) }; 319 | self.clone() 320 | } 321 | 322 | /// Removes an attribute from element and its children if the attrib is inheritable. 323 | /// 324 | /// It is useful to reset the state of inheritable attributes in a tree of elements. 325 | fn reset_attrib>(&mut self, name: S) -> Self { 326 | let cname = CString::new(name.into()).unwrap(); 327 | unsafe { iup_sys::IupResetAttribute(self.raw(), cname.as_ptr()) }; 328 | self.clone() 329 | } 330 | 331 | /// Returns the identifier of an interface element that has an associated handle name using 332 | /// `Element::add_handle_name` or using LED. 333 | /// 334 | /// Handle names shouldn't be confused with the NAME attribute. 335 | fn handle_name(&self) -> Option { 336 | match unsafe { iup_sys::IupGetName(self.raw()) } { 337 | name if name.is_null() => None, 338 | name => Some(string_from_cstr!(name)), 339 | } 340 | } 341 | 342 | /// Associates a handle name with an interface element. 343 | /// 344 | /// Can be called several times with the same element and different names. 345 | /// There is no restriction for the number of names a pointer can have, but `Element::handle_name` 346 | /// will return the first name found. 347 | /// 348 | /// Returns the handle of the interface element previously associated to the parameter name. 349 | /// 350 | /// Handle names shouldn't be confused with the NAME attribute. 351 | fn add_handle_name>(&self, name: S) -> Option { 352 | let cname = CString::new(name.into()).unwrap(); 353 | match unsafe { iup_sys::IupSetHandle(cname.as_ptr(), self.raw()) } { 354 | ptr if ptr.is_null() => None, 355 | ptr => Some(Handle::from_raw(ptr)), 356 | } 357 | } 358 | 359 | /// Clears the handle name association on the specified name. 360 | /// 361 | /// Note this will not destroy associated elements, just remove a name from the 362 | /// association table. 363 | /// 364 | /// Returns the handle of the interface element previously associated to the parameter name. 365 | /// 366 | /// Handle names shouldn't be confused with the NAME attribute. 367 | fn clear_handle_name>(name: S) -> Option { 368 | let cname = CString::new(name.into()).unwrap(); 369 | match unsafe { iup_sys::IupSetHandle(cname.as_ptr(), ptr::null_mut()) } { 370 | ptr if ptr.is_null() => None, 371 | ptr => Some(Handle::from_raw(ptr)), 372 | } 373 | } 374 | 375 | 376 | 377 | #[doc(hidden)] 378 | fn set_attrib_rgb(&mut self, name: S1, rgb: (u8, u8, u8)) -> Self 379 | where S1: Into { 380 | let cname = CString::new(name.into()).unwrap(); 381 | unsafe { iup_sys::IupSetRGB(self.raw(), cname.as_ptr(), rgb.0, rgb.1, rgb.2) }; 382 | self.clone() 383 | } 384 | 385 | #[doc(hidden)] 386 | fn attrib_rgb(&self, name: S1) -> Option<(u8, u8, u8)> 387 | where S1: Into { 388 | unsafe { 389 | let cname = CString::new(name.into()).unwrap(); 390 | if self.does_attrib_exist(&cname) { 391 | let mut rgb = (0u8, 0u8, 0u8); 392 | iup_sys::IupGetRGB(self.raw(), cname.as_ptr(), &mut rgb.0, &mut rgb.1, &mut rgb.2); 393 | Some(rgb) 394 | } else { 395 | None 396 | } 397 | } 398 | } 399 | 400 | #[doc(hidden)] 401 | fn attrib_bool(&self, name: S1) -> Option 402 | where S1: Into { 403 | unsafe { 404 | let cname = CString::new(name.into()).unwrap(); 405 | if self.does_attrib_exist(&cname) { 406 | Some(iup_sys::IupGetInt(self.raw(), cname.as_ptr()) != 0) 407 | } else { 408 | None 409 | } 410 | } 411 | } 412 | 413 | #[doc(hidden)] 414 | fn attrib_parse(&self, name: S1) -> Option 415 | where T: FromStr, S1: Into { 416 | self.attrib(name).and_then(|s| s.parse::().ok()) 417 | } 418 | 419 | } 420 | 421 | pub trait ConvertXYToPos : Element { 422 | /// Converts a x,y coordinate in an item position in the container. 423 | /// 424 | /// The x,y coordinates are relative to the left corner and top corner of the element. 425 | /// 426 | /// This have a different effect for each control it is applied to, check their documentation. 427 | fn convert_xy_to_pos(&self, x: i32, y: i32) -> Option { 428 | match unsafe { iup_sys::IupConvertXYToPos(self.raw(), x as c_int, y as c_int) } { 429 | -1 => None, 430 | id => Some(id), 431 | } 432 | } 433 | } 434 | 435 | 436 | /// Sets an attribute in the global environment. 437 | /// 438 | /// If the driver process the attribute then it will not be stored internally. 439 | pub fn set_global(name: S1, value: S2) 440 | where S1: Into, S2: Into { 441 | let cname = CString::new(name.into()).unwrap(); 442 | let cvalue = CString::new(value.into()).unwrap(); 443 | unsafe { iup_sys::IupSetStrGlobal(cname.as_ptr(), cvalue.as_ptr()) }; 444 | } 445 | 446 | /// Returns an attribute value from the global environment. 447 | /// 448 | /// The value can be returned from the driver or from the internal storage. 449 | /// 450 | /// This function’s return value is not necessarily the same one used by the application to 451 | /// set the attribute’s value. 452 | pub fn global>(name: S) -> Option { 453 | let cname = CString::new(name.into()).unwrap(); 454 | match unsafe { iup_sys::IupGetGlobal(cname.as_ptr()) } { 455 | cvalue if cvalue.is_null() => None, 456 | cvalue => Some(string_from_cstr!(cvalue)), 457 | } 458 | } 459 | 460 | /// Clears the value associated with an global attribute. 461 | pub fn clear_attrib>(name: S) { 462 | let cname = CString::new(name.into()).unwrap(); 463 | unsafe { iup_sys::IupSetGlobal(cname.as_ptr(), ptr::null()) }; 464 | } 465 | 466 | pub fn set_global_data(name: S1, data: *const c_void) 467 | where S1: Into { 468 | let cname = CString::new(name.into()).unwrap(); 469 | unsafe { iup_sys::IupSetGlobal(cname.as_ptr(), data as *const _) }; 470 | } 471 | 472 | pub fn global_data(name: S1) -> *mut c_void 473 | where S1: Into { 474 | let cname = CString::new(name.into()).unwrap(); 475 | unsafe { iup_sys::IupGetGlobal(cname.as_ptr()) as *mut c_void } 476 | } 477 | 478 | 479 | /// Called whenever a Element gets destroyed. 480 | /// 481 | /// Use this to perform frees related to the Rust binding that are per-element. 482 | extern fn on_element_destroy(ih: *mut iup_sys::Ihandle) -> c_int { 483 | unsafe { ::callback::drop_callbacks(ih); } 484 | iup_sys::IUP_DEFAULT 485 | } 486 | -------------------------------------------------------------------------------- /src/element/widget.rs: -------------------------------------------------------------------------------- 1 | //! Graphical user interface commons. 2 | use iup_sys; 3 | use std::result::Result; 4 | 5 | use element::{Element, Node}; 6 | 7 | macro_rules! impl_widget { 8 | ($ty_path:path, $classname:expr) => { 9 | impl_element!($ty_path, $classname); 10 | impl $crate::element::Widget for $ty_path {} 11 | impl $crate::element::Node for $ty_path {} 12 | } 13 | } 14 | 15 | macro_rules! impl_widget_container { 16 | ($ty_path:path, $classname:expr) => { 17 | impl_widget!($ty_path, $classname); 18 | impl $crate::element::Container for $ty_path {} 19 | } 20 | } 21 | 22 | 23 | // An widget is any element that maps to the graphical user interface. 24 | pub trait Widget : Element + Node { 25 | /// Creates (maps) the native interface objects corresponding to the given IUP interface elements. 26 | /// 27 | /// It will also called recursively to create the native element of all the children in the 28 | /// element's tree. 29 | /// 30 | /// The element must be already attached to a mapped container, except the dialog. 31 | /// A child can only be mapped if its parent is already mapped. 32 | /// 33 | /// This function is automatically called before the dialog is shown in 34 | /// `Widget::show`, `DialogElement::showxy` and `DialogElement::popup`. 35 | /// 36 | /// The function returns success if the element is already mapped and if the native creation 37 | /// was successful. 38 | fn map(&mut self) -> Result { 39 | match unsafe { iup_sys::IupMap(self.raw()) } { 40 | iup_sys::IUP_NOERROR => Ok(*self), 41 | iup_sys::IUP_ERROR => Err(*self), 42 | _ => unreachable!(), 43 | } 44 | } 45 | 46 | /// Unmap the element from the native system. It will also unmap all its children. 47 | /// 48 | /// It will **not** detach the element from its parent, and it will **not** destroy the element. 49 | fn unmap(&mut self) { 50 | unsafe { iup_sys::IupUnmap(self.raw()) } 51 | } 52 | 53 | /// Shows an interfance element. 54 | /// 55 | /// Displays a dialog in the current position, or changes a control VISIBLE attribute. If the 56 | /// dialog needs to be mapped and the current position is not known then the dialog is centered. 57 | /// 58 | /// This function can be executed more than once for the same dialog. This will make the dialog 59 | /// be placed above all other dialogs in the application, changing its Z-order, and update 60 | /// its position and/or size on screen. 61 | /// 62 | /// The string wrapped in `Err` may be meaningless, it is this way so that the returned value 63 | /// of this call can be passed directly to the closure return of `with_iup`. 64 | /// 65 | /// See also `DialogElement::showxy` and `DialogElement::popup`. 66 | fn show(&mut self) -> Result<(), String> { 67 | match unsafe { iup_sys::IupShow(self.raw()) } { 68 | iup_sys::IUP_NOERROR => Ok(()), 69 | iup_sys::IUP_ERROR => Err("show:IUP_ERROR".into()), 70 | _ => unreachable!(), 71 | } 72 | } 73 | 74 | /// Hides an interface element. 75 | /// 76 | /// This function has the same effect as attributing value "NO" to the interface element’s 77 | /// VISIBLE attribute. 78 | fn hide(&mut self) -> Self { 79 | unsafe { iup_sys::IupHide(self.raw()) }; 80 | self.clone() 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/image.rs: -------------------------------------------------------------------------------- 1 | //! Image elements to be embedded in other controls. 2 | use iup_sys; 3 | use Element; 4 | 5 | #[derive(Debug, Copy, Clone)] 6 | pub struct InPixels<'a, T: 'static>(pub &'a [&'a [T]]); 7 | 8 | /// This macro is meant to represent expressively pixels of a image. 9 | /// 10 | /// It's output should go in the `ImageElement::with` constructor. 11 | /// The output can be also stored in a variable and passed to several image elements. 12 | /// 13 | /// # Example 14 | /// ```ignore 15 | /// ImageRgb::with(pixels![ 16 | /// [(255, 0, 0), (255, 0, 0), (255, 0, 0)], 17 | /// [(0, 255, 0), (0, 255, 0), (0, 255, 0)], 18 | /// [(0, 0, 255), (0, 0, 255), (0, 0, 255)], 19 | /// [(0, 0, 255), (0, 0, 255), (0, 0, 255)], 20 | /// [(128, 128, 128), (0, 0, 0), (0, 88, 99)], 21 | /// ]) 22 | /// ``` 23 | #[macro_export] 24 | macro_rules! pixels { 25 | ( $($row:expr),+, ) => { pixels![ $($row),+ ] }; 26 | ( $($row:expr),* ) => { $crate::image::InPixels(&[ $(&$row),* ]) }; 27 | } 28 | 29 | pub trait ImageElement : Element { 30 | /// The type of a single pixel of this image. 31 | type Pixel : 'static + Copy; 32 | 33 | /// Creates a new image element with the specified dimensions from the specified pixel array. 34 | /// 35 | /// # Panics 36 | /// Should panic if `width`x`height` does not match the length of `pixels`. 37 | fn new(width: u32, height: u32, pixels: U) -> Self where U: AsRef<[Self::Pixel]>; 38 | 39 | /// Creates a new image element from the output of the `pixels!` macro. 40 | /// 41 | /// # Panics 42 | /// Panics if not all the rows of `pixels` have the same length. 43 | fn with<'a>(pixels: InPixels<'a, Self::Pixel>) -> Self { 44 | let pixels = pixels.0; 45 | let height = pixels.len(); 46 | let width = pixels.first().map(|row| row.len()).unwrap_or(0); 47 | 48 | let mut v = Vec::with_capacity(height * width); 49 | 50 | for row in pixels.iter() { 51 | assert_eq!(width, row.len()); 52 | for &pixel in row.iter() { 53 | v.push(pixel); 54 | } 55 | } 56 | 57 | Self::new(width as u32, height as u32, v) 58 | } 59 | } 60 | 61 | 62 | /// A one-channel image which each pixel is a byte index to a pallet of RGB colors. 63 | /// 64 | /// The image is meant to be shown on a label, button, toggle, or as a cursor. 65 | /// 66 | /// See the [IUP Image Documentation][1]. 67 | /// [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/elem/iupimage.html 68 | pub struct Image(*mut iup_sys::Ihandle); 69 | 70 | impl Image { 71 | /// Sets the pallet of colors to be used by the pixels. 72 | /// 73 | /// This is a shortcut to the `0`, `1`, `2`, ..., `i` attributes. 74 | /// 75 | /// # Panics 76 | /// Panics if the length of the colors pallet is greater than 256. 77 | pub fn set_colors(&mut self, colors: U) -> Image where U: AsRef<[(u8, u8, u8)]> { 78 | assert!(colors.as_ref().len() < 256); 79 | for (i, &color) in colors.as_ref().iter().enumerate() { 80 | self.set_attrib_rgb(i.to_string(), color); 81 | } 82 | self.clone() 83 | } 84 | } 85 | 86 | impl ImageElement for Image { 87 | type Pixel = u8; 88 | fn new(width: u32, height: u32, pixels: U) -> Image where U: AsRef<[u8]> { 89 | unsafe { 90 | let pixels = pixels.as_ref(); 91 | assert_eq!((width * height) as usize, pixels.len()); 92 | Image::from_raw(iup_sys::IupImage(width as i32, height as i32, pixels.as_ptr())) 93 | } 94 | } 95 | } 96 | 97 | impl_element!(Image, "image"); 98 | 99 | 100 | /// A three-channel image to be shown on a label, button, toggle, or as a cursor. 101 | /// 102 | /// See the [IUP Image Documentation][1]. 103 | /// [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/elem/iupimage.html 104 | pub struct ImageRgb(*mut iup_sys::Ihandle); 105 | 106 | impl ImageElement for ImageRgb { 107 | type Pixel = (u8, u8, u8); 108 | fn new(width: u32, height: u32, pixels: U) -> ImageRgb where U: AsRef<[(u8, u8, u8)]> { 109 | unsafe { 110 | let pixels = pixels.as_ref(); 111 | assert_eq!((width * height) as usize, pixels.len()); 112 | ImageRgb::from_raw(iup_sys::IupImageRGB(width as i32, height as i32, 113 | pixels.as_ptr() as *const _)) 114 | } 115 | } 116 | } 117 | 118 | impl_element!(ImageRgb, "imagergb"); 119 | 120 | 121 | /// A four-channel image to be shown on a label, button, toggle, or as a cursor. 122 | /// 123 | /// See the [IUP Image Documentation][1]. 124 | /// [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/elem/iupimage.html 125 | pub struct ImageRgba(*mut iup_sys::Ihandle); 126 | 127 | impl ImageElement for ImageRgba { 128 | type Pixel = (u8, u8, u8, u8); 129 | fn new(width: u32, height: u32, pixels: U) -> ImageRgba where U: AsRef<[(u8, u8, u8, u8)]> { 130 | unsafe { 131 | let pixels = pixels.as_ref(); 132 | assert_eq!((width * height) as usize, pixels.len()); 133 | ImageRgba::from_raw(iup_sys::IupImageRGBA(width as i32, height as i32, 134 | pixels.as_ptr() as *const _)) 135 | } 136 | } 137 | } 138 | 139 | impl_element!(ImageRgba, "imagergba"); 140 | -------------------------------------------------------------------------------- /src/layout/fill.rs: -------------------------------------------------------------------------------- 1 | use iup_sys; 2 | 3 | use Element; 4 | 5 | /// A void element, which dynamically occupies empty spaces always trying to expand itself. 6 | /// 7 | /// Its parent should be an `HBox`, an `VBox` or a `GridBox`, or else this type of expansion 8 | /// will not work. If an EXPAND is set on at least one of the other children of the box, 9 | /// then the fill expansion is ignored. 10 | /// 11 | /// See the [IUP Fill Documentation][1]. 12 | /// [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/elem/iupfill.html 13 | pub struct Fill(*mut iup_sys::Ihandle); 14 | 15 | impl Fill { 16 | pub fn new() -> Fill { 17 | unsafe { Fill::from_raw(iup_sys::IupFill()) } 18 | } 19 | } 20 | 21 | impl_widget!(Fill, "fill"); 22 | -------------------------------------------------------------------------------- /src/layout/hbox.rs: -------------------------------------------------------------------------------- 1 | use iup_sys; 2 | 3 | use Handle; 4 | use Element; 5 | 6 | /// A void container for composing elements horizontally. 7 | /// It is a box that arranges the elements it contains from left to right. 8 | /// 9 | /// See the [IUP HBox Documentation][1]. 10 | /// [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/elem/iuphbox.html 11 | pub struct HBox(*mut iup_sys::Ihandle); 12 | 13 | impl HBox { 14 | /// Creates a horizontal container box with the specified childs. 15 | pub fn new(elems: A) -> HBox where A: AsRef<[Handle]> { 16 | let mut carray = slice_to_ih_array!(elems.as_ref()); 17 | unsafe { HBox::from_raw(iup_sys::IupHboxv(carray.as_mut_ptr())) } 18 | } 19 | } 20 | 21 | impl_widget_container!(HBox, "hbox"); 22 | -------------------------------------------------------------------------------- /src/layout/mod.rs: -------------------------------------------------------------------------------- 1 | //! See the [Layout Composion Guide][1] and the [Layout Guide][2]. 2 | //! [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/layout.html 3 | //! [2]: http://webserver2.tecgraf.puc-rio.br/iup/en/layout_guide.html 4 | 5 | pub mod vbox; 6 | pub mod hbox; 7 | pub mod radio; 8 | pub mod fill; 9 | 10 | pub use self::vbox::VBox; 11 | pub use self::hbox::HBox; 12 | pub use self::radio::Radio; 13 | pub use self::fill::Fill; 14 | -------------------------------------------------------------------------------- /src/layout/radio.rs: -------------------------------------------------------------------------------- 1 | use iup_sys; 2 | use std::ptr; 3 | 4 | use Element; 5 | 6 | /// A void container for grouping mutual exclusive toggles. 7 | /// Only one of its descendent toggles will be active at a time. 8 | /// The toggles can be at any composition. 9 | /// 10 | /// See the [IUP Radio Documentation][1]. 11 | /// [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/elem/iupradio.html 12 | pub struct Radio(*mut iup_sys::Ihandle); 13 | 14 | impl Radio { 15 | /// Creates a radio to wrap toggles. 16 | /// 17 | /// The child is usually a `VBox` or `HBox` containing the toggles. 18 | pub fn new(child: E) -> Radio { 19 | unsafe { Radio::from_raw(iup_sys::IupRadio(child.raw())) } 20 | } 21 | 22 | /// Creates a radio with no wrapped content. 23 | pub fn new_empty() -> Radio { 24 | unsafe { Radio::from_raw(iup_sys::IupRadio(ptr::null_mut())) } 25 | } 26 | } 27 | 28 | impl_widget_container!(Radio, "radio"); 29 | -------------------------------------------------------------------------------- /src/layout/vbox.rs: -------------------------------------------------------------------------------- 1 | use iup_sys; 2 | 3 | use Handle; 4 | use Element; 5 | 6 | /// A void container for composing elements vertically. 7 | /// It is a box that arranges the elements it contains from top to bottom. 8 | /// 9 | /// See the [IUP VBox Documentation][1]. 10 | /// [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/elem/iupvbox.html 11 | pub struct VBox(*mut iup_sys::Ihandle); 12 | 13 | impl VBox { 14 | /// Creates a vertical container box with the specified childs. 15 | pub fn new(elems: A) -> VBox where A: AsRef<[Handle]> { 16 | let mut carray = slice_to_ih_array!(elems.as_ref()); 17 | unsafe { VBox::from_raw(iup_sys::IupVboxv(carray.as_mut_ptr())) } 18 | } 19 | } 20 | 21 | impl_widget_container!(VBox, "vbox"); 22 | -------------------------------------------------------------------------------- /src/led.rs: -------------------------------------------------------------------------------- 1 | //! LED *(Dialog-specification language)* functionalities. 2 | //! 3 | //! LED is a dialog-specification language whose purpose is not to be a complete programming language, 4 | //! but rather to make dialog specification simpler than in C. Additionally it allows users to easily 5 | //! edit your application layout from external files without touching any source code. 6 | //! 7 | //! In LED, attributes and expressions follow this form: 8 | //! 9 | //! `elem = element[attribute1=value1,attribute2=value2,...](...expression...)` 10 | //! 11 | //! The names of the elements must not contain the “iup” prefix. 12 | //! Attribute values are always interpreted as strings, but they need to be in quotes (“…”) only 13 | //! when they include spaces. The “IUP_” prefix must not be added to the names of the attributes 14 | //! and predefined values. Expressions contain parameters for creating the element. 15 | //! 16 | //! In LED there is no distinction between upper and lower case, except for attribute names. 17 | //! 18 | //! Also there is no optional parameters, in arrays at least one parameter must exist. 19 | //! 20 | //! To simply view a LED file objects use the LED Viewer application called [IupView][1], in the 21 | //! applications included in the distribution. Pre-compiled binaries are available at the 22 | //! [Download][2]. 23 | //! 24 | //! You need to check out the [IUP documentation][0] for each control to see their 25 | //! respective function signatures in LED. 26 | //! 27 | //! **Note:** Using LED may allow you to create controls not yet implemented in iup-rust and 28 | //! that's *fine*. Use a `Handle` to have access to controls created from LED. 29 | //! 30 | //! [0]: http://webserver2.tecgraf.puc-rio.br/iup/ 31 | //! [1]: http://webserver2.tecgraf.puc-rio.br/iup/en/led.html 32 | //! [2]: http://webserver2.tecgraf.puc-rio.br/iup/en/download.html 33 | 34 | use iup_sys; 35 | use std::path::Path; 36 | use std::result::Result; 37 | use std::ffi::CString; 38 | 39 | 40 | /// Compiles a LED specification from a file. 41 | /// 42 | /// Each time the function loads a LED file, the elements contained in it are created. 43 | /// Therefore, the same LED file cannot be loaded several times, otherwise the elements will also 44 | /// be created several times. 45 | /// 46 | /// In case of failure returns the compilation error message. 47 | pub fn load>(path: P) -> Result<(), String> { 48 | let path = path.as_ref(); 49 | 50 | let str = path.to_str().ok_or_else(|| "Failed to convert Path to string".to_string())?; 51 | let cstr = CString::new(str).unwrap(); 52 | 53 | match unsafe { iup_sys::IupLoad(cstr.as_ptr()) } { 54 | err if err.is_null() => Ok(()), 55 | err => Err(string_from_cstr!(err)), 56 | } 57 | } 58 | 59 | /// Compiles a LED specification from a string. 60 | /// 61 | /// See the `load` function for additional semantic details. 62 | pub fn load_buffer>(buf: S) -> Result<(), String> { 63 | let cstr = CString::new(buf.into()).unwrap(); 64 | match unsafe { iup_sys::IupLoadBuffer(cstr.as_ptr()) } { 65 | err if err.is_null() => Ok(()), 66 | err => Err(string_from_cstr!(err)), 67 | } 68 | } 69 | 70 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # IUP 2 | //! 3 | //! [IUP][1] is a multi-platform toolkit for building graphical user interfaces. 4 | //! 5 | //! IUP's purpose is to allow a program to run in different systems without changes - the toolkit 6 | //! provides the application portability. Supported systems include: GTK+, Motif and Windows. 7 | //! 8 | //! IUP has some advantages over other interface toolkits available: 9 | //! 10 | //! + **Simplicity:** due to the small number of functions and to its attribute mechanism, 11 | //! the learning curve for a new user is often faster. 12 | //! + **Portability:** the same functions are implemented in each one of the platforms, thus 13 | //! assuring the interface system's portability. 14 | //! + **Customization:** the dialog specification language (LED) is a mechanisms in which it 15 | //! is possible to customize an application for a specific user with a simple-syntax text file. 16 | //! + **Flexibility:** its abstract layout mechanism provides flexibility to dialog creation. 17 | //! + **Extensibility:** the programmer can create new interface elements as needed. 18 | //! 19 | //! # The Rust Binding 20 | //! 21 | //! The Rust binding provides a way to do things in a more Rustic way but without moving out of 22 | //! IUP base nameclatures and philosophy in such a way that one can program on this binding by reading the 23 | //! original [IUP documentation][1]. 24 | //! 25 | //! Everything created by IUP is so called a [`element`](element/) which imeplements the `Element` 26 | //! trait. Each of those objects can also be encapsulated in a `Handle` element which contains 27 | //! the common functionalities and allow downcasting back to the original element type. 28 | //! 29 | //! The library is divided in a few submodules in a way that it matches the IUP documentation 30 | //! division of elements: 31 | //! 32 | //! + The [controls](control/) submodule contains the user interface controls. 33 | //! + The [layout](layout/) submodule contains the abstract layout composition controls. 34 | //! + The [dialogs](dialog/) submodule contains the dialog definitions, such as windows, 35 | //! message boxes, file selection, color selection between others. 36 | //! 37 | //! Each of those elements communicates with the programmer by the means of [callbacks](callback/) 38 | //! and attributes. Callbacks are closures that gets called when *something* happens with the 39 | //! control such as a button click and attributes are the way to set and get specific properties 40 | //! of the element such as it's design or value. 41 | //! 42 | //! Currently attributes are not individual methods specific to each element but that may be a 43 | //! thing in the future. 44 | //! 45 | //! The binding is built in a way one can build controls or even the entire window of the 46 | //! application in a single expression in a very expressive way, for example: 47 | //! 48 | //! ```ignore 49 | //! Dialog::new( 50 | //! Radio::new( 51 | //! VBox::new(elements![ 52 | //! Toggle::with_title("Option 1") 53 | //! .set_attrib("TIP", "I am a tip!") 54 | //! .set_attrib("VALUE", "ON") 55 | //! .set_action(|(_, state)| println!("Option 1 = {}", state)), 56 | //! Toggle::with_title("Option 2") 57 | //! .set_action(|(_, state)| println!("Option 2 = {}", state)) 58 | //! .set_valuechanged_cb(|_| println!("Option 2 changed!!!")), 59 | //! ]) 60 | //! ) 61 | //! ).set_attrib("TITLE", "Hello IUP!") 62 | //! .show(); 63 | //! ``` 64 | //! 65 | //! This is just a example of one of the many ways one could build the GUI creation code, 66 | //! this model opens a lot of possibilities on this matter. There's also the possibility to use 67 | //! the [LED](led/) file format and allow users to easily modify the user interface with no 68 | //! programming experience. 69 | //! 70 | //! ## Ownership 71 | //! 72 | //! IUP have a few ownership restrictions, the library owns most of the elements it creates. 73 | //! That unfortunately means the Rust ownership semantics are a bit useless on IUP and thus all 74 | //! elements are `Copy`able. 75 | //! 76 | //! The following elements are automatically destroyed when IUP closes: 77 | //! 78 | //! + All the dialogs and all of it's children widgets. 79 | //! + Any element associated with a handle name (as set by [LED](led/), `Element::add_handle_name`, 80 | //! or implicitly by `Element::set_attrib_handle`). 81 | //! 82 | //! The user is also able to destroy elements manually by calling `Element::destroy`, one should 83 | //! make sure such element does not have other *references* to it wandering in the code. 84 | //! 85 | //! From looking on the above auto-destroy rules, the following are the cases of elements that must 86 | //! be destroyed manually: 87 | //! 88 | //! + Dettached widgets with no handle name. 89 | //! + Images not associated with any widget and thus no handle name. 90 | //! + Resources with no handle name — usually timers, clipboards, popup menus, config and user elements. 91 | //! 92 | //! To help on the task of destroying those mentioned cases, the `Guard` type is available to 93 | //! provide some kind of RAII to them. This type wrapper automatically destroys the wrapped element 94 | //! when it gets out of scope. Please refer to its documentation for more details. 95 | //! 96 | //! ## UTF-8 97 | //! 98 | //! By default in C, IUP uses strings in the current locale, IUP-Rust enables the UTF-8 mode of 99 | //! IUP to conform with Rust string standards, thus both `UTF8MODE` and `UTF8MODE_FILE` attributes 100 | //! are enabled by default in IUP-Rust. 101 | //! 102 | //! [1]: http://www.tecgraf.puc-rio.br/iup/ 103 | //! 104 | 105 | extern crate libc; 106 | extern crate iup_sys; 107 | 108 | use std::result::Result; 109 | use std::ptr; 110 | 111 | #[macro_use] 112 | mod macros; 113 | 114 | #[macro_use] 115 | pub mod element; 116 | pub use element::{Element, Handle, Guard}; 117 | 118 | #[macro_use] 119 | pub mod callback; 120 | 121 | pub mod dialog; 122 | pub mod layout; 123 | pub mod control; 124 | 125 | pub mod led; 126 | pub mod image; 127 | pub mod timer; 128 | pub mod clipboard; 129 | 130 | pub mod prelude; 131 | 132 | pub enum Orientation { 133 | Vertical, 134 | Horizontal, 135 | } 136 | 137 | impl Orientation { 138 | #[doc(hidden)] 139 | pub fn as_cstr(self) -> *const libc::c_char { 140 | use self::Orientation::*; 141 | match self { 142 | Vertical => cstr!("VERTICAL"), 143 | Horizontal => cstr!("HORIZONTAL"), 144 | } 145 | } 146 | } 147 | 148 | #[derive(Debug, Clone, Eq, PartialEq)] 149 | pub enum InitError { 150 | /// An IUP initialization error has happened. 151 | /// 152 | /// This usually happens only in UNIX because X-Windows may be not initialized. 153 | Error, 154 | /// The error returned by the user initialization function. 155 | UserError(String), 156 | /// IUP is already initialized. 157 | AlreadyOpen, 158 | } 159 | 160 | /// Initializes IUP toolkit, calls `f` for user initialization and runs the application. 161 | /// 162 | /// All IUP-Rust functions, objects and methods must be used within the bounds of the `f` closure. 163 | /// Such closure must return a `Result` indicating whether the user initialization was successful. 164 | /// 165 | /// This function will return only after the GUI application is closed. 166 | /// 167 | /// Returns `Ok` if the IUP initialization and user initialization were successful. `Err` otherwise. 168 | /// 169 | /// # Notes 170 | /// 171 | /// ## Blocking 172 | /// This functin will not return until until a callback returns `CallbackReturn::Close`, 173 | /// IupExitLoop (TODO) is called, or there are no visible dialogs. 174 | /// 175 | /// If the `f` closure returns successfully without any visible dialogs and no active timers, 176 | /// the application will hang and will not be possible to close the main loop. The process will 177 | /// have to be interrupted by the system. 178 | /// 179 | /// When the last visible dialog is hidden the IupExitLoop (TODO) function is automatically called, 180 | /// causing this function to return. To avoid that set LOCKLOOP=YES before hiding the last dialog. 181 | /// 182 | /// ## Enviroment Variables 183 | /// 184 | /// The toolkit's initialization depends also on platform-dependent environment variables, see 185 | /// each driver documentation. 186 | /// 187 | /// + **QUIET**: When this variable is set to `NO`, IUP will generate a message in console 188 | /// indicating the driver’s version when initializing. Default: `YES`. 189 | /// + **VERSION**: When this variable is set to `YES`, IUP generates a message dialog indicating 190 | /// the driver's version when initializing. Default: `NO`. 191 | /// 192 | pub fn with_iup Result<(), String>>(f: F) -> Result<(), InitError> { 193 | 194 | match unsafe { iup_sys::IupOpen(ptr::null(), ptr::null()) } { 195 | iup_sys::IUP_NOERROR => {}, 196 | iup_sys::IUP_OPENED => return Err(InitError::AlreadyOpen), 197 | iup_sys::IUP_ERROR => return Err(InitError::Error), 198 | _ => unreachable!(), 199 | }; 200 | 201 | // Turn UTF-8 mode ON since Rust uses UTF-8 on strings. 202 | match element::global("DRIVER").unwrap().as_ref() { 203 | "GTK" | "Win32" => unsafe { 204 | iup_sys::IupSetGlobal(cstr!("UTF8MODE"), cstr!("YES")); 205 | iup_sys::IupSetGlobal(cstr!("UTF8MODE_FILE"), cstr!("YES")); 206 | }, 207 | _ => println!("Warning: This IUP driver does not seem to support UTF-8!"), 208 | } 209 | 210 | let user_result = f(); 211 | if user_result.is_ok() { 212 | // IupMainLoop always returns IUP_NOERROR. 213 | unsafe { iup_sys::IupMainLoop(); } 214 | } 215 | 216 | // perform manual drop_callback! on the global callbacks. 217 | // also calls our iup-rust specific close callback. 218 | callback::remove_idle(); 219 | callback::remove_close_cb().map( |mut fbox| fbox.on_callback(()) ); 220 | unsafe { iup_sys::IupClose(); } 221 | 222 | user_result.map_err(|e| InitError::UserError(e)) 223 | } 224 | 225 | /// Returns a string with the IUP version number. 226 | pub fn version() -> String { 227 | string_from_cstr!(unsafe { iup_sys::IupVersion() }) 228 | } 229 | 230 | /// Returns a number indicating the IUP version. 231 | pub fn version_number() -> i32 { 232 | unsafe { iup_sys::IupVersionNumber() as i32 } 233 | } 234 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | 2 | /// Converts a Rust string literal into a C null terminated string literal typed `c_char`. 3 | macro_rules! cstr { 4 | ($str_lit:expr) => {{ // must be a literal!!! 5 | use libc::c_char; 6 | concat!($str_lit, '\0').as_ptr() as *const c_char 7 | }}; 8 | } 9 | 10 | /// Converts a `*const c_char` pointer into a owned `String`. 11 | macro_rules! string_from_cstr { 12 | ($c_str:expr) => {{ 13 | use std::ffi::CStr; 14 | let cstr = $c_str; 15 | unsafe { String::from_utf8_lossy(CStr::from_ptr(cstr).to_bytes()).to_string() } 16 | }}; 17 | } 18 | 19 | macro_rules! slice_to_ih_array { 20 | ($v:expr) => {{ 21 | use std::ptr; 22 | let mut v = Vec::with_capacity($v.len() + 1); 23 | v.extend($v.iter().map(|elem| elem.raw())); 24 | v.push(ptr::null_mut()); 25 | v 26 | }}; 27 | } 28 | -------------------------------------------------------------------------------- /src/prelude.rs: -------------------------------------------------------------------------------- 1 | //! The IUP-Rust Prelude. 2 | //! 3 | //! This is a prelude module that reexports many of the most common traits, types and functions. 4 | //! 5 | //! The users can glob import this prelude module to get access to many of the most common traits, 6 | //! types and functions. 7 | //! 8 | //! ```ignore 9 | //! use iup::prelude::*; 10 | //! ``` 11 | //! 12 | //! The prelude is primarily concerned with exporting traits that are so pervasive that it would 13 | //! be obnoxious to import each them every time. 14 | 15 | // Common Types 16 | pub use ::Orientation; 17 | pub use dialog::{Dialog, DialogPos}; 18 | 19 | // Common Traits 20 | pub use element::{Element, Widget, Container, Node, ConvertXYToPos}; 21 | pub use image::ImageElement; 22 | pub use dialog::DialogElement; 23 | 24 | // Callbacks 25 | pub use callback::{CallbackReturn, Action, ActionCb, DestroyCb}; 26 | pub use callback::{MapCb, UnmapCb, GetFocusCb, KillFocusCb, EnterWindowCb, LeaveWindowCb, HelpCb}; 27 | pub use callback::{CaretCb, SpinCb, ValueChangedCb, DropFilesCb}; 28 | pub use callback::{CloseCb, MoveCb, ResizeCb}; 29 | pub use callback::button::{ButtonCb, MotionCb}; 30 | pub use control::{TextAction, ToggleAction, ListAction}; 31 | pub use dialog::{CopyDataCb, MdiActivateCb, ShowCb, TrayClickCb}; 32 | -------------------------------------------------------------------------------- /src/timer.rs: -------------------------------------------------------------------------------- 1 | //! Timer to periodically execute an action. 2 | use iup_sys; 3 | 4 | use Element; 5 | use Guard; 6 | 7 | /// A timer which periodically invokes a callback when the time is up. 8 | /// 9 | /// # Ownership 10 | /// 11 | /// The timer must be manually destroyed, thus for the user safety it returns a guarded object 12 | /// on the `new` constructor. 13 | /// 14 | /// Please refer to the crate level documentation of IUP-Rust (the main doc page) for details on 15 | /// ownership of elements. 16 | pub struct Timer(*mut iup_sys::Ihandle); 17 | 18 | impl Timer { 19 | /// Constructs a timer. 20 | pub fn new() -> Guard { 21 | Guard::new( 22 | Timer::from_raw(unsafe { iup_sys::IupTimer() }) 23 | ) 24 | } 25 | 26 | /// Gets the set time interval in milliseconds or `None` if not set. 27 | pub fn time(&self) -> Option { 28 | self.attrib_parse("TIME") 29 | } 30 | 31 | /// Sets the time interval in milliseconds. 32 | /// 33 | /// In Windows the minimum value is 10ms. 34 | pub fn set_time(&mut self, time: u32) -> Self { 35 | self.set_attrib("TIME", time.to_string()) 36 | } 37 | 38 | /// Starts the timer. 39 | /// 40 | /// Does nothing if the TIME attribute is not set i.e. `set_time`. 41 | /// 42 | /// If you have multiple threads start the timer in the main thread. 43 | pub fn run(&mut self) -> Self { 44 | self.set_attrib("RUN", "YES") 45 | } 46 | 47 | /// Stops the timer. 48 | pub fn stop(&mut self) -> Self { 49 | self.set_attrib("RUN", "NO") 50 | } 51 | 52 | /// Returns the current timer state. 53 | pub fn is_running(&self) -> bool { 54 | self.attrib_bool("RUN").unwrap() 55 | } 56 | } 57 | 58 | impl_element!(Timer, "timer"); 59 | 60 | /// Called every time the defined time interval is reached. 61 | /// 62 | /// To stop the callback from being called simply stop de timer with RUN=NO or `Timer::stop`. 63 | /// 64 | /// `CallbackReturn::Close` will be processed. 65 | impl ::callback::ActionCb for Timer {} 66 | --------------------------------------------------------------------------------