Skip to content

Commit 4cbd767

Browse files
committed
Initial test implementation of iui
This is an improved libUI safe interface.
1 parent 1f04869 commit 4cbd767

File tree

18 files changed

+656
-33
lines changed

18 files changed

+656
-33
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ Cargo.lock
22
*.swp
33
ui/target
44
ui-sys/target
5+
iui/target
56

.travis.yml

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,29 @@ rust:
44
- beta
55
- nightly
66
matrix:
7-
allow_failures:
8-
- rust: nightly
9-
10-
before_install:
7+
allow_failures:
8+
- rust: nightly
9+
before_install:
1110
- sudo apt-get -qq update
1211
- sudo apt-get install -y cmake
1312
- sudo apt-get install -y libgtk-3-dev
14-
1513
script:
1614
- cd ui-sys
1715
- cargo build --verbose --tests --examples
1816
- cd ../ui
1917
- cargo build --verbose --tests --examples
18+
- cd ../iui
19+
- cargo build --verbose --tests --examples
20+
after_success: |
21+
[ $TRAVIS_BRANCH = master ] &&
22+
[ $TRAVIS_PULL_REQUEST = false ] &&
23+
cd $TRAVIS_BUILD_DIR/iui/ &&
24+
cargo doc &&
25+
echo "<meta http-equiv=refresh content=0;url=iui/index.html>" > 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+
30+
env:
31+
global:
32+
secure: dfPG6E3lrxnbah17u8S4xJmqFMhrAKoXNxQKGSnEoP3HjZL1NrFFsGCtGbIGPGyz02sCQ3U35nmuGHjUP9SOn+oRV6oQlBx41PPHULoDfsMKojEG80Uphjhd6rCXhdTG10EeIjqK+thNvbhnKXJoSPxe7rIk+r+z4HWHjQhxQuT6i4A6AsjvLGV1UI/WxCzrRFJohnaSyd/jx4/BgBd6XXsHzBvrjYSKSDrrBfJJwqrVchLozD6wSKTaTYdK9CSVGYx8S58Kx2m1j1Hk7FuuC6tqPtOARpwlNOX83p4PUMgZln74QzPN65Y2oYx5dszjflyAf5p264ltDwMLlygDTs92kilRCrznnP1iHgtrO3+aV2jwtRtqXB16JpeYKGFMkRtd3afWZ1SaMjU7s+6tVbSvlXg88GstZPqYYw7B4qYP1BGPX6UGiYMtpqH3Tm1Ne3lX3sPQYDFaV7vkd4JoS05vuHuGU+FERJRmaMAHjz2PCEhcgwnQTzL5LFKTcfNHEFv7NkQ8zPiJ7GELg9n2KnWiRhdB4JGUu8WB1LyYP6j3CQ9NwfBj0VGiH8Urgl9fC3IJnXQW3f284vk0VxBQnSdKILHsvdo8pe6INbDu97XOhzZNIc4jLZRT08aQe5Lk+3e7nGf0CCxFNQSlP6FFN7UP9c/Qvd4M3Ys5atBAryQ=

iui/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "iui"
3+
version = "0.1.0"
4+
authors = ["Leo Tindall <[email protected]>"]
5+
6+
[dependencies]
7+
bitflags = "0.7"
8+
libc = "*"
9+
failure = "0.1.1"
10+
11+
[dependencies.ui-sys]
12+
path = "../ui-sys"

iui/examples/basic.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
extern crate iui;
2+
use iui::prelude::*;
3+
4+
fn main() {
5+
// Initialize the UI library
6+
let ui = UI::init().expect("Couldn't initialize UI library");
7+
// Create a window into which controls can be placed
8+
let win = Window::new(&ui, "Test App", 200, 200, WindowType::NoMenubar);
9+
// Create a button to place in the window
10+
let btn = iui::controls::Button::new(&ui, "Button");
11+
12+
// Actually put the button in the window
13+
win.set_child(&ui, btn);
14+
// Show the window
15+
win.show(&ui);
16+
// Run the application
17+
ui.main();
18+
}

