pub struct Ui<T: Renderer> { /* private fields */ }
Expand description
UI state. This is what’s used for laying out groups and drawing to the screen.
The group stack
The group stack is what controls positioning at a given moment. Because paws is an immediate-mode GUI library, there is no intermediate state with nodes and what not. Everything is rendered to the screen directly.
However, during rendering, paws has to keep some information around for layouting. After all, that’s what it’s for: laying out rectangles on the screen. The way it maintains this state is through a stack of groups.
Groups are essentially just glorified rectangles with some extra layouting and rendering data. When you push a new group, the its position is placed relative to the previous group, at a vector called the cursor. When you pop this new group off the stack, the cursor of the group above is offset by the size of the group you just popped off. This allows you to cascade elements, flexbox-style, just without the overhead of holding a hundred or so nodes scattered across heap memory. The group stack grows as you nest more and more groups inside of each other, but old groups are not preserved after they’re popped off the stack.
Of course this paradigm makes laying out simple panels quite easy, but elements that need to preserve state may be a bit more challenging to implement. But worry not, structs come to your rescue! If you want to implement a more complex element that needs to preserve some state across frames, create a struct that’ll hold its data:
// let's assume this is our UI type:
use paws::NoRenderer;
type Ui = paws::Ui<NoRenderer>;
struct Slider {
value: f32,
min: f32,
max: f32,
}
Then, implement a method on this struct that’ll render the element onto the screen, and process all incoming input events:
use paws::Layout;
impl Slider {
// for brevity, we'll assume no input events need to be processed, as that's outside
// of paws's scope.
fn process(&mut self, ui: &mut Ui, width: f32) {
// create a group that'll span a rectangle with the provided width and the parent
// group's height
ui.push((width, ui.height()), Layout::Freeform);
ui.draw(|ui| {
// ... do all the rendering work here ...
});
ui.pop();
}
}
Then, simply keep a Slider somewhere outside of your event loop, and call process()
when you want to place it
onto your UI.
let mut ui = Ui::new(NoRenderer);
let mut slider = Slider {
value: 0.0,
min: 0.0,
max: 32.0,
};
// ↓ this is your event loop! usually done by winit, SDL2, or some other library.
'app: loop {
// don't forget the root group!
ui.root(window_size, Layout::Vertical);
// ... other layout stuff goes here ...
// when time comes, process() the slider:
slider.process(&mut ui, 256.0);
}
Initialization
Most of the group-related methods described below will panic if there are no groups on the stack, with the exception
of Ui::new
(obviously), and Ui::root
. The latter is used to initialize the UI state so that at least one
group is present, but also to make sure that the group stack doesn’t grow to oblivion if the user forgets to pop a
group or two. (This isn’t a good excuse for not popping groups, but it’ll at least prevent your program from leaking
memory if you really do forget.)
Panicking
All functions that mention the “current group” are guaranteed to panic if there are no groups on the stack. Other groups, such as the “parent group”, which is the group above the current group, may also appear, but are always followed up with a Panicking section that specify the requirements for these groups’ presence.
Rendering
Because there’s usually no use to using a UI library without any actual rendering, the Ui
type takes an extra
renderer type as a parameter. The methods from this type are available via the Deref
and DerefMut
traits,
except for names that collide with those defined on Ui
itself (obviously).
The renderer can be retrieved as an immutable reference (for probing and measurements) using Ui::renderer
,
and as a mutable reference (for doing actual rendering) using Ui::render
.
build!
For your convenience while building UIs, a macro is available to make all those push
es and pop
s get out of your
face. See build!
’s documentation for more info.
Implementations
Returns the position of the topmost group, in absolute (screen) coordinates.
Sets the absolute position of the topmost group.
Returns the current group’s rectangle. The rectangle’s position is expressed in absolute coordinates.
Returns the “remaining size” of the current group. This is measured by subtracting the group’s cursor from its size, effectively giving you the size that remains in the group. In reversed layouts, the cursor is added instead, as it goes into the negative. On the freeform layout, this always returns (0, 0).
Returns the “remaining width” of the current group, as per the convention described in
Ui::remaining_size
’s documentation.
Returns the “remaining height” of the current group, as per the convention described in
Ui::remaining_size
’s documentation.
Clears the group stack and pushes the root group onto the stack, with the given size and layout. The root group is the first group that should be pushed onto the stack. It defines the size of the window, and the initial layout to be used.
Note that this root group must not be popped off manually, as it gets popped off every frame anyways, because the stack is cleared upon calling this function.
Pushes a group onto the group stack, with the given size and layout.
Pops a group off the group stack, updating the cursor of the group under it.
Sets the cursor position of the current group. This is most useful with freeform layouts.
Pads the current group with some amount of padding.
Aligns the current group in the parent group, with the provided alignment.
Panics
If there are less than two groups (the parent and the subject) on the stack.
Inserts empty space between subgroups, by increasing or decreasing the cursor position by the given amount.
Panics
- If there are no groups.
- On freeform layout, as it’s not clear which direction the spacing should be performed in.
Resizes the current group to fit its children. This function considers a few cases:
- on
Freeform
layout, it sets the width and height to the cursor, - on
Horizontal
layout, it sets the width to the cursor’s X position, - on
Vertical
layout, it sets the height to the cursor’s Y position. - on reversed layouts, it panics, as layouting there works a bit backwards and fitting currently doesn’t work properly. This might get solved in a future release.
Panics
- If there are no groups.
- On reversed layouts, as noted above.
Allows one to draw in the current group by translating the renderer’s matrix to the group’s position.
The renderer can be obtained inside of the callback by using Ui::render
.
Clips drawing to only occur inside of the current group.
Any pixels outside of the group are discarded. Note that to undo the clip,
self.render().push()
and self.render().pop()
must be used.
Draws a rectangle that fills the current group with the given color.
Draws a rounded rectangle that fills the current group, with the given color and corner radius.
Draws a rectangle outline that creates a border around the current group, with the given color and line thickness.
Draws a rounded rectangle outline that creates a border around the current group, with the given color, corner radius, and line thickness.
Sets the current group’s line cap for rendering lines. The root group’s default line cap is LineCap::Butt
.
Draws a line spanning the left side of the current group, with the given color and line thickness.
Draws a line spanning the top side of the current group, with the given color and line thickness.
Draws a line spanning the right side of the current group, with the given color and line thickness.
Draws a line spanning the bottom side of the current group, with the given color and line thickness.
Trait Implementations
Any Ui
instance acts as if it were the underlying renderer.
In case any conflicts occur (such as with Ui::text
and Renderer::text
, Ui::render
may be used to
specify that the renderer method should be called instead.
Any mutable Ui
instance acts as if it were the underlying renderer.
In case any conflicts occur (such as with Ui::text
and Renderer::text
, Ui::renderer
may be used to
specify that the renderer method should be called instead.