[−][src]Crate pugl_ui
pugl
is a minimal portable API
for embeddable GUIs. This crate aims to provide a stub for
GUI-toolkits using pugl
pugl-ui
(this crate) features
It does not feature actual widgets, though.
API principles
pugl-ui
's API differs from classical object oriented approaches
of GUI programming. This is due to Rust's safe ownership concepts
which disallows shared mutable references to objects.
For example if a click on a button was to change the state of something in the app, usually the button would retain a reference or a callback to this "something". When the button is clicked it can use this reference to perform the state change.
In Rust that's not possible as the consequence of the button retaining a mutable reference to the state would be that no other reference – not even a readable one – could coexist in the application.
The players: Widgets, the UI, the Application
pugl-ui
has in principle three players.
-
The widgets: they receive event notifications and can then change their internal state. Widgets must implement
Widget
-
The UI: an instance of
UI
The UI is the interface between the application, the windowing system and the widgets. It receives event notifications from the windowing system and passes them to the widgets. Then the application can borrow references to individual widgets to check if the application's state needs to be changed.
-
The application holds a reference to the UI and implements the event loop. There is no trait nor struct for it in
pugl-ui
. Typically its a function that initializes the UI and then has an event loop that asks theUI
to propagate events from the windowing system by callingUI::next_event()
and then checks the widgets if any application state change is required, for example when a button has been clicked. So it is the application that holds all the application logic. The application can also borrow mutable references to widgets, for example to change their state.
Widget handles
The application does not retain references to the widget. It is
the UI
that has them. The application retains only
WidgetHandle
objects. The
WidgetHandle
object are created by
UI::new_widget()
and can
later be accessed by
UI::widget()
.
Example
use pugl_sys::*; use pugl_ui::ui::*; use pugl_ui::layout::stacklayout::*; use pugl_ui::widget::*; use pugl_ui::*; use cairo; // A simple root widget, that does only draw a gray background. #[derive(Default)] struct RootWidget { stub: WidgetStub, } impl Widget for RootWidget { widget_stub!(); fn exposed (&mut self, _expose: &ExposeArea, cr: &cairo::Context) { cr.set_source_rgb(0.2, 0.2, 0.2); let size = self.size(); cr.rectangle(0., 0., size.w, size.h); cr.fill(); } } const BUTTON_TEXT: &'static str = "Click me"; // A simple button that knows when it has been clicked #[derive(Default)] struct Button { stub: WidgetStub, clicked: bool, } impl Button { // by this method the application can check if the button has been clicked fn has_been_clicked(&mut self) -> bool { let clicked = self.clicked; self.clicked = false; clicked } } impl Widget for Button { widget_stub!(); // rendering the button fn exposed(&mut self, _expose: &ExposeArea, cr: &cairo::Context) { cr.set_source_rgb(0.7, 0.7, 0.7); let (x, y, w, h) = self.rect(); cr.rectangle(x, y, w, h); cr.fill(); cr.set_source_rgb(0., 0., 0.); cr.move_to(x+w/3., y+2.*h/3.); cr.select_font_face("Sans", cairo::FontSlant::Normal, cairo::FontWeight::Normal); cr.set_font_size(60.0); cr.show_text(BUTTON_TEXT); cr.fill(); } // processing the event fn event(&mut self, ev: Event) -> Option<Event> { match ev.data { EventType::MouseButtonRelease(_) => { self.clicked = true; event_processed!() } _ => event_not_processed!() }.and_then(|p| p.pass_event(ev)) } // signaling the minimal size of the button fn min_size(&self) -> Size { Size { w: 600., h: 100. } } } // The application function fn app_execute() { // Initializing the UI and the interface to the windowing system let rw = Box::new(RootWidget::default()); let mut view = PuglView::new(std::ptr::null_mut(), |pv| UI::new(pv, rw)); let ui = view.handle(); // creating the button let button = ui.new_widget(Box::new(Button::default())); // widget layouting ui.pack_to_layout(button, ui.root_layout(), StackDirection::Back); ui.do_layout(); // showing the window ui.fit_window_size(); ui.show_window(); // event loop while !ui.close_request_issued() { ui.next_event(-1.0); // minimalist application logic // // We borrow the `button` widget from the `ui` and check if it has been clicked. if ui.widget(button).has_been_clicked() { println!("Button has been clicked."); } } }
Modules
layout | Facilities for widget layouting |
ui | The |
widget | Everything to describe an access a widget |
Macros
event_not_processed | |
event_processed | |
widget_stub | Implements |