iui/src/controls/basic.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use std::mem;
2+
use std::ffi::{CString, CStr};
3+
use libc::c_void;
4+
use ui_sys::{self, uiButton, uiLabel, uiControl};
5+
use super::Control;
6+
use ui::UI;
7+
8+
define_control!{
9+
/// A textual button which users can click on, causing a callback to run.
10+
rust_type: Button,
11+
sys_type: uiButton
12+
}
13+
14+
impl Button {
15+
/// Create a new button with the given text as its label.
16+
pub fn new(_ctx: &UI, text: &str) -> Button {
17+
unsafe {
18+
let c_string = CString::new(text.as_bytes().to_vec()).unwrap();
19+
Button::from_raw(ui_sys::uiNewButton(c_string.as_ptr()))
20+
}
21+
}
22+
23+
/// Get a copy of the existing text on the button.
24+
pub fn text(&self, _ctx: &UI) -> String {
25+
unsafe { CStr::from_ptr(ui_sys::uiButtonText(self.uiButton)).to_string_lossy().into_owned() }
26+
}
27+
28+
/// Get a reference to the existing text on the button.
29+
pub fn text_ref(&self, _ctx: &UI) -> &CStr {
30+
unsafe { CStr::from_ptr(ui_sys::uiButtonText(self.uiButton)) }
31+
}
32+
33+
/// Set the text on the button.
34+
pub fn set_text(&self, _ctx: &UI, text: &str) {
35+
unsafe {
36+
let c_string = CString::new(text.as_bytes().to_vec()).unwrap();
37+
ui_sys::uiButtonSetText(self.uiButton, c_string.as_ptr())
38+
}
39+
}
40+
41+
/// Run the given callback when the button is clicked.
42+
pub fn on_clicked<F: FnMut(&Button)>(&self, _ctx: &UI, callback: F) {
43+
unsafe {
44+
let mut data: Box<Box<FnMut(&Button)>> = Box::new(Box::new(callback));
45+
ui_sys::uiButtonOnClicked(
46+
self.uiButton,
47+
c_callback,
48+
&mut *data as *mut Box<FnMut(&Button)> as *mut c_void,
49+
);
50+
mem::forget(data);
51+
}
52+
53+
extern "C" fn c_callback(button: *mut uiButton, data: *mut c_void) {
54+
unsafe {
55+
let button = Button { uiButton: button };
56+
mem::transmute::<*mut c_void, &mut Box<FnMut(&Button)>>(data)(&button)
57+
}
58+
}
59+
}
60+
}
61+
62+
define_control!{
63+
/// A non-interactable piece of text.
64+
rust_type: Label,
65+
sys_type: uiLabel
66+
}
67+

