Repository: cybergeek94/kiss-ui
Branch: master
Commit: 074bf82df1b9
Files: 28
Total size: 71.7 KB
Directory structure:
gitextract_5c7epxqh/
├── .gitignore
├── .travis.yml
├── Cargo.toml
├── LICENSE
├── README.md
├── SCREENSHOTS.md
├── build_docs.sh
├── examples/
│ ├── button_test.rs
│ ├── progress_test.rs
│ ├── show_image.rs
│ ├── textbox_test.rs
│ └── window_test.rs
├── kiss-app.manifest
└── src/
├── attrs.rs
├── base.rs
├── button.rs
├── callback.rs
├── container.rs
├── dialog.rs
├── image.rs
├── lib.rs
├── progress.rs
├── text.rs
├── timer.rs
├── utils/
│ ├── cstr.rs
│ ├── mod.rs
│ └── move_cell.rs
└── widget.rs
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
target
Cargo.lock
*~
*.swp
================================================
FILE: .travis.yml
================================================
env:
global:
- IUP_DL='http://sourceforge.net/projects/iup/files/3.14/Linux%20Libraries/iup-3.14_Linux32_64_lib.tar.gz'
language: rust
install:
- sudo apt-get install libgtk-3-dev
# Download and install IUP
- mkdir iup_libs/
- wget $IUP_DL -O iup_libs.tar.gz
- tar -xzvf iup_libs.tar.gz -C iup_libs/
# By piping a newline to ./install, we skip the enter prompt
- (cd iup_libs/ && echo -ne '\n' | sudo ./install)
- rm -rf iup_libs/
script:
- cargo build -v
- cargo test -v
- cargo doc -v --no-deps
================================================
FILE: Cargo.toml
================================================
[package]
name = "kiss-ui"
version = "0.1.0"
authors = ["Austin Bonander <austin.bonander@gmail.com>"]
[dependencies]
libc = "*"
iup-sys = "*"
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2015 Austin Bonander
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
KISS-UI [](https://travis-ci.org/cybergeek94/kiss-ui)
=========
A UI framework for Rust based on the KISS (Keep It Simple, Stupid!) philosophy.
Powered by the [IUP][iup] GUI library for C by Tecgraf, via the bindings created for [iup-rust][iup-rust].
(No relation to the equally awesome [kiss3d][kiss3d].)
[kiss3d]: https://github.com/sebcrozet/kiss3d
[iup]: http://webserver2.tecgraf.puc-rio.br/iup/
[iup-rust]: https://github.com/dcampbell24/iup-rust
Contents
--------
* [Documentation](#documentation)
* [Usage](#usage)
* [Installing IUP Binaries](#installing-iup-binaries)
* [Windows](#windows)
* [Linux](#linux)
* [OS X](#os-x)
* [Comparison to Other UI Frameworks](#comparison-to-other-ui-frameworks)
* [Enabling Visual Styles on Windows](#enabling-visual-styles-on-windows)
Documentation
-------------
[`kiss-ui` docs hosted on Github Pages](http://kiss-ui.github.io/kiss-ui/doc/kiss_ui/)
Usage
-----
Simply add the following to your `Cargo.toml`:
```
[dependencies.kiss-ui]
git = "https://github.com/cybergeek94/kiss-ui"
```
Import KISS-UI's macros and common types:
```rust
#[macro_use]
extern crate kiss_ui;
use kiss_ui::prelude::*;
```
#### KISS-UI builds on all Rust release channels!
[iup-dl]: http://sourceforge.net/projects/iup/files/3.14/
Installing IUP Binaries
-------------------
You will need to install the IUP binaries for your system, which are available for download [here][iup-dl].
Consult the following for which files to download and where to install them. The specific steps depend on your platform and preferred method of linking: dynamic or static.
PRs amending or adding instructions for any platform are very welcome.
***
### Windows
#### Dynamic linking
1. Navigate to `Windows Libraries/Dynamic`
* 32-bit: Download `iup-3.14_Win32_dllw4_lib.zip`
* 64-bit: Download `iup-3.14_Win64_dllw4_lib.zip`
2. Extract all `.dll` files to a folder where the linker can find them (pick one):
* `<Rust install>/bin/rustlib/<platform target>/lib/` (recommended)
* (using MinGW/MSYS) `<MinGW/MSYS install>/usr/lib`
* `<Your cargo repository>/bin/<platform target>`
3. Copy the same DLLs to a folder in your PATH (pick one):
* `<Rust install>/bin/` (recommended)
* Create a folder anywhere and add it to your PATH.
* Add one of the folders from step 2 to your PATH.
You should **NEVER** place arbitrary files in your Windows install folder, no matter how benign.
#### Static Linking
Static linking with IUP on Windows is not currently possible as it requires resource scripts (`.rc`) files from IUP to be compiled and linked in, which Rust does not currently support.
***
### Linux
The Linux binary packages for IUP include both static and dynamic libraries. While efforts are underway to create up-to-date packages for various distributions' package managers, the currently most well supported methods of obtaining IUP binaries are to either compile them from source or download precompiled binaries from the creators.
#### Compile from Source
To compile from source, see [this page][iup-compile]. The instructions to check-out the source tree are available [here][iup-source]. If you understand how to build projects with Makefiles, then it shouldn't be too difficult.
#### Download the Precompiled Binaries
However, if you would rather download the precompiled binaries, begin by going to [the download page][iup-dl].
1. Navigate to the `Linux Libraries` folder.
2. Identify your kernel version. This can be done by entering the command `uname -r` into a terminal.
* If you don't know if your Linux is 32-bit or 64-bit, use the command `uname -a` and look for the following:
* `x86_64`: Your system is 64-bit.
* `x86`: Your system is 32-bit.
3. Select and download the tarball for your kernel version and bit-arity.
* For 32-bit (`x86`), there is only one package: `iup-3.14_Linux32_lib.tar.gz`
* For 64-bit (`x86_64`), select one of the following based on your kernel version:
* **>= 3.19**: `iup-3.14_Linux319_64_lib.tar.gz`
* **>= 3.13**: `iup-3.14_Linux313_64_lib.tar.gz`
* **>= 3.5**: `iup-3.14_Linux35_64_lib.tar.gz`
* **>= 3.2**: `iup-3.14_Linux32_64_lib.tar.gz`
* **2.6**: `iup-3.14_Linux26g4_64_lib.tar.gz`
4. Navigate to the folder where you downloaded the tarball to in a terminal.
5. Extract the tarball:
* `mkdir iup_libs/`
* `tar -xzvf <tarball file> -C iup_libs/`
6. Install the binaries:
* `cd iup_libs/` (The install script must be run in its folder.)
* You can run either, or both, of the following two commands:
* To install the dynamic libraries: `sudo ./install`
* To install the static libraries: `sudo ./install_dev`
7. Follow the prompts in the installer.
Once the installer completes, you are finished. If you later want to uninstall IUP, open that `iup_libs/` folder in a terminal and run `sudo ./uninstall`. Otherwise, you may now delete the tarball and/or the `iup_libs/` folder.
[iup-compile]: http://webserver2.tecgraf.puc-rio.br/iup/en/guide.html#buildlib
[iup-source]: http://webserver2.tecgraf.puc-rio.br/iup/en/svn.html
***
### OS X
Before you install IUP, you need to install GTK+. (An IUP driver for Cocoa was under development, but as of 7/5/2015 is not being worked on.) You can use version 2.x or 3.x, IUP will work with both.
To install GTK+ 2:
```
brew install gtk+
```
To install GTK+ 3:
```
brew install gtk+3
```
**Note:** if you have troubles building after installing GTK+ 3, please consult [this StackOverflow answer](http://stackoverflow.com/a/20114598/1299804).
Once GTK+ is installed, you can download and install the precompiled Mac OS X IUP binary available [here][os-x]. It appears the only download available is for OS X 10.10 64-bit.
Once you have downloaded the tarball, the installation process *should be* equivalent to Linux's starting at **Step 4**.
[os-x]: http://sourceforge.net/projects/iup/files/3.14/Other%20Libraries/
***
Comparison to Other UI Frameworks
---------------------------------
**NOTE**: This list is *far* from exhaustive and may contain outdated information.
Pull requests for corrections and additions are welcome!
* KISS-UI
* Build Status: [](https://travis-ci.org/cybergeek94/kiss-ui)
* Supported Platforms: Windows (using Win32 APIs), Linux and Mac (using GTK+)
* Native Look and Feel: **Yes**
* "Hello, World!" LOC: **[18][kiss-ui-hw]**
* External Crates: **2**
* External Native Libs: 1
* [PistonDevelopers/conrod][conrod]
* Build Status: [](https://travis-ci.org/PistonDevelopers/conrod)
* Supported Platforms: Windows, Mac, Linux
* Native Look and Feel: No
* "Hello, World!" LOC: [40][conrod-hw] (estimated based on linked example)
* External Crates: 9 (not including testing crates and transitive dependencies)
* External Native Libs: **~0** (depends on backend used)
* [rust-gnome/gtk][rgtk]
* Build Status: [](https://travis-ci.org/rust-gnome/gtk)
* Supported Platforms: Windows, Mac, Linux
* Native Look and Feel: **Yes**
* "Hello, World!" LOC: [23][rust-gnome-hw]
* External Crates: 10 (1 local but pulled from Crates.io)
* External Native Libs: ~5 (installed on most Linux distros/external on Windows, Mac)
Lines of code should be listed based on the `# sloc` stat on the Github file page. The raw linecount includes empty lines, which can arbitrarily affect the linecount.
Enabling Visual Styles on Windows
---------------------------------
Since Rust/Cargo currently do not support adding resource items to executables, Windows XP and later need an external manifest file to enable visual styles in KISS-UI applications. Otherwise the visual style will be Windows Classic.
However, we have made this very simple to do! Simply copy the `kiss-app.manifest` file from this repo into the folder of your KISS-UI based executable, rename the file to `<executable name>.manifest` (including the `.exe` extension, e.g. `my_executable.exe.manifest`), and run the executable as-is. You may need to delete and replace or rebuild the executable for this to take effect, as Windows appears to cache manifest file data, likely to avoid reparsing it on each run.
Optionally, you can edit the `name=` and the `<description>` values in the manifest file, using any text editor. However, it is unclear to the author what these actually affect.
[kiss-ui-hw]: https://github.com/cybergeek94/kiss-ui/blob/master/examples/window_test.rs
[conrod]: https://github.com/PistonDevelopers/conrod
[conrod-hw]: https://github.com/PistonDevelopers/conrod/blob/master/examples/counter.rs
[rust-gnome-hw]: https://github.com/rust-gnome/examples/blob/master/src/basic.rs
[rgtk]: https://github.com/rust-gnome/gtk
================================================
FILE: SCREENSHOTS.md
================================================
KISS-UI Screenshots
-------------------
Below are screenshots of the KISS-UI examples, on Windows 7 with Visual Styles enabled (see the README for more info), and on Linux Mint using the [Nightlife - Mint][nightlife-mint] theme.
We at KISS-UI aren't exactly designers, so feel free to let us know how we can make these better aesthetically.
[nightlife-mint]: http://cinnamon-spices.linuxmint.com/themes/view/31
###`examples/button_test.rs`
This shows the main example as well as the dialogs that pop up when the "Message" and "Alert" buttons are clicked, respectively.
####Windows
Not sure why the borders are cut off on the other dialogs.
  
####GTK+
  
###`examples/progress_test.rs`
**Note**: the dashed version of the progress bar isn't always distinguished. It depends on the platform and visual theme being used.
####Windows

####GTK+

###`examples/show_image.rs`
[Thanks to some wonderful suggestions on Reddit, this is no longer ugly!](http://www.reddit.com/r/rust/comments/37eezt/by_popular_demand_i_put_together_some_screenshots/crme9t8)
####Windows

####GTK+

###`examples/textbox_test.rs`
This example shows the main dialog as well as the one that pops up when the "Save" button is clicked.
####Windows
Notice the very faint drop-shadow around the right dialog, which is the currently focused one. This screencap was taken by selecting screen area instead of a specific window like the others.

####GTK+
The same was done in Linux.

###`examples/window_test.rs`
#####Note: resized to fit the page better.
####Windows

####GTK+

================================================
FILE: build_docs.sh
================================================
git merge master
cargo doc
rm -rf doc/
cp -avr target/doc/ doc/
================================================
FILE: examples/button_test.rs
================================================
#[macro_use]
extern crate kiss_ui;
use kiss_ui::prelude::*;
use kiss_ui::button::Button;
use kiss_ui::container::{Horizontal, Vertical};
use kiss_ui::dialog::{self, AlertPopupBuilder};
fn main() {
kiss_ui::show_gui(||
Dialog::new(
Vertical::new(children![
Horizontal::new(
children![
Button::new()
.set_label("Message #1")
.set_name("Message_1")
.set_onclick(show_message_dialog),
Button::new()
.set_label("Message #2")
.set_name("Message_2")
.set_onclick(show_message_dialog),
Button::new()
.set_label("change the name")
.set_name("bar")
.set_onclick(show_change_name),
]
)
.set_elem_spacing_pixels(10),
Horizontal::new(
children![
Button::new()
.set_label("Alert")
.set_onclick(show_alert_dialog),
Button::new()
.set_label("Close")
.set_onclick(close_dialog),
]
)
.set_elem_spacing_pixels(10)
])
)
.set_title("Button test!")
)
}
fn show_message_dialog(btn: Button) {
let name = btn.get_name().unwrap();
dialog::message_popup("Good job!", format!("You clicked the button {:?}!", name));
}
fn show_change_name(btn: Button) {
let name = btn.get_name().unwrap();
dialog::message_popup("Lets try it!", format!("The button's name is '{}'!\n
Now we try to change it!\n
You will get a Panic if you still use the 'name'", name));
// drop(name); // <= uncomment this line
btn.set_name("foo");
show_message_dialog(btn);
}
fn show_alert_dialog(_: Button) {
let res = AlertPopupBuilder::new("Alert!", "You clicked the other button!", "Yes")
.button2("No")
.button3("Cancel")
.popup();
println!("Alert result = {}", res);
}
fn close_dialog(_: Button) -> CallbackStatus {
println!("Closing dialog!");
CallbackStatus::Close
}
================================================
FILE: examples/progress_test.rs
================================================
#[macro_use]
extern crate kiss_ui;
use kiss_ui::callback::OnShow;
use kiss_ui::container::Vertical;
use kiss_ui::dialog::Dialog;
use kiss_ui::progress::ProgressBar;
use kiss_ui::text::Label;
use kiss_ui::timer::Timer;
fn main() {
kiss_ui::show_gui(|| {
let regular = ProgressBar::new();
let dashed = ProgressBar::new().set_dashed(true);
let dialog = Dialog::new(
Vertical::new(
children![
Label::new("Regular:"),
regular.clone(),
Label::new("Dashed:"),
dashed.clone(),
Label::new("Indefinite:"),
ProgressBar::new().set_indefinite(true),
]
)
);
dialog
.set_title("Progressbar Test")
.set_on_show(move |_| {
let on_timer_interval = move |timer: Timer|{
regular.add_value(0.1);
dashed.add_value(0.1);
if regular.get_value() == 1.0 {
timer.stop();
}
};
Timer::new()
.set_interval(1000)
.set_on_interval(on_timer_interval)
.start();
});
dialog
});
}
================================================
FILE: examples/show_image.rs
================================================
#[macro_use]
extern crate kiss_ui;
use kiss_ui::container::Horizontal;
use kiss_ui::dialog::Dialog;
use kiss_ui::image::{Image, ImageContainer};
use kiss_ui::text::Label;
fn main() {
const WIDTH: u32 = 512;
const HEIGHT: u32 = 512;
let image_data: Vec<_> = (0..HEIGHT)
.flat_map(|y| (0..WIDTH).map(move |x| color(x, y)))
.collect();
kiss_ui::show_gui(|| {
Dialog::new(
Horizontal::new(
children![
Label::new("")
.set_image(Image::new_rgb(WIDTH, HEIGHT, &image_data)),
]
)
)
.set_title(format!("Image! ({width} x {height})", width=WIDTH, height=HEIGHT))
});
}
/// Play with this function and let us know what you come up with!
fn color(x: u32, y: u32) -> (u8, u8, u8) {
// Suggested by /u/GBGamer117
((x ^ y) as u8, y as u8, x as u8)
// Suggested by /u/Effnote
// ((x ^ y) as u8, ((x + 2) ^ (y + 1)) as u8, ((x + 4) ^ (y + 2)) as u8)
// Suggested by /u/ImSoCabbage
// let val = (x ^ y) as u8;
// (val, val, val)
// (255 - val, val, val) // Inverted red
}
================================================
FILE: examples/textbox_test.rs
================================================
#[macro_use]
extern crate kiss_ui;
use kiss_ui::prelude::*;
use kiss_ui::button::Button;
use kiss_ui::container::Vertical;
use kiss_ui::dialog;
use kiss_ui::text::{Label, TextBox};
fn main() {
kiss_ui::show_gui(|| {
Dialog::new(
Vertical::new(
children![
Label::new("Enter a message:"),
TextBox::new()
.set_visible_columns(20)
.set_name("my_textbox"),
Button::new()
.set_label("Save")
.set_onclick(show_alert_message),
]
)
)
.set_title("Textbox Test")
});
}
fn show_alert_message(clicked: Button) {
let dialog = clicked.get_dialog().unwrap();
let text_box = dialog.get_child("my_textbox").unwrap()
.try_downcast::<TextBox>().ok().expect("child my_textbox was not a TextBox!");
let text = text_box.get_text();
dialog::message_popup("Message saved!", format!("Your message: {}", text));
}
================================================
FILE: examples/window_test.rs
================================================
#[macro_use]
extern crate kiss_ui;
use kiss_ui::container::Horizontal;
use kiss_ui::dialog::Dialog;
use kiss_ui::text::Label;
fn main() {
kiss_ui::show_gui(|| {
Dialog::new(
Horizontal::new(
children![
Label::new("Hello, world!"),
]
)
)
.set_title("Hello, world!")
.set_size_pixels(640, 480)
});
}
================================================
FILE: kiss-app.manifest
================================================
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="1.0.0.0"
processorArchitecture="*"
name="KISS-UI Application"
type="win32"
/>
<description>KISS-UI Application</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</assembly>
================================================
FILE: src/attrs.rs
================================================
c_str_consts! {
//Globals
UTF8_MODE = "UTF8MODE",
// Basic widget attributes
TITLE = "TITLE",
VALUE = "VALUE",
ACTIVE = "ACTIVE",
NAME = "NAME",
VISIBLE = "VISIBLE",
// Rendering attributes
RASTERSIZE = "RASTERSIZE",
POSITION = "POSITION",
// Layout attributes
ALIGNMENT_VERT = "ALIGNMENTLIN",
ALIGNMENT_HORI = "ALIGNMENTCOL",
ORIENTATION = "ORIENTATION",
NUMDIV = "numdiv",
// Specific to `Absolute`
CX = "CX",
CY = "CY",
//Textbox attributes
MULTILINE = "MULTILINE",
VISIBLE_COLUMNS = "VISIBLECOLUMNS",
VISIBLE_LINES = "VISIBLELINES",
// Progressbar attributes
DASHED = "DASHED",
MARQUEE = "MARQUEE",
MIN = "MIN",
MAX = "MAX",
//Timer attribute
TIME = "TIME",
RUN = "RUN",
// Spacing between elements in a container
GAP = "GAP",
// Handles
IMAGE = "IMAGE",
//Callbacks
ACTION = "ACTION",
ACTION_CB = "ACTION_CB",
VALUE_CHANGED_CB = "VALUECHANGED_CB",
MAP_CB = "MAP_CB",
}
pub mod values {
c_str_consts! {
YES = "YES",
NO = "NO",
}
pub fn bool_yes_no(_bool: bool) -> &'static str {
match _bool {
true => YES,
false => NO,
}
}
}
================================================
FILE: src/base.rs
================================================
//! A general widget type that can be specialized at runtime.
use widget_prelude::*;
use ::KISSContext;
use std::borrow::Borrow;
/// A general widget type that can be specialized at runtime via `Downcast`.
pub struct BaseWidget(IUPPtr);
impl BaseWidget {
/// Attempt to load a widget named by `name` from internal storage.
///
/// If successful, the `BaseWidget` can then be downcast to the original widget type.
///
/// Returns `None` if no widget by that name was found.
///
/// ##Panics
/// If called before `kiss_ui::show_gui()` is invoked or after it returns.
pub fn load<N: Borrow<str>>(name: N) -> Option<BaseWidget> {
KISSContext::load_widget(&name)
}
/// Attempt to downcast this `BaseWidget` to a more specialized widget type.
///
/// This will return an error if the underlying widget class is different than the one
/// it is being cast to.
pub fn try_downcast<T>(self) -> Result<T, Self> where T: Downcast {
T::try_downcast(self)
}
}
impl_widget! { BaseWidget }
/// A trait describing a widget's ability to be downcast from `BaseWidget`.
pub trait Downcast: Widget {
/// Attempt to downcast `base` to the `Self` type,
/// returning `Err(base)` if unsuccessful.
fn try_downcast(base: BaseWidget) -> Result<Self, BaseWidget> {
if Self::can_downcast(&base) {
Ok(unsafe { Self::downcast(base) })
} else {
Err(base)
}
}
// These are not meant for end-users to call.
// They are an implementation detail of `try_downcast()`.
#[doc(hidden)]
unsafe fn downcast(base: BaseWidget) -> Self {
Self::from_ptr(base.ptr())
}
#[doc(hidden)]
fn can_downcast(base: &BaseWidget) -> bool;
}
================================================
FILE: src/button.rs
================================================
//! Buttons that can receive user input.
use widget_prelude::*;
use std::ptr;
/// A button that can be clicked momentarily and invoke a callback when this happens.
pub struct Button(IUPPtr);
impl Button {
/// Create a new `Button` with no label.
pub fn new() -> Button {
unsafe {
let ptr = ::iup_sys::IupButton(ptr::null(), ptr::null());
Self::from_ptr(ptr)
}
}
/// Set the label of this button. Can be blank.
pub fn set_label<L: Into<String>>(self, label: L) -> Self {
self.set_str_attribute(::attrs::TITLE, label);
self
}
}
impl_widget! { Button, "button" }
impl_onclick! { Button }
impl ::image::ImageContainer for Button {}
================================================
FILE: src/callback.rs
================================================
//! Traits for notifying client code when the state of a KISS-UI widget is updated.
use widget_prelude::*;
use iup_sys::Ihandle;
use std::cell::RefCell;
use std::collections::HashMap;
/// Set this within a callback to tell the framework if it should close or not.
///
/// If `Callback::close.set()` is called within a callback, then when the callback returns,
/// the dialog containing the widget on which the callback was invoked will be closed.
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum CallbackStatus {
//Ignore,
/// The default `CallbackStatus`, does nothing when set.
Default,
/// If this is set within a callback, then when the callback returns the dialog containing the
/// widget on which the callback was invoked will be closed.
Close,
//Continue,
}
impl CallbackStatus {
pub fn close(&mut self) {
*self = CallbackStatus::Close;
}
#[doc(hidden)]
pub fn to_cb_return(self) -> ::libc::c_int {
use self::CallbackStatus::*;
match self {
Close => ::iup_sys::IUP_CLOSE,
Default => ::iup_sys::IUP_DEFAULT,
// _ => unimplemented!(),
}
}
}
impl From<()> for CallbackStatus {
fn from(_: ()) -> CallbackStatus {
CallbackStatus::Default
}
}
pub trait Callback<Args>: 'static {
fn on_callback(&mut self, args: Args) -> CallbackStatus;
}
impl<Args, Out: Into<CallbackStatus>, F: 'static> Callback<Args> for F where F: FnMut(Args) -> Out {
/// Because of the `impl From<()> for CallbackStatus`, closures that return `()` can be
/// accepted by this impl.
fn on_callback(&mut self, args: Args) -> CallbackStatus {
self(args).into()
}
}
#[doc(hidden)]
pub type CallbackMap<T> = RefCell<HashMap<*mut Ihandle, Box<Callback<T>>>>;
macro_rules! callback_impl {
($cb_attr:expr, $base:expr, $callback:expr, $self_ty:ident) => (
{
thread_local!(
static CALLBACKS: ::callback::CallbackMap<$self_ty> =
::std::cell::RefCell::new(::std::collections::HashMap::new())
);
extern fn extern_callback(element: *mut ::iup_sys::Ihandle)
-> ::libc::c_int {
use ::callback::CallbackStatus;
let widget = unsafe { $self_ty::from_ptr(element) };
CALLBACKS.with(|callbacks|
callbacks.borrow_mut()
.get_mut(&widget.ptr())
.map(|cb| cb.on_callback(widget))
).unwrap_or(CallbackStatus::Default).to_cb_return()
}
CALLBACKS.with(|callbacks|
callbacks.borrow_mut().insert($base.ptr(), Box::new($callback))
);
$base.set_callback($cb_attr, extern_callback);
}
)
}
/// A trait describing a widget that can be clicked, and can notify client code when this occurs.
pub trait OnClick: Widget {
fn set_onclick<Cb>(self, on_click: Cb) -> Self where Cb: Callback<Self>;
}
macro_rules! impl_onclick {
($self_ty:ident) => (
impl $crate::callback::OnClick for $self_ty {
fn set_onclick<Cb>(self, on_click: Cb) -> Self where Cb: ::callback::Callback<Self> {
callback_impl! { $crate::attrs::ACTION, self, on_click, $self_ty }
self
}
}
)
}
/// A trait describing a widget which has a value that can be changed by the user, and can notify
/// client code when this occurs.
pub trait OnValueChange: Widget {
fn set_on_value_changed<Cb>(self, on_value_chaged: Cb) -> Self where Cb: Callback<Self>;
}
macro_rules! impl_on_value_change {
($self_ty:ident) => (
impl $crate::callback::OnValueChange for $self_ty {
fn set_on_value_changed<Cb>(self, on_value_changed: Cb) -> Self where Cb: ::callback::Callback<Self> {
callback_impl! { $crate::attrs::VALUE_CHANGED_CB, self, on_value_changed, $self_ty }
self
}
}
)
}
/// A trait describing a widget that can be shown, and can notify client code when this occurs.
pub trait OnShow: Widget {
fn set_on_show<Cb>(self, on_show: Cb) -> Self where Cb: Callback<Self>;
}
macro_rules! impl_on_show {
($self_ty:ident) => (
impl ::callback::OnShow for $self_ty {
fn set_on_show<Cb>(self, on_show: Cb) -> Self where Cb: ::callback::Callback<Self> {
callback_impl! { ::attrs::MAP_CB, self, on_show, $self_ty }
self
}
}
)
}
================================================
FILE: src/container.rs
================================================
//! Assorted types that can contain multiple widgets.
//!
//! All container types can be nested.
//!
//! Use the `children!{}` macro in this crate to convert a heterogeneous list of widgets into a
//! `Vec<BaseWidget>` for the container constructors.
use base::BaseWidget;
use widget_prelude::*;
/// Vertical alignment setting, used by `Horizontal` and `Grid`.
#[derive(Copy, Clone)]
pub enum VAlign {
Top,
Center,
Bottom,
}
impl VAlign {
fn as_cstr(self) -> &'static str {
use self::VAlign::*;
match self {
Top => cstr!("ATOP"),
Center => cstr!("ACENTER"),
Bottom => cstr!("ABOTTOM"),
}
}
}
/// Horizontal alignment setting, used by `Vertical` and `Grid`.
#[derive(Copy, Clone)]
pub enum HAlign {
Left,
Center,
Right,
}
impl HAlign {
fn as_cstr(self) -> &'static str {
use self::HAlign::*;
match self {
Left => cstr!("ALEFT"),
Center => cstr!("ACENTER"),
Right => cstr!("ARIGHT"),
}
}
}
/// The behavior of this enum depends on its point of use.
#[derive(PartialEq, Eq, Copy, Clone)]
pub enum Orientation {
Vertical,
Horizontal,
}
impl Orientation {
#[doc(hidden)]
pub fn as_cstr(self) -> &'static str {
use self::Orientation::*;
match self {
Vertical => cstr!("VERTICAL"),
Horizontal => cstr!("HORIZONTAL"),
}
}
}
fn raw_handle_vec<B>(widgets: B) -> Vec<IUPPtr> where B: AsRef<[BaseWidget]> {
let mut raw_handles: Vec<_> = widgets.as_ref().iter().cloned().map(BaseWidget::ptr).collect();
raw_handles.push(::std::ptr::null_mut());
raw_handles
}
/// A builder for `Absolute`, used to create and add children.
pub struct AbsoluteBuilder {
handles: Vec<IUPPtr>,
}
impl AbsoluteBuilder {
fn new() -> AbsoluteBuilder {
AbsoluteBuilder { handles: Vec::new() }
}
/// Add a child to the `Absolute` at the given coordinates, relative to the top-left corner of
/// the container.
pub fn add_child_at<W: Widget>(&mut self, x: u32, y: u32, child: W) -> &mut Self {
Absolute::set_child_pos(x, y, child);
self.handles.push(child.ptr());
self
}
}
/// A container type that makes no effort to arrange its children. Instead, they must be positioned
/// manually.
pub struct Absolute(IUPPtr);
impl Absolute {
/// Create a new absolute container using the given closure, which will be passed a mutable builder
/// instance.
///
/// The builder is necessary to ensure that the children have their positions set correctly, as
/// `Widget::set_position` will not set the attributes that this particular container is
/// expecting.
pub fn new<F>(build_fn: F) -> Absolute where F: FnOnce(&mut AbsoluteBuilder) {
let mut builder = AbsoluteBuilder::new();
build_fn(&mut builder);
unsafe {
let ptr = ::iup_sys::IupCboxv(builder.handles.as_mut_ptr());
Self::from_ptr(ptr)
}
}
/// Set the position of a child of an `Absolute` relative to its top-left corner.
pub fn set_child_pos<W: Widget>(x: u32, y: u32, child: W) {
child.set_int_attribute(::attrs::CX, x as i32);
child.set_int_attribute(::attrs::CY, y as i32);
}
}
impl_widget! { Absolute, "cbox" }
/// A container widget that lines up its children from left to right.
pub struct Horizontal(IUPPtr);
impl Horizontal {
/// Create a new horizontal container with the given vector or array of children, which may
/// also be empty.
///
/// See the `children![]` macro in this crate for more info.
pub fn new<C>(children: C) -> Horizontal where C: AsRef<[BaseWidget]> {
let mut raw_handles = raw_handle_vec(children);
unsafe {
let ptr = ::iup_sys::IupHboxv(raw_handles.as_mut_ptr());
Self::from_ptr(ptr)
}
}
pub fn set_valign(self, valign: VAlign) -> Self {
self.set_const_str_attribute(::attrs::ALIGNMENT_VERT, valign.as_cstr());
self
}
pub fn set_elem_spacing_pixels(self, spacing: u32) -> Self {
self.set_str_attribute(::attrs::GAP, spacing.to_string());
self
}
}
impl_widget! { Horizontal, "hbox" }
/// A container widget that lines up its children from top to bottom.
pub struct Vertical(IUPPtr);
impl Vertical {
pub fn new<C>(children: C) -> Vertical where C: AsRef<[BaseWidget]> {
let mut raw_handles = raw_handle_vec(children);
unsafe {
let ptr = ::iup_sys::IupVboxv(raw_handles.as_mut_ptr());
Self::from_ptr(ptr)
}
}
pub fn set_halign(self, halign: HAlign) -> Self {
self.set_const_str_attribute(::attrs::ALIGNMENT_HORI, halign.as_cstr());
self
}
pub fn set_elem_spacing_pixels(self, spacing: u32) -> Self {
self.set_str_attribute(::attrs::GAP, spacing.to_string());
self
}
}
impl_widget! { Vertical, "vbox" }
/// A container widget that lines up its children from left to right, and from top to bottom.
pub struct Grid(IUPPtr);
impl Grid {
pub fn new<C>(children: C) -> Grid where C: AsRef<[BaseWidget]> {
let mut raw_handles = raw_handle_vec(children);
unsafe {
let ptr = ::iup_sys::IupGridBoxv(raw_handles.as_mut_ptr());
Self::from_ptr(ptr)
}
}
pub fn set_valign(self, valign: VAlign) -> Self {
self.set_const_str_attribute(::attrs::ALIGNMENT_VERT, valign.as_cstr());
self
}
pub fn set_halign(self, halign: HAlign) -> Self {
self.set_const_str_attribute(::attrs::ALIGNMENT_HORI, halign.as_cstr());
self
}
/// Based on the orientation, set the number of children to place in a:
///
/// * `Vertical`: **column**
/// * `Horizontal`: **row**
///
/// before beginning the next one.
pub fn set_ndiv(self, ndiv: u32) -> Self {
self.set_int_attribute(::attrs::NUMDIV, ndiv as i32);
self
}
/// Sets how children are distributed in the container.
///
/// * `Vertical`: The container will fill columns first.
///
/// Visual example (`ndiv=3` grid with 7 children):
/// <table>
/// <tr>
/// <td>Child</td>
/// <td>Child</td>
/// <td>Child</td>
/// </tr>
/// <tr>
/// <td>Child</td>
/// <td>Child</td>
/// </tr>
/// <tr>
/// <td>Child</td>
/// <td>Child</td>
/// </tr>
/// </table>
///
/// * `Horizontal`: The container will fill rows first. **Default.**
///
/// Visual example (`ndiv=3` grid with 7 children):
/// <table>
/// <tr>
/// <td>Child</td>
/// <td>Child</td>
/// <td>Child</td>
/// </tr>
/// <tr>
/// <td>Child</td>
/// <td>Child</td>
/// <td>Child</td>
/// </tr>
/// <tr>
/// <td>Child</td>
/// </tr>
/// </table>
///
pub fn set_orientation(&mut self, orientation: Orientation) -> &mut Self {
self.set_const_str_attribute(::attrs::ORIENTATION, orientation.as_cstr());
self
}
}
impl_widget! { Grid, "matrix" }
/// Convert a heterogeneous list of widgets into a `Vec<BaseWidget>`,
/// suitable for passing to any function that takes `AsRef<[BaseWidget]>`, such as a constructor
/// for one of the container types.
#[macro_export]
macro_rules! children [
// Accepts invocation with or without a final comma.
($($child:expr),+,) => (children![$($child),+]);
($($child:expr),+) => ({
use ::kiss_ui::widget::Widget;
vec![$($child.to_base()),+]
});
() => (vec![]);
];
================================================
FILE: src/dialog.rs
================================================
//! KISS-UI top-level dialogs (windows)
use base::BaseWidget;
use widget_prelude::*;
use ::iup_sys;
use std::ffi::CString;
use std::ptr;
/// A top-level dialog that can create a new native window when shown,
/// and can contain a single widget (which can be a container for many widgets).
pub struct Dialog(IUPPtr);
impl Dialog {
/// Create a new dialog with a single child.
///
/// To create a dialog containing multiple widgets, use a struct from the `container` module.
///
/// ##Note
/// This does **not** make the dialog appear on screen. `.show()` must be called after the
/// dialog has been configured.
///
/// ##Panics
/// If called outside a valid KISS-UI context.
pub fn new<W>(contents: W) -> Dialog where W: Widget {
assert_kiss_running!();
unsafe {
let ptr = iup_sys::IupDialog(contents.ptr());
Self::from_ptr(ptr)
}
}
/// Create a new dialog with no children.
///
/// ##Panics
/// If called outside a valid KISS-UI context.
pub fn empty() -> Dialog {
assert_kiss_running!();
unsafe {
let ptr = iup_sys::IupDialog(ptr::null_mut());
Self::from_ptr(ptr)
}
}
/// Set the title of this dialog, which will appear in the title bar of the native window.
pub fn set_title<T: Into<String>>(self, title: T) -> Self {
self.set_str_attribute(::attrs::TITLE, title);
self
}
/// Set the size of this dialog in pixels.
pub fn set_size_pixels(self, width: u32, height: u32) -> Self {
let rastersize = format!("{}x{}", width, height);
self.set_str_attribute(::attrs::RASTERSIZE, rastersize);
self
}
/// Get a child of this dialog named by `name`.
///
/// Returns `None` if the child was not found.
pub fn get_child(self, name: &str) -> Option<BaseWidget> {
let name = CString::new(name).unwrap();
unsafe {
let child_ptr = iup_sys::IupGetDialogChild(self.ptr(), name.as_ptr());
BaseWidget::from_ptr_opt(child_ptr)
}
}
}
impl Destroy for Dialog {}
impl_widget! { Dialog, "dialog" }
impl_on_show! { Dialog }
/// Popup a message dialog and block until it is closed, by either the OK button or the exit
/// button.
pub fn message_popup<T: Into<String>, M: Into<String>>(title: T, message: M) {
assert_kiss_running!();
let title = CString::new(title.into()).unwrap();
let message = CString::new(message.into()).unwrap();
unsafe {
iup_sys::IupMessage(title.as_ptr(), message.as_ptr());
}
}
/// A builder for an alert dialog that can show a message and up to 3 buttons for the user's
/// response.
pub struct AlertPopupBuilder {
pub title: String,
pub message: String,
pub button1: String,
pub button2: Option<String>,
pub button3: Option<String>,
}
impl AlertPopupBuilder {
pub fn new<T: Into<String>, M: Into<String>, B1: Into<String>>(
title: T, message: M, button1: B1
) -> AlertPopupBuilder {
AlertPopupBuilder {
title: title.into(),
message: message.into(),
button1: button1.into(),
button2: None,
button3: None,
}
}
/// Set the text of the second button
pub fn button2<B2: Into<String>>(mut self, button2: B2) -> Self {
self.button2 = Some(button2.into());
self
}
pub fn button3<B3: Into<String>>(mut self, button3: B3) -> Self {
self.button3 = Some(button3.into());
self
}
/// Popup the dialog and block until the user takes an action.
///
/// Returns: which button was pressed, or **0** if the dialog was closed.
pub fn popup(self) -> i32 {
let title = CString::new(self.title).unwrap();
let message = CString::new(self.message).unwrap();
let button1 = CString::new(self.button1).unwrap();
let button2 = self.button2.map(|b2| CString::new(b2).unwrap());
let button3 = self.button3.map(|b3| CString::new(b3).unwrap());
unsafe {
iup_sys::IupAlarm(
title.as_ptr(),
message.as_ptr(),
button1.as_ptr(),
button2.as_ref().map_or_else(ptr::null, |b2| b2.as_ptr()),
button3.as_ref().map_or_else(ptr::null, |b3| b3.as_ptr()),
)
}
}
}
================================================
FILE: src/image.rs
================================================
//! Renderable image buffers.
use widget_prelude::*;
use base::Downcast;
use std::mem;
/// An image buffer allocated by IUP.
///
/// ##Note: Not a Renderable Widget
/// While this type can be dereferenced and converted to `BaseWidget`, it is *not* a renderable
/// widget and adding it to a container will have no visual effect.
///
/// Instead, it should be set on another widget type that implements the `ImageContainer` trait,
/// which will handle the actual rendering.
///
/// ##Note: Memory Usage
/// This struct should be freed by calling `.destroy()` on it when it is no longer in use.
/// Otherwise, it will be freed when `kiss_ui::show_gui()` exits^([citation needed]).
///
/// ##Note: Cloning
/// Cloning this image does not duplicate its allocation. Thus, destroying one image cloned from
/// another will destroy them both.
pub struct Image(IUPPtr);
impl Image {
/// Create a new RGB image buffer from a slice of 3-byte tuples, copying the data into a new
/// allocation.
///
/// See `transmute_buffer_rgb()` in this module.
///
/// ##Panics
/// If `width * height` is not equal to `pixels.len()`.
pub fn new_rgb(width: u32, height: u32, pixels: &[(u8, u8, u8)]) -> Image {
assert_eq!((width * height) as usize, pixels.len());
unsafe {
let ptr = ::iup_sys::IupImageRGB(width as i32, height as i32, pixels.as_ptr() as *const u8);
Self::from_ptr(ptr)
}
}
/// Create a new RGBA image buffer from a slice of 4-byte tuples, copying the data into a new
/// allocation.
///
/// See `transmute_buffer_rgba` in this module.
///
/// ##Panics
/// If `width * height` is not equal to `pixels.len()`.
pub fn new_rgba(width: u32, height: u32, pixels: &[(u8, u8, u8, u8)]) -> Image {
assert_eq!((width * height) as usize, pixels.len());
unsafe {
let ptr = ::iup_sys::IupImageRGBA(width as i32, height as i32, pixels.as_ptr() as *const u8);
Self::from_ptr(ptr)
}
}
}
impl Destroy for Image {}
impl_widget! { Image, ["image", "imagergb", "imagergba"] }
/// Cast a slice of bytes to a slice of 3-byte tuples without copying.
///
/// Returns `None` if `buf.len()` is not evenly divisible by 3.
pub fn transmute_buffer_rgb(buf: &[u8]) -> Option<&[(u8, u8, u8)]> {
if buf.len() % 3 == 0 {
Some(unsafe { mem::transmute(buf) })
} else {
None
}
}
/// Cast a slice of bytes to a slice of 4-byte tuples without copying.
///
/// Returns `None` if `buf.len()` is not evenly divisible by 4.
pub fn transmute_buffer_rgba(buf: &[u8]) -> Option<&[(u8, u8, u8, u8)]> {
if buf.len() % 4 == 0 {
Some(unsafe { mem::transmute(buf) })
} else {
None
}
}
/// A trait describing an object that can render an image within itself.
pub trait ImageContainer: Widget {
/// Set the image this widget is to render and return `self` for method chaining.
fn set_image(self, image: Image) -> Self {
self.set_attr_handle(::attrs::IMAGE, image);
self
}
/// Get a copy of the image set on this widget, if any.
fn get_image(&self) -> Option<Image> {
use base::BaseWidget;
self.get_attr_handle(::attrs::IMAGE)
.map(BaseWidget::try_downcast::<Image>)
.and_then(Result::ok)
}
}
================================================
FILE: src/lib.rs
================================================
//! A UI framework for Rust based on the KISS principle: "Keep It Simple, Stupid!"
//!
//! Built on top of the [IUP GUI library for C.][iup]
//!
//! ##Note: "valid KISS-UI context"
//! All KISS-UI static widget methods will panic if called before `kiss_ui::show_gui()` is invoked or
//! after it returns.
//!
//! This is because the underlying IUP library has been either, respectively, not initialized yet
//! or already deinitialized, and attempting to interact with it in either situation will likely cause
//! undefined behavior.
//!
//! ##Note: This is a (technically) leaky abstraction.
//! Because IUP only frees all its allocations when it is deinitialized, all widgets created by KISS-UI
//! will remain in-memory until `kiss_ui::show_gui()` returns. While unbounded memory growth can
//! happen with complex applications, this should not be an issue for most use-cases.
//!
//! However, some types *do* allocate large chunks of memory, or other valuable system resources,
//! and should be manually freed when they are no longer being used.
//! This is most evident with the `Image` struct, which can allocate large backing buffers for image data.
//!
//! All types that should be manually freed expose a `.destroy()` method which should be called
//! when they are no longer being used. This can safely be called multiple times on clones of the
//! widget types^([citation needed]).
//!
//! [iup]: http://webserver2.tecgraf.puc-rio.br/iup/
extern crate libc;
extern crate iup_sys;
macro_rules! assert_kiss_running (
() => (
assert!(
::KISS_RUNNING.load(::std::sync::atomic::Ordering::Acquire),
"No KISS-UI widget methods may be called before `kiss_ui::show_gui()` is invoked or after it returns!"
)
)
);
#[macro_use]
pub mod widget;
#[macro_use]
pub mod utils;
// Internal use modules
mod attrs;
// User-facing modules
#[macro_use]
pub mod callback;
pub mod base;
pub mod button;
pub mod container;
pub mod dialog;
pub mod image;
pub mod progress;
pub mod text;
pub mod timer;
use std::borrow::Borrow;
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::ptr;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
use base::BaseWidget;
use dialog::Dialog;
use widget::Widget;
use utils::cstr::AsCStr;
use widget_prelude::IUPPtr;
mod widget_prelude {
pub use widget::{Widget, IUPWidget, Destroy, WidgetStr};
pub type IUPPtr = *mut ::iup_sys::Ihandle;
}
/// A module that KISS-UI users can glob-import to get the most common types.
pub mod prelude {
pub use base::BaseWidget;
pub use dialog::Dialog;
pub use container::Orientation;
pub use callback::{CallbackStatus, OnClick, OnShow, OnValueChange};
pub use widget::{Widget, Destroy};
}
static KISS_RUNNING: AtomicBool = ATOMIC_BOOL_INIT;
thread_local! { static CONTEXT: KISSContext = KISSContext::default() }
#[derive(Default)]
struct KISSContext {
widget_store: RefCell<HashMap<String, BaseWidget>>,
// FIXME: use Rc<()> once Rc::is_unique stabilizes
borrowed_strs: RefCell<HashMap<IUPPtr, HashMap<&'static str, Rc<Cell<usize>>>>>,
}
impl KISSContext {
fn assert_str_not_borrowed(widget: IUPPtr, str_: &'static str) {
assert_kiss_running!();
let is_borrowed = CONTEXT.with(|context|
context.borrowed_strs.borrow()
.get(&widget)
.and_then(|widget_strs|
widget_strs.get(str_)
.map(|refcount| refcount.get() != 0)
)
.unwrap_or(false)
);
assert!(
!is_borrowed,
"Cannot update the value of a string property of a widget if it's been previously borrowed!"
);
}
fn str_refcount(widget: IUPPtr, str_: &'static str) -> Rc<Cell<usize>> {
assert_kiss_running!();
CONTEXT.with(|context|
context.borrowed_strs.borrow_mut()
.entry(widget).or_insert_with(HashMap::new)
.entry(str_).or_insert_with(|| Rc::new(Cell::new(0)))
.clone()
)
}
fn store_widget<N: Into<String>, W: Widget>(name: N, widget: W) -> Option<BaseWidget> {
CONTEXT.with(|context|
context.widget_store.borrow_mut()
.insert(name.into(), widget.to_base())
)
}
fn load_widget<N: Borrow<str>>(name: &N) -> Option<BaseWidget> {
CONTEXT.with(|context|
context.widget_store.borrow().get(name.borrow()).cloned()
)
}
unsafe fn clear() {
CONTEXT.with(|context| {
context.widget_store.borrow_mut().clear();
context.borrowed_strs.borrow_mut().clear();
})
}
}
/// The entry point for KISS-UI. The closure argument should initialize and call `.show()`.
///
/// ##Blocks
/// Until all KISS-UI dialogs are closed.
///
/// ##Warning
/// No static widget methods from this crate may be called before this function is
/// invoked or after it returns, with the exception of the closure passed to this function.
///
/// While this function is blocked and the IUP event loop is running, any reachable code is
/// considered a "valid KISS-UI context" and may create and interact with widgets and dialogs.
///
/// After it returns, IUP is deinitialized and all static widget methods will panic to avoid
/// undefined behavior.
///
/// ##Note: `Send` bound
/// This closure will be called in the same thread where `show_gui()` is invoked. No threading is
/// involved.
///
/// However, without the `Send` bound it would be possible to move widget types outside
/// of the closure with safe code and interact with them after IUP has been deinitialized,
/// which would cause undefined behavior.
///
/// Since no widget types are `Send`, this bound prevents this from happening without requiring
/// all widget methods to check if they were invoked in a valid context.
pub fn show_gui<F>(init_fn: F) where F: FnOnce() -> Dialog + Send {
assert!(
!KISS_RUNNING.compare_and_swap(false, true, Ordering::SeqCst),
"KISS-UI may only be running (in `kiss_ui::show_gui()`) in one thread at a time!"
);
unsafe {
assert!(iup_sys::IupOpen(ptr::null(), ptr::null()) == 0);
// Force IUP to always use UTF-8
iup_sys::IupSetGlobal(::attrs::UTF8_MODE.as_cstr(), ::attrs::values::YES.as_cstr());
}
init_fn().show();
unsafe {
iup_sys::IupMainLoop();
iup_sys::IupClose();
KISSContext::clear();
}
KISS_RUNNING.store(false, Ordering::SeqCst);
}
================================================
FILE: src/progress.rs
================================================
//! Progress bars and dialogs.
use widget_prelude::*;
use container::Orientation;
/// A widget that renders a bar which fills as its set value approaches a maximum.
///
/// For more info, see the [`IupProgressBar`][iup-progress] documentation. (Note: "marquee" is the
/// same as "indefinite")
///
/// [iup-progress]: http://webserver2.tecgraf.puc-rio.br/iup/en/elem/iupprogressbar.html
pub struct ProgressBar(IUPPtr);
impl ProgressBar {
/// Create a new progress bar.
pub fn new() -> ProgressBar {
unsafe {
let ptr = ::iup_sys::IupProgressBar();
Self::from_ptr(ptr)
}
}
/// Set this progress bar as indefinite or not.
///
/// In the indefinite state, the progress bar will not
/// show its true value; instead it will render a looping animation.
///
/// This may not have a visual effect on certain platforms.
pub fn set_indefinite(self, is_indefinite: bool) -> Self {
self.set_bool_attribute(::attrs::MARQUEE, is_indefinite);
self
}
/// Set if the progress bar should render solid (`false`) or dashed (`true`).
///
/// This may not have a visual effect on certain platforms.
pub fn set_dashed(self, dashed: bool) -> Self {
self.set_bool_attribute(::attrs::DASHED, dashed);
self
}
/// Set the maximum value of this progress bar, i.e. the value at which it will show full.
///
/// Defaults to `1.0`.
pub fn set_max(self, max: f32) -> Self {
self.set_float_attribute(::attrs::MAX, max);
self
}
/// Set the minimum value of this progress bar, i.e. the value at which it will be empty.
///
/// Defaults to `0.0`.
pub fn set_min(self, min: f32) -> Self {
self.set_float_attribute(::attrs::MIN, min);
self
}
/// Set the orientation of this progress bar.
///
/// * `Vertical`: The progress bar will render as a vertical bar, and fill from bottom to top.
/// * `Horizontal`: The progress bar will render as a horizontal bar, and fill from left to
/// right.
pub fn set_orientation(self, orientation: Orientation) -> Self {
self.set_const_str_attribute(::attrs::ORIENTATION, orientation.as_cstr());
self
}
/// Set the current value of this progress bar. Its rendered infill will be updated to reflect
/// the new value in relation to the minimum and maximum.
pub fn set_value(self, val: f32) -> Self {
self.set_float_attribute(::attrs::VALUE, val);
self
}
/// Get the current value.
pub fn get_value(self) -> f32 {
self.get_float_attribute(::attrs::VALUE)
}
/// Add `amt` to the current value and update it. `amt` may be negative.
pub fn add_value(self, amt: f32) -> Self {
let val = self.get_float_attribute(::attrs::VALUE);
self.set_float_attribute(::attrs::VALUE, val + amt);
self
}
}
impl_widget! { ProgressBar, "progressbar" }
================================================
FILE: src/text.rs
================================================
//! Widgets that can render and process text (labels, text boxes).
use widget_prelude::*;
use std::ffi::CString;
use std::ptr;
/// A static widget that renders text within its parent.
pub struct Label(IUPPtr);
impl Label {
/// Create a label with some text.
pub fn new<S: Into<String>>(text: S) -> Label {
let c_text = CString::new(text.into()).unwrap();
unsafe {
let ptr = ::iup_sys::IupLabel(c_text.as_ptr());
Self::from_ptr(ptr)
}
}
/// Create a blank label. The text can be set later.
pub fn new_empty() -> Label {
unsafe {
let ptr = ::iup_sys::IupLabel(ptr::null());
Self::from_ptr(ptr)
}
}
/// Update the text of this label.
///
/// ##Panics
/// If any `WidgetStr` instances from `self.get_text()` are still reachable.
pub fn set_text(self, text: &str) -> Self {
self.set_str_attribute(::attrs::TITLE, text);
self
}
/// Get the text of this label.
pub fn get_text(&self) -> WidgetStr {
self.get_str_attribute(::attrs::TITLE)
.expect("This widget should have a text pointer even if it's empty!")
}
}
impl_widget! { Label, "label" }
impl ::image::ImageContainer for Label {}
/// A widget that renders user-editable text.
pub struct TextBox(IUPPtr);
impl TextBox {
/// Create a new, empty text box.
pub fn new() -> TextBox {
unsafe {
let ptr = ::iup_sys::IupText(ptr::null());
Self::from_ptr(ptr)
}
}
/// Set if the text box should accept and render newline characters.
///
/// If `false`, it will only be slightly taller than a line of text in the current font.
/// If `true`, the total dimensions will be set by `set_visible_columns` and
/// `set_visible_lines`. Text outside these bounds will be accessible with a scrollbar.
pub fn set_multiline(self, multiline: bool) -> Self {
self.set_bool_attribute(::attrs::MULTILINE, multiline);
self
}
/// Set the rendered width of the textbox in columns (character width + padding).
///
/// If the textbox is set as multiline, this will cause additional text beyond the maximum
/// width to wrap. Otherwise, it can be scrolled only horizontally.
pub fn set_visible_columns(self, cols: u32) -> Self {
self.set_int_attribute(::attrs::VISIBLE_COLUMNS, cols as i32);
self
}
/// Set the rendered height of the textbox in lines (character height + padding).
///
/// If the textbox is set as multiline, newline characters will push text following them to the
/// next visible line. Line counts beyond these bounds will cause a scrollbar to be shown.
pub fn set_visible_lines(self, lines: u32) -> Self {
self.set_int_attribute(::attrs::VISIBLE_LINES, lines as i32);
self
}
/// Set the text of this textbox.
///
/// ##Panics
/// If any `WidgetStr` instances from `self.get_text()` are still reachable.
pub fn set_text(self, value: &str) -> Self {
self.set_str_attribute(::attrs::VALUE, value);
self
}
/// Get the text value of this textbox.
pub fn get_text(&self) -> WidgetStr {
self.get_str_attribute(::attrs::VALUE)
.expect("This string should be present even if it's empty!")
}
}
impl_widget! { TextBox, "text" }
impl_on_value_change! { TextBox }
================================================
FILE: src/timer.rs
================================================
//! Timers that can invoke a callback on an interval.
use widget_prelude::*;
use ::callback::Callback;
/// A timer that can invoke a callback on a configurable interval.
///
/// ##Note: Not a Renderable Widget
/// While this type can be dereferenced and converted to `BaseWidget`, it is *not* a renderable
/// widget and adding it to a container will have no visual effect.
///
/// ##Note: Resource Usage
/// This struct should be freed by calling `.destroy()` on it when it is no longer in use to free
/// any resources it has allocated. Otherwise, it will be freed when `kiss_ui::show_gui()` returns.
pub struct Timer(IUPPtr);
impl Timer {
/// Create a new timer with a default interval.
///
/// TODO: Document default interval.
pub fn new() -> Timer {
unsafe {
let ptr = ::iup_sys::IupTimer();
Self::from_ptr(ptr)
}
}
/// Set the timer interval in milliseconds.
pub fn set_interval(self, time: u32) -> Self {
self.set_int_attribute(::attrs::TIME, time as i32);
self
}
/// Set a callback to be invoked when the timer interval elapses.
/// The callback will be invoked on every interval until `.stop()` is called.
pub fn set_on_interval<Cb>(self, on_interval: Cb) -> Self where Cb: Callback<Self> {
callback_impl! { ::attrs::ACTION_CB, self, on_interval, Timer }
self
}
/// Start the timer. The callback will be invoked when the next interval elapses.
pub fn start(self) -> Self {
self.set_bool_attribute(::attrs::RUN, true);
self
}
/// Stop the timer. The callback will not be invoked until the timer is restarted.
pub fn stop(self) -> Self {
self.set_bool_attribute(::attrs::RUN, false);
self
}
}
impl_widget! { Timer, "timer" }
impl Destroy for Timer {}
================================================
FILE: src/utils/cstr.rs
================================================
pub trait AsCStr {
fn as_cstr(&self) -> *const ::libc::c_char;
}
impl AsCStr for &'static str {
fn as_cstr(&self) -> *const ::libc::c_char {
let bytes = self.as_bytes();
assert!(bytes[bytes.len() - 1] == 0u8);
bytes.as_ptr() as *const ::libc::c_char
}
}
macro_rules! cstr (
($val:expr) => (
concat!($val, "\0")
)
);
macro_rules! c_str_const (
($name:ident = $val:expr) => (
pub const $name: &'static str = cstr!($val);
)
);
macro_rules! c_str_consts {
($($name:ident = $val:expr),+,) => (
$(c_str_const!($name = $val);)+
)
}
================================================
FILE: src/utils/mod.rs
================================================
//! Utility types and functions that might be useful for KISS-UI users.
#[doc(hidden)]
#[macro_use]
pub mod cstr;
pub mod move_cell;
================================================
FILE: src/utils/move_cell.rs
================================================
//! A cell type that can move values into and out of a shared reference.
//!
//! Behaves like `RefCell<Option<T>>` but optimized for use-cases where temporary or permanent
//! ownership is required.
use std::cell::UnsafeCell;
use std::mem;
/// A cell type that can move values into and out of a shared reference.
pub struct MoveCell<T>(UnsafeCell<Option<T>>);
impl<T> MoveCell<T> {
/// Create a new `MoveCell` with no contained value.
pub fn new() -> MoveCell<T> {
MoveCell(UnsafeCell::new(None))
}
/// Create a new `MoveCell` with the given value.
pub fn with(val: T) -> MoveCell<T> {
MoveCell(UnsafeCell::new(Some(val)))
}
/// Create a new `MoveCell<T>` around the given `Option<T>`.
pub fn from(opt: Option<T>) -> MoveCell<T> {
MoveCell(UnsafeCell::new(opt))
}
unsafe fn as_mut(&self) -> &mut Option<T> {
&mut *self.0.get()
}
unsafe fn as_ref(&self) -> &Option<T> {
& *self.0.get()
}
/// Place a value into this `MoveCell`, returning the previous value, if present.
pub fn put(&self, val: T) -> Option<T> {
mem::replace(unsafe { self.as_mut() }, Some(val))
}
/// Take the value out of this `MoveCell`, leaving nothing in its place.
pub fn take(&self) -> Option<T> {
unsafe { self.as_mut().take() }
}
/// Take the value out of this `MoveCell`, leaving a clone in its place.
pub fn clone_inner(&self) -> Option<T> where T: Clone {
let inner = self.take();
inner.clone().map(|inner| self.put(inner));
inner
}
/// Check if this `MoveCell` contains a value or not.
pub fn has_value(&self) -> bool {
unsafe { self.as_ref().is_some() }
}
}
impl<T> Default for MoveCell<T> {
fn default() -> Self {
MoveCell::new()
}
}
================================================
FILE: src/widget.rs
================================================
//! Operations common to all widget types.
use utils::cstr::AsCStr;
use base::{BaseWidget, Downcast};
use dialog::Dialog;
use widget_prelude::IUPPtr;
use ::KISSContext;
use iup_sys;
use std::borrow::Borrow;
use std::cell::Cell;
use std::ffi::{CStr, CString};
use std::fmt::{self, Debug, Display, Formatter};
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::ptr;
use std::rc::Rc;
/// Trait implemented for all widget types.
///
/// Some methods may not apply to some widgets.
pub trait Widget: IUPWidget {
/// Show this widget if it was previously hidden.
///
/// Does nothing if the widget is already shown, or if the operation does not apply.
fn show(self) -> Self {
unsafe { iup_sys::IupShow(self.ptr()); }
self
}
/// Hide this widget if it was previously visible.
///
/// Does nothing if the widget is already hidden, or if the operation does not apply.
fn hide(self) -> Self {
unsafe { iup_sys::IupHide(self.ptr()); }
self
}
/// Set the widget's visibility state.
///
/// `.set_visible(true)` is equivalent to calling `.show()`, and `.set_visible(false)`
/// is equivalent to calling `.hide()`.
///
/// Does nothing if the widget is in the same visibility state as the one being set,
/// or if the operation does not apply.
fn set_visible(self, visible: bool) -> Self {
self.set_bool_attribute(::attrs::VISIBLE, visible);
self
}
/// Set the widget's enabled state.
///
/// When a widget is disabled, it does not react to user interaction or invoke any callbacks.
///
/// Does nothing if the widget does not support being disabled.
fn set_enabled(self, enabled: bool) -> Self {
self.set_bool_attribute(::attrs::ACTIVE, enabled);
self
}
/// Set the position of this widget relative to the top-left corner of its parent.
///
/// Does nothing if the widget is not renderable or not attached to a parent.
fn set_position(self, x: i32, y: i32) -> Self {
self.set_str_attribute(::attrs::POSITION, format!("{x},{y}", x=x, y=y));
self
}
/// Get the position of this widget relative to the top-left corner of its parent.
///
/// Returns (0, 0) if the widget is not renderable, not attached to a parent, or if that is the
/// widget's actual relative position.
fn get_position(self) -> (i32, i32) {
self.get_int2_attribute(::attrs::POSITION)
}
/// Set the name of the widget so it can be found within its parent.
///
/// Does nothing if the widget does not support having a name.
///
/// ##Panics
/// If any `WidgetStr` instances from `self.get_name()` are still reachable.
fn set_name(self, name: &str) -> Self {
self.set_str_attribute(::attrs::NAME, name);
self
}
/// Get the name of this widget, if the widget supports having a name and one is set.
fn get_name(&self) -> Option<WidgetStr> {
self.get_str_attribute(::attrs::NAME)
}
/// Get the next child in the parent after this widget, based on the order in which they were
/// added.
///
/// Returns `None` if this widget is an only child or is not attached to a parent.
fn get_sibling(self) -> Option<BaseWidget> {
unsafe {
let ptr = iup_sys::IupGetBrother(self.ptr());
BaseWidget::from_ptr_opt(ptr)
}
}
/// Get the parent of this widget.
///
/// Returns `None` if this widget has no parent.
fn get_parent(self) -> Option<BaseWidget> {
unsafe {
let ptr = iup_sys::IupGetParent(self.ptr());
BaseWidget::from_ptr_opt(ptr)
}
}
/// Get the containing dialog of this widget.
///
/// Returns `None` if this widget is not attached to a dialog.
fn get_dialog(self) -> Option<Dialog> {
unsafe {
let ptr = iup_sys::IupGetDialog(self.ptr());
// Note to self: not using UFCS because `downcast()` is an unsafe function.
BaseWidget::from_ptr_opt(ptr).map(|base| Dialog::downcast(base))
}
}
/// Get the rendered size of this widget, in pixels.
///
/// Returns `(0, 0)` if this widget has no rendered size.
fn get_size_pixels(self) -> (u32, u32) {
let (width, height) = self.get_int2_attribute(::attrs::RASTERSIZE);
(width as u32, height as u32)
}
/// Store this widget under `name`, returning the previous widget stored, if any.
///
/// It may later be retrieved from any valid KISS-UI context
/// by calling `BaseWidget::load(name)`.
fn store<N: Into<String>>(self, name: N) -> Option<BaseWidget> {
KISSContext::store_widget(name, self)
}
fn to_base(self) -> BaseWidget {
unsafe { BaseWidget::from_ptr(self.ptr()) }
}
}
pub trait Destroy: Widget {
fn destroy(self) {
unsafe {
iup_sys::IupDestroy(self.ptr());
}
}
}
/// A string slice borrowed from a widget's metadata. Can be dereferenced to `&str`.
///
/// Tracks ownership of the string so that its pointer cannot be invalidated. Releases ownership
/// on-drop.
pub struct WidgetStr<'a> {
refcount: Rc<Cell<usize>>,
data: &'a str,
}
impl<'a> WidgetStr<'a> {
pub fn new(ptr: *mut iup_sys::Ihandle, name: &'static str, str_data: &'a str) -> WidgetStr<'a> {
let widgetStr = WidgetStr {
refcount: KISSContext::str_refcount(ptr, name),
data: str_data,
};
widgetStr.inc_refcount();
widgetStr
}
fn inc_refcount(&self) {
let refcount = self.refcount.get();
self.refcount.set(refcount + 1);
}
}
impl<'a> Borrow<str> for WidgetStr<'a> {
fn borrow(&self) -> &str {
self.data
}
}
impl<'a> Debug for WidgetStr<'a> {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
<str as Debug>::fmt(self.data, fmt)
}
}
impl<'a> Display for WidgetStr<'a> {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
<str as Display>::fmt(self.data, fmt)
}
}
impl<'a> Hash for WidgetStr<'a> {
fn hash<H>(&self, hasher: &mut H) where H: Hasher {
self.data.hash(hasher)
}
}
impl<'a> Deref for WidgetStr<'a> {
type Target = str;
fn deref<'b>(&'b self) -> &'b str {
self.data
}
}
impl<'a> Clone for WidgetStr<'a> {
fn clone(&self) -> Self {
self.inc_refcount();
WidgetStr {
refcount: self.refcount.clone(),
data: self.data,
}
}
}
impl<'a> Drop for WidgetStr<'a> {
fn drop(&mut self) {
let refcount = self.refcount.get();
self.refcount.set(refcount - 1);
}
}
#[doc(hidden)]
pub trait IUPWidget: Copy {
unsafe fn from_ptr(ptr: IUPPtr) -> Self;
unsafe fn from_ptr_opt(ptr: IUPPtr) -> Option<Self> {
if !ptr.is_null() {
Some(Self::from_ptr(ptr))
} else {
None
}
}
fn ptr(self) -> IUPPtr;
fn classname(&self) -> &CStr {
unsafe { CStr::from_ptr(iup_sys::IupGetClassName(self.ptr())) }
}
fn set_str_attribute<V>(self, name: &'static str, val: V) where V: Into<String> {
KISSContext::assert_str_not_borrowed(self.ptr(), name);
let c_val = CString::new(val.into()).unwrap();
unsafe { iup_sys::IupSetStrAttribute(self.ptr(), name.as_cstr(), c_val.as_ptr()); }
}
fn set_opt_str_attribute<V>(self, name: &'static str, val: Option<V>) where V: Into<String> {
KISSContext::assert_str_not_borrowed(self.ptr(), name);
let c_val = val.map(V::into).map(CString::new).map(Result::unwrap);
unsafe {
iup_sys::IupSetStrAttribute(
self.ptr(),
name.as_cstr(),
// This looks backwards, but check the docs. It's right.
c_val.as_ref().map_or_else(ptr::null, |c_val| c_val.as_ptr())
)
}
}
fn set_const_str_attribute(self, name: &'static str, val: &'static str) {
KISSContext::assert_str_not_borrowed(self.ptr(), name);
unsafe { iup_sys::IupSetAttribute(self.ptr(), name.as_cstr(), val.as_cstr()); }
}
fn get_str_attribute(&self, name: &'static str) -> Option<WidgetStr> {
let ptr = unsafe { iup_sys::IupGetAttribute(self.ptr(), name.as_cstr()) };
if !ptr.is_null() {
unsafe {
// Safe since we're controlling the lifetime
let c_str = CStr::from_ptr(ptr);
// We're forcing IUP to use UTF-8
let str_data = ::std::str::from_utf8_unchecked(c_str.to_bytes());
Some(WidgetStr::new(self.ptr(), name, str_data))
}
} else {
None
}
}
fn set_int_attribute(self, name: &'static str, val: i32) {
unsafe { iup_sys::IupSetInt(self.ptr(), name.as_cstr(), val); }
}
fn get_int_attribute(self, name: &'static str) -> i32 {
unsafe { iup_sys::IupGetInt(self.ptr(), name.as_cstr()) }
}
fn get_int2_attribute(self, name: &'static str) -> (i32, i32) {
let mut left = 0;
let mut right = 0;
unsafe {
assert!(iup_sys::IupGetIntInt(self.ptr(), name.as_cstr(), &mut left, &mut right) != 0);
}
(left, right)
}
fn set_float_attribute(self, name: &'static str, val: f32) {
unsafe { iup_sys::IupSetFloat(self.ptr(), name.as_cstr(), val); }
}
fn get_float_attribute(self, name: &'static str) -> f32 {
unsafe { iup_sys::IupGetFloat(self.ptr(), name.as_cstr()) }
}
fn set_bool_attribute(self, name: &'static str, val: bool) {
let val = ::attrs::values::bool_yes_no(val);
self.set_const_str_attribute(name, val);
}
fn set_attr_handle<W: Widget>(self, name: &'static str, handle: W) {
unsafe { iup_sys::IupSetAttributeHandle(self.ptr(), name.as_cstr(), handle.ptr()); }
}
fn get_attr_handle(self, name: &'static str) -> Option<BaseWidget> {
unsafe {
let existing = iup_sys::IupGetAttributeHandle(self.ptr(), name.as_cstr());
BaseWidget::from_ptr_opt(existing)
}
}
fn set_callback(self, name: &'static str, callback: ::iup_sys::Icallback) {
unsafe { iup_sys::IupSetCallback(self.ptr(), name.as_cstr(), callback); }
}
}
impl<'a, T: IUPWidget> IUPWidget for &'a T {
unsafe fn from_ptr(_ptr: *mut iup_sys::Ihandle) -> Self {
panic!("Cannot construct an &mut Self from a pointer");
}
fn ptr(self) -> *mut iup_sys::Ihandle {
(*self).ptr()
}
}
macro_rules! impl_widget {
($ty:ident, [$($classname:expr),+]) => {
impl_widget!($ty);
impl ::base::Downcast for $ty {
fn can_downcast(base: &::base::BaseWidget) -> bool {
[$($classname.as_bytes()),+].contains(&base.classname().to_bytes())
}
}
};
($ty:ident, $classname:expr) => {
impl_widget!($ty);
impl ::base::Downcast for $ty {
fn can_downcast(base: &::base::BaseWidget) -> bool {
$classname.as_bytes() == base.classname().to_bytes()
}
}
};
($ty:ident) => {
impl ::widget::IUPWidget for $ty {
unsafe fn from_ptr(ptr: ::widget_prelude::IUPPtr) -> Self {
assert!(
!ptr.is_null(),
concat!(
concat!("Failed to construct ", stringify!($ty)),
"; pointer returned from IUP was null!"
)
);
$ty(ptr)
}
fn ptr(self) -> ::widget_prelude::IUPPtr {
self.0
}
}
impl ::widget::Widget for $ty {}
impl Copy for $ty {}
impl Clone for $ty {
fn clone(&self) -> Self {
*self
}
}
}
}
gitextract_5c7epxqh/
├── .gitignore
├── .travis.yml
├── Cargo.toml
├── LICENSE
├── README.md
├── SCREENSHOTS.md
├── build_docs.sh
├── examples/
│ ├── button_test.rs
│ ├── progress_test.rs
│ ├── show_image.rs
│ ├── textbox_test.rs
│ └── window_test.rs
├── kiss-app.manifest
└── src/
├── attrs.rs
├── base.rs
├── button.rs
├── callback.rs
├── container.rs
├── dialog.rs
├── image.rs
├── lib.rs
├── progress.rs
├── text.rs
├── timer.rs
├── utils/
│ ├── cstr.rs
│ ├── mod.rs
│ └── move_cell.rs
└── widget.rs
SYMBOL INDEX (181 symbols across 19 files)
FILE: examples/button_test.rs
function main (line 10) | fn main() {
function show_message_dialog (line 48) | fn show_message_dialog(btn: Button) {
function show_change_name (line 53) | fn show_change_name(btn: Button) {
function show_alert_dialog (line 63) | fn show_alert_dialog(_: Button) {
function close_dialog (line 72) | fn close_dialog(_: Button) -> CallbackStatus {
FILE: examples/progress_test.rs
function main (line 11) | fn main() {
FILE: examples/show_image.rs
function main (line 9) | fn main() {
function color (line 31) | fn color(x: u32, y: u32) -> (u8, u8, u8) {
FILE: examples/textbox_test.rs
function main (line 11) | fn main() {
function show_alert_message (line 30) | fn show_alert_message(clicked: Button) {
FILE: examples/window_test.rs
function main (line 8) | fn main() {
FILE: src/attrs.rs
function bool_yes_no (line 60) | pub fn bool_yes_no(_bool: bool) -> &'static str {
FILE: src/base.rs
type BaseWidget (line 10) | pub struct BaseWidget(IUPPtr);
method load (line 21) | pub fn load<N: Borrow<str>>(name: N) -> Option<BaseWidget> {
method try_downcast (line 29) | pub fn try_downcast<T>(self) -> Result<T, Self> where T: Downcast {
type Downcast (line 37) | pub trait Downcast: Widget {
method try_downcast (line 40) | fn try_downcast(base: BaseWidget) -> Result<Self, BaseWidget> {
method downcast (line 51) | unsafe fn downcast(base: BaseWidget) -> Self {
method can_downcast (line 56) | fn can_downcast(base: &BaseWidget) -> bool;
FILE: src/button.rs
type Button (line 8) | pub struct Button(IUPPtr);
method new (line 12) | pub fn new() -> Button {
method set_label (line 20) | pub fn set_label<L: Into<String>>(self, label: L) -> Self {
FILE: src/callback.rs
type CallbackStatus (line 15) | pub enum CallbackStatus {
method close (line 26) | pub fn close(&mut self) {
method to_cb_return (line 31) | pub fn to_cb_return(self) -> ::libc::c_int {
method from (line 43) | fn from(_: ()) -> CallbackStatus {
type Callback (line 48) | pub trait Callback<Args>: 'static {
method on_callback (line 49) | fn on_callback(&mut self, args: Args) -> CallbackStatus;
method on_callback (line 55) | fn on_callback(&mut self, args: Args) -> CallbackStatus {
type CallbackMap (line 61) | pub type CallbackMap<T> = RefCell<HashMap<*mut Ihandle, Box<Callback<T>>>>;
type OnClick (line 93) | pub trait OnClick: Widget {
method set_onclick (line 94) | fn set_onclick<Cb>(self, on_click: Cb) -> Self where Cb: Callback<Self>;
type OnValueChange (line 110) | pub trait OnValueChange: Widget {
method set_on_value_changed (line 111) | fn set_on_value_changed<Cb>(self, on_value_chaged: Cb) -> Self where C...
type OnShow (line 126) | pub trait OnShow: Widget {
method set_on_show (line 127) | fn set_on_show<Cb>(self, on_show: Cb) -> Self where Cb: Callback<Self>;
FILE: src/container.rs
type VAlign (line 13) | pub enum VAlign {
method as_cstr (line 20) | fn as_cstr(self) -> &'static str {
type HAlign (line 33) | pub enum HAlign {
method as_cstr (line 40) | fn as_cstr(self) -> &'static str {
type Orientation (line 53) | pub enum Orientation {
method as_cstr (line 60) | pub fn as_cstr(self) -> &'static str {
function raw_handle_vec (line 71) | fn raw_handle_vec<B>(widgets: B) -> Vec<IUPPtr> where B: AsRef<[BaseWidg...
type AbsoluteBuilder (line 78) | pub struct AbsoluteBuilder {
method new (line 83) | fn new() -> AbsoluteBuilder {
method add_child_at (line 89) | pub fn add_child_at<W: Widget>(&mut self, x: u32, y: u32, child: W) ->...
type Absolute (line 99) | pub struct Absolute(IUPPtr);
method new (line 108) | pub fn new<F>(build_fn: F) -> Absolute where F: FnOnce(&mut AbsoluteBu...
method set_child_pos (line 119) | pub fn set_child_pos<W: Widget>(x: u32, y: u32, child: W) {
type Horizontal (line 128) | pub struct Horizontal(IUPPtr);
method new (line 135) | pub fn new<C>(children: C) -> Horizontal where C: AsRef<[BaseWidget]> {
method set_valign (line 144) | pub fn set_valign(self, valign: VAlign) -> Self {
method set_elem_spacing_pixels (line 149) | pub fn set_elem_spacing_pixels(self, spacing: u32) -> Self {
type Vertical (line 158) | pub struct Vertical(IUPPtr);
method new (line 161) | pub fn new<C>(children: C) -> Vertical where C: AsRef<[BaseWidget]> {
method set_halign (line 170) | pub fn set_halign(self, halign: HAlign) -> Self {
method set_elem_spacing_pixels (line 175) | pub fn set_elem_spacing_pixels(self, spacing: u32) -> Self {
type Grid (line 185) | pub struct Grid(IUPPtr);
method new (line 188) | pub fn new<C>(children: C) -> Grid where C: AsRef<[BaseWidget]> {
method set_valign (line 196) | pub fn set_valign(self, valign: VAlign) -> Self {
method set_halign (line 201) | pub fn set_halign(self, halign: HAlign) -> Self {
method set_ndiv (line 212) | pub fn set_ndiv(self, ndiv: u32) -> Self {
method set_orientation (line 257) | pub fn set_orientation(&mut self, orientation: Orientation) -> &mut Se...
FILE: src/dialog.rs
type Dialog (line 13) | pub struct Dialog(IUPPtr);
method new (line 26) | pub fn new<W>(contents: W) -> Dialog where W: Widget {
method empty (line 39) | pub fn empty() -> Dialog {
method set_title (line 49) | pub fn set_title<T: Into<String>>(self, title: T) -> Self {
method set_size_pixels (line 55) | pub fn set_size_pixels(self, width: u32, height: u32) -> Self {
method get_child (line 64) | pub fn get_child(self, name: &str) -> Option<BaseWidget> {
function message_popup (line 82) | pub fn message_popup<T: Into<String>, M: Into<String>>(title: T, message...
type AlertPopupBuilder (line 95) | pub struct AlertPopupBuilder {
method new (line 104) | pub fn new<T: Into<String>, M: Into<String>, B1: Into<String>>(
method button2 (line 117) | pub fn button2<B2: Into<String>>(mut self, button2: B2) -> Self {
method button3 (line 122) | pub fn button3<B3: Into<String>>(mut self, button3: B3) -> Self {
method popup (line 130) | pub fn popup(self) -> i32 {
FILE: src/image.rs
type Image (line 25) | pub struct Image(IUPPtr);
method new_rgb (line 35) | pub fn new_rgb(width: u32, height: u32, pixels: &[(u8, u8, u8)]) -> Im...
method new_rgba (line 50) | pub fn new_rgba(width: u32, height: u32, pixels: &[(u8, u8, u8, u8)]) ...
function transmute_buffer_rgb (line 66) | pub fn transmute_buffer_rgb(buf: &[u8]) -> Option<&[(u8, u8, u8)]> {
function transmute_buffer_rgba (line 77) | pub fn transmute_buffer_rgba(buf: &[u8]) -> Option<&[(u8, u8, u8, u8)]> {
type ImageContainer (line 86) | pub trait ImageContainer: Widget {
method set_image (line 88) | fn set_image(self, image: Image) -> Self {
method get_image (line 94) | fn get_image(&self) -> Option<Image> {
FILE: src/lib.rs
type IUPPtr (line 80) | pub type IUPPtr = *mut ::iup_sys::Ihandle;
type KISSContext (line 98) | struct KISSContext {
method assert_str_not_borrowed (line 105) | fn assert_str_not_borrowed(widget: IUPPtr, str_: &'static str) {
method str_refcount (line 124) | fn str_refcount(widget: IUPPtr, str_: &'static str) -> Rc<Cell<usize>> {
method store_widget (line 135) | fn store_widget<N: Into<String>, W: Widget>(name: N, widget: W) -> Opt...
method load_widget (line 142) | fn load_widget<N: Borrow<str>>(name: &N) -> Option<BaseWidget> {
method clear (line 148) | unsafe fn clear() {
function show_gui (line 182) | pub fn show_gui<F>(init_fn: F) where F: FnOnce() -> Dialog + Send {
FILE: src/progress.rs
type ProgressBar (line 13) | pub struct ProgressBar(IUPPtr);
method new (line 17) | pub fn new() -> ProgressBar {
method set_indefinite (line 30) | pub fn set_indefinite(self, is_indefinite: bool) -> Self {
method set_dashed (line 38) | pub fn set_dashed(self, dashed: bool) -> Self {
method set_max (line 46) | pub fn set_max(self, max: f32) -> Self {
method set_min (line 54) | pub fn set_min(self, min: f32) -> Self {
method set_orientation (line 64) | pub fn set_orientation(self, orientation: Orientation) -> Self {
method set_value (line 71) | pub fn set_value(self, val: f32) -> Self {
method get_value (line 77) | pub fn get_value(self) -> f32 {
method add_value (line 82) | pub fn add_value(self, amt: f32) -> Self {
FILE: src/text.rs
type Label (line 9) | pub struct Label(IUPPtr);
method new (line 13) | pub fn new<S: Into<String>>(text: S) -> Label {
method new_empty (line 22) | pub fn new_empty() -> Label {
method set_text (line 33) | pub fn set_text(self, text: &str) -> Self {
method get_text (line 39) | pub fn get_text(&self) -> WidgetStr {
type TextBox (line 50) | pub struct TextBox(IUPPtr);
method new (line 54) | pub fn new() -> TextBox {
method set_multiline (line 66) | pub fn set_multiline(self, multiline: bool) -> Self {
method set_visible_columns (line 75) | pub fn set_visible_columns(self, cols: u32) -> Self {
method set_visible_lines (line 84) | pub fn set_visible_lines(self, lines: u32) -> Self {
method set_text (line 93) | pub fn set_text(self, value: &str) -> Self {
method get_text (line 99) | pub fn get_text(&self) -> WidgetStr {
FILE: src/timer.rs
type Timer (line 14) | pub struct Timer(IUPPtr);
method new (line 20) | pub fn new() -> Timer {
method set_interval (line 28) | pub fn set_interval(self, time: u32) -> Self {
method set_on_interval (line 35) | pub fn set_on_interval<Cb>(self, on_interval: Cb) -> Self where Cb: Ca...
method start (line 41) | pub fn start(self) -> Self {
method stop (line 47) | pub fn stop(self) -> Self {
FILE: src/utils/cstr.rs
type AsCStr (line 1) | pub trait AsCStr {
method as_cstr (line 2) | fn as_cstr(&self) -> *const ::libc::c_char;
method as_cstr (line 6) | fn as_cstr(&self) -> *const ::libc::c_char {
FILE: src/utils/move_cell.rs
type MoveCell (line 10) | pub struct MoveCell<T>(UnsafeCell<Option<T>>);
function new (line 14) | pub fn new() -> MoveCell<T> {
function with (line 19) | pub fn with(val: T) -> MoveCell<T> {
function from (line 24) | pub fn from(opt: Option<T>) -> MoveCell<T> {
function as_mut (line 28) | unsafe fn as_mut(&self) -> &mut Option<T> {
function as_ref (line 32) | unsafe fn as_ref(&self) -> &Option<T> {
function put (line 37) | pub fn put(&self, val: T) -> Option<T> {
function take (line 42) | pub fn take(&self) -> Option<T> {
function clone_inner (line 47) | pub fn clone_inner(&self) -> Option<T> where T: Clone {
function has_value (line 54) | pub fn has_value(&self) -> bool {
method default (line 60) | fn default() -> Self {
FILE: src/widget.rs
type Widget (line 25) | pub trait Widget: IUPWidget {
method show (line 29) | fn show(self) -> Self {
method hide (line 37) | fn hide(self) -> Self {
method set_visible (line 49) | fn set_visible(self, visible: bool) -> Self {
method set_enabled (line 59) | fn set_enabled(self, enabled: bool) -> Self {
method set_position (line 67) | fn set_position(self, x: i32, y: i32) -> Self {
method get_position (line 76) | fn get_position(self) -> (i32, i32) {
method set_name (line 86) | fn set_name(self, name: &str) -> Self {
method get_name (line 92) | fn get_name(&self) -> Option<WidgetStr> {
method get_sibling (line 100) | fn get_sibling(self) -> Option<BaseWidget> {
method get_parent (line 110) | fn get_parent(self) -> Option<BaseWidget> {
method get_dialog (line 120) | fn get_dialog(self) -> Option<Dialog> {
method get_size_pixels (line 131) | fn get_size_pixels(self) -> (u32, u32) {
method store (line 140) | fn store<N: Into<String>>(self, name: N) -> Option<BaseWidget> {
method to_base (line 144) | fn to_base(self) -> BaseWidget {
type Destroy (line 149) | pub trait Destroy: Widget {
method destroy (line 150) | fn destroy(self) {
type WidgetStr (line 161) | pub struct WidgetStr<'a> {
function new (line 167) | pub fn new(ptr: *mut iup_sys::Ihandle, name: &'static str, str_data: &'a...
function inc_refcount (line 176) | fn inc_refcount(&self) {
function borrow (line 183) | fn borrow(&self) -> &str {
method fmt (line 189) | fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
method fmt (line 195) | fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
method hash (line 201) | fn hash<H>(&self, hasher: &mut H) where H: Hasher {
type Target (line 207) | type Target = str;
method deref (line 209) | fn deref<'b>(&'b self) -> &'b str {
method clone (line 215) | fn clone(&self) -> Self {
method drop (line 226) | fn drop(&mut self) {
type IUPWidget (line 235) | pub trait IUPWidget: Copy {
method from_ptr (line 236) | unsafe fn from_ptr(ptr: IUPPtr) -> Self;
method from_ptr_opt (line 238) | unsafe fn from_ptr_opt(ptr: IUPPtr) -> Option<Self> {
method ptr (line 246) | fn ptr(self) -> IUPPtr;
method classname (line 248) | fn classname(&self) -> &CStr {
method set_str_attribute (line 252) | fn set_str_attribute<V>(self, name: &'static str, val: V) where V: Int...
method set_opt_str_attribute (line 259) | fn set_opt_str_attribute<V>(self, name: &'static str, val: Option<V>) ...
method set_const_str_attribute (line 273) | fn set_const_str_attribute(self, name: &'static str, val: &'static str) {
method get_str_attribute (line 279) | fn get_str_attribute(&self, name: &'static str) -> Option<WidgetStr> {
method set_int_attribute (line 296) | fn set_int_attribute(self, name: &'static str, val: i32) {
method get_int_attribute (line 300) | fn get_int_attribute(self, name: &'static str) -> i32 {
method get_int2_attribute (line 304) | fn get_int2_attribute(self, name: &'static str) -> (i32, i32) {
method set_float_attribute (line 315) | fn set_float_attribute(self, name: &'static str, val: f32) {
method get_float_attribute (line 319) | fn get_float_attribute(self, name: &'static str) -> f32 {
method set_bool_attribute (line 323) | fn set_bool_attribute(self, name: &'static str, val: bool) {
method set_attr_handle (line 328) | fn set_attr_handle<W: Widget>(self, name: &'static str, handle: W) {
method get_attr_handle (line 332) | fn get_attr_handle(self, name: &'static str) -> Option<BaseWidget> {
method set_callback (line 339) | fn set_callback(self, name: &'static str, callback: ::iup_sys::Icallba...
method from_ptr (line 345) | unsafe fn from_ptr(_ptr: *mut iup_sys::Ihandle) -> Self {
method ptr (line 349) | fn ptr(self) -> *mut iup_sys::Ihandle {
Condensed preview — 28 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (77K chars).
[
{
"path": ".gitignore",
"chars": 28,
"preview": "target\n\nCargo.lock\n\n*~\n*.swp"
},
{
"path": ".travis.yml",
"chars": 534,
"preview": "env:\n global:\n - IUP_DL='http://sourceforge.net/projects/iup/files/3.14/Linux%20Libraries/iup-3.14_Linux32_64_lib.ta"
},
{
"path": "Cargo.toml",
"chars": 144,
"preview": "[package]\nname = \"kiss-ui\"\nversion = \"0.1.0\"\nauthors = [\"Austin Bonander <austin.bonander@gmail.com>\"]\n\n[dependencies]\nl"
},
{
"path": "LICENSE",
"chars": 1083,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2015 Austin Bonander\n\nPermission is hereby granted, free of charge, to any person o"
},
{
"path": "README.md",
"chars": 8957,
"preview": "KISS-UI [](https://travis-ci.org/cybergeek94"
},
{
"path": "SCREENSHOTS.md",
"chars": 2137,
"preview": "KISS-UI Screenshots\n-------------------\n\nBelow are screenshots of the KISS-UI examples, on Windows 7 with Visual Styles "
},
{
"path": "build_docs.sh",
"chars": 64,
"preview": "git merge master\ncargo doc\nrm -rf doc/\ncp -avr target/doc/ doc/\n"
},
{
"path": "examples/button_test.rs",
"chars": 2335,
"preview": "#[macro_use]\nextern crate kiss_ui;\n\nuse kiss_ui::prelude::*;\n\nuse kiss_ui::button::Button;\nuse kiss_ui::container::{Hori"
},
{
"path": "examples/progress_test.rs",
"chars": 1349,
"preview": "#[macro_use]\nextern crate kiss_ui;\n\nuse kiss_ui::callback::OnShow;\nuse kiss_ui::container::Vertical;\nuse kiss_ui::dialog"
},
{
"path": "examples/show_image.rs",
"chars": 1162,
"preview": "#[macro_use]\nextern crate kiss_ui;\n\nuse kiss_ui::container::Horizontal;\nuse kiss_ui::dialog::Dialog;\nuse kiss_ui::image:"
},
{
"path": "examples/textbox_test.rs",
"chars": 1060,
"preview": "#[macro_use]\nextern crate kiss_ui;\n\nuse kiss_ui::prelude::*;\n\nuse kiss_ui::button::Button;\nuse kiss_ui::container::Verti"
},
{
"path": "examples/window_test.rs",
"chars": 416,
"preview": "#[macro_use]\nextern crate kiss_ui;\n\nuse kiss_ui::container::Horizontal;\nuse kiss_ui::dialog::Dialog;\nuse kiss_ui::text::"
},
{
"path": "kiss-app.manifest",
"chars": 640,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersi"
},
{
"path": "src/attrs.rs",
"chars": 1279,
"preview": "c_str_consts! {\n //Globals\n UTF8_MODE = \"UTF8MODE\",\n\n // Basic widget attributes\n TITLE = \"TITLE\",\n VALUE"
},
{
"path": "src/base.rs",
"chars": 1779,
"preview": "//! A general widget type that can be specialized at runtime.\n\nuse widget_prelude::*;\n\nuse ::KISSContext;\n\nuse std::borr"
},
{
"path": "src/button.rs",
"chars": 724,
"preview": "//! Buttons that can receive user input.\n\nuse widget_prelude::*;\n\nuse std::ptr;\n\n/// A button that can be clicked moment"
},
{
"path": "src/callback.rs",
"chars": 4563,
"preview": "//! Traits for notifying client code when the state of a KISS-UI widget is updated.\n\nuse widget_prelude::*;\n\nuse iup_sys"
},
{
"path": "src/container.rs",
"chars": 7811,
"preview": "//! Assorted types that can contain multiple widgets.\n//!\n//! All container types can be nested.\n//!\n//! Use the `childr"
},
{
"path": "src/dialog.rs",
"chars": 4433,
"preview": "//! KISS-UI top-level dialogs (windows)\n\nuse base::BaseWidget;\nuse widget_prelude::*;\n\nuse ::iup_sys;\n\nuse std::ffi::CSt"
},
{
"path": "src/image.rs",
"chars": 3356,
"preview": "//! Renderable image buffers.\n\nuse widget_prelude::*;\n\nuse base::Downcast;\n\nuse std::mem;\n\n/// An image buffer allocated"
},
{
"path": "src/lib.rs",
"chars": 6627,
"preview": "//! A UI framework for Rust based on the KISS principle: \"Keep It Simple, Stupid!\"\n//!\n//! Built on top of the [IUP GUI "
},
{
"path": "src/progress.rs",
"chars": 2986,
"preview": "//! Progress bars and dialogs.\n\nuse widget_prelude::*;\n\nuse container::Orientation;\n\n/// A widget that renders a bar whi"
},
{
"path": "src/text.rs",
"chars": 3454,
"preview": "//! Widgets that can render and process text (labels, text boxes).\n\nuse widget_prelude::*;\n\nuse std::ffi::CString;\nuse s"
},
{
"path": "src/timer.rs",
"chars": 1845,
"preview": "//! Timers that can invoke a callback on an interval. \nuse widget_prelude::*;\nuse ::callback::Callback;\n\n/// A timer th"
},
{
"path": "src/utils/cstr.rs",
"chars": 613,
"preview": "pub trait AsCStr {\n fn as_cstr(&self) -> *const ::libc::c_char;\n}\n\nimpl AsCStr for &'static str {\n fn as_cstr(&sel"
},
{
"path": "src/utils/mod.rs",
"chars": 134,
"preview": "//! Utility types and functions that might be useful for KISS-UI users.\n\n#[doc(hidden)]\n#[macro_use]\npub mod cstr;\npub m"
},
{
"path": "src/utils/move_cell.rs",
"chars": 1838,
"preview": "//! A cell type that can move values into and out of a shared reference.\n//!\n//! Behaves like `RefCell<Option<T>>` but o"
},
{
"path": "src/widget.rs",
"chars": 12085,
"preview": "//! Operations common to all widget types.\n\nuse utils::cstr::AsCStr;\n\nuse base::{BaseWidget, Downcast};\nuse dialog::Dial"
}
]
About this extraction
This page contains the full source code of the cybergeek94/kiss-ui GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 28 files (71.7 KB), approximately 19.0k tokens, and a symbol index with 181 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.