├── .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 [](https://travis-ci.org/dcampbell24/iup-rust)[](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