iui/src/controls/create_macro.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Defines a new control, creating a Rust wrapper, a `Deref` implementation, and a destructor.
2+
// An example of use:
3+
//
4+
// define_control!{
5+
// /// Some documentation
6+
// #[attribute(whatever="something")]
7+
// rust_type: Slider,
8+
// sys_type: uiSlider,
9+
// }
10+
macro_rules! define_control {
11+
// Match first any attributes (incl. doc comments) and then the actual invocation
12+
{$(#[$attr:meta])* rust_type: $rust_type:ident, sys_type: $sys_type:ident } => {
13+
#[allow(non_snake_case)]
14+
// Include all attributes
15+
$(#[$attr])*
16+
pub struct $rust_type {
17+
$sys_type: *mut $sys_type,
18+
}
19+
20+
impl Drop for $rust_type {
21+
fn drop(&mut self) {
22+
// For now this does nothing, but in the future, when `libui` supports proper
23+
// memory management, this will likely need to twiddle reference counts.
24+
}
25+
}
26+
27+
impl Clone for $rust_type {
28+
fn clone(&self) -> $rust_type {
29+
$rust_type {
30+
$sys_type: self.$sys_type,
31+
}
32+
}
33+
}
34+
35+
impl Into<Control> for $rust_type {
36+
fn into(self) -> Control {
37+
unsafe {
38+
let control = Control::from_ui_control(self.$sys_type as *mut uiControl);
39+
mem::forget(self);
40+
control
41+
}
42+
}
43+
}
44+
45+
impl $rust_type {
46+
pub fn show(&self, _ctx: &UI) {
47+
let control: Control = self.clone().into();
48+
unsafe { ui_sys::uiControlShow(control.ui_control) }
49+
}
50+
/// Create an `iui` struct for this control from the raw pointer for it.
51+
///
52+
/// # Unsafety
53+
/// The given pointer must point to a valid control or memory unsafety may result.
54+
#[allow(non_snake_case)]
55+
#[allow(unused)]
56+
pub unsafe fn from_raw($sys_type: *mut $sys_type) -> $rust_type {
57+
$rust_type {
58+
$sys_type: $sys_type
59+
}
60+
}
61+
}
62+
}
63+
}

iui/src/controls/mod.rs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
//! Available controls and related functionality. Note that these structs are created by methods on the `UI` struct.
2+
3+
use ui_sys::{self, uiControl};
4+
use ui::UI;
5+
6+
use std::ptr;
7+
8+
#[macro_use] mod create_macro;
9+
mod basic;
10+
pub use self::basic::*;
11+
mod window;
12+
pub use self::window::*;
13+
14+
/// A generic UI control. Any UI control can be turned into this type.
15+
/// Note that `Control` and all specific control types are references
16+
/// whose memory is owned by the UI library.
17+
pub struct Control {
18+
ui_control: *mut uiControl,
19+
}
20+
21+
impl Drop for Control {
22+
#[inline]
23+
fn drop(&mut self) {
24+
// For now this does nothing, but in the future, when `libui` supports proper memory
25+
// management, this will likely need to twiddle reference counts.
26+
}
27+
}
28+
29+
impl Clone for Control {
30+
#[inline]
31+
fn clone(&self) -> Control {
32+
Control {
33+
ui_control: self.ui_control,
34+
}
35+
}
36+
}
37+
38+
impl Control {
39+
/// Creates a new `Control` object from an existing `*mut uiControl`.
40+
pub unsafe fn from_ui_control(ui_control: *mut uiControl) -> Control {
41+
Control {
42+
ui_control: ui_control,
43+
}
44+
}
45+
46+
/// Returns the underlying `*mut uiControl`.
47+
pub fn as_ui_control(&self) -> *mut uiControl {
48+
self.ui_control
49+
}
50+
51+
/// Destroys a control. Any use of the control after this is use-after-free; therefore, this
52+
/// is marked unsafe.
53+
pub unsafe fn destroy(&self) {
54+
// Don't check for initialization here since this can be run during deinitialization.
55+
ui_sys::uiControlDestroy(self.ui_control)
56+
}
57+
58+
}
59+
60+
impl UI {
61+
// Return the parent control of the given control, or None if the control is orphaned.
62+
pub fn parent_of<T: Into<Control>>(&self, control: T) -> Option<Control> {
63+
unsafe {
64+
let ptr = ui_sys::uiControlParent(control.into().ui_control);
65+
if ptr.is_null() {
66+
None
67+
} else {
68+
Some(Control::from_ui_control(ptr))
69+
}
70+
}
71+
}
72+
73+
/// Set the parent control of this control, "moving" it to a new place in
74+
/// the UI tree or, if passed `None`, removing it from the tree.
75+
// TODO: Does this actually need to be unsafe? I don't really see why it is.
76+
pub unsafe fn set_parent_of<T: Into<Control>>(&self, control: T, parent: Option<T>) {
77+
ui_sys::uiControlSetParent(
78+
control.into().ui_control,
79+
match parent {
80+
None => ptr::null_mut(),
81+
Some(parent) => parent.into().ui_control,
82+
},
83+
)
84+
}
85+
86+
/// Returns true if this control is a top-level control; the root of
87+
/// the UI tree.
88+
pub fn is_toplevel<T: Into<Control>>(&self, control: T) -> bool {
89+
unsafe { ui_sys::uiControlToplevel(control.into().ui_control) != 0 }
90+
}
91+
92+
/// Returns true if this control is currently set to be displayed.
93+
pub fn is_shown<T: Into<Control>>(&self, control: T) -> bool {
94+
unsafe { ui_sys::uiControlVisible(control.into().ui_control) != 0 }
95+
}
96+
97+
/// Sets whether or not the control should be displayed.
98+
pub fn set_shown<T: Into<Control>>(&self, control: T, show: bool) {
99+
if show {
100+
unsafe { ui_sys::uiControlShow(control.into().ui_control) }
101+
} else {
102+
unsafe { ui_sys::uiControlHide(control.into().ui_control) }
103+
}
104+
}
105+
106+
/// Returns true if the control is enabled (can be interacted with).
107+
pub fn is_enabled<T: Into<Control>>(&self, control: T) -> bool {
108+
unsafe { ui_sys::uiControlEnabled(control.into().ui_control) != 0 }
109+
}
110+
111+
/// Sets the enable/disable state of the control. If disabled, a control
112+
/// cannot be interacted with, and visual cues to that effect are presented
113+
/// to the user.
114+
pub fn set_enabled<T: Into<Control>>(&self, control: T, enabled: bool) {
115+
if enabled {
116+
unsafe { ui_sys::uiControlEnable(control.into().ui_control) }
117+
} else {
118+
unsafe { ui_sys::uiControlDisable(control.into().ui_control) }
119+
}
120+
}
121+
}

0 commit comments

Comments
 (0)