1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
use ggez::{graphics::Color, *};
use mooeye::{scene_manager, ui, ui::UiContainer, ui::UiContent};
// # Containers
// In this example, we learn about the 4 main types of containers provided with mooeye and use them to create a UI containing multiple elements.
/// A very basic scene struct, once again only holding the root element of our GUI.
pub struct DScene {
/// The root element of DScene's GUI.
gui: ui::UiElement<()>,
}
impl DScene {
/// Creates a new DScene.
/// As you can see, for scenes that do not change based on parameters, we would like to use something like ```DScene::default()``` to
/// communicate that this is the standard way for a ```DScene``` to come into existence.
/// However, we cannot derive Default as the passing of a parameter ```ctx: &Context``` is almost always neccessary,
/// so we have to use ```new(ctx: &Context)``` instead.
pub fn new(ctx: &Context) -> Result<Self, GameError> {
// Predefine some visuals so we don't have to do it for every element.
let vis = ui::Visuals::new(
Color::from_rgb(180, 120, 60),
Color::from_rgb(18, 12, 6),
1.,
0.,
);
// You can also create 'custom' visuals that allow you to set the thickness of each border & radius of each corner separately.
let vis2 = ui::Visuals::new_custom(
Color::from_rgb(180, 120, 60),
Color::from_rgb(18, 12, 6),
[4., 1., 4., 1.],
[2., 2., 2., 2.],
);
let hover_vis = ui::Visuals::new(
Color::from_rgb(160, 100, 40),
Color::from_rgb(18, 12, 6),
3.,
0.,
);
let cont_vis = ui::Visuals::new_custom(
Color::from_rgb(60, 120, 180),
Color::from_rgb(180, 180, 190),
[16., 8., 8., 8.],
[12., 2., 2., 12.],
);
// Note that the constructor now returns a Result.
// This is neccessary as the 'add' function used to add UI elements to grid containers can fail, thus failing the constructor.
// The first container we use is a vertical box simply laying out elements from top to bottom.
let mut ver_box = ui::containers::VerticalBox::new();
// We can manually change the spacing between elements in the box
ver_box.spacing = 10.;
// first, we need to add all the children to this vertical box.
// We'll just use TextElement for now, but these can also be images, sprites, placeholders or more containers.
for i in 0..8 {
// Create an element.
let element = graphics::Text::new(format!("{}", i))
.set_font("Bahnschrift")
.set_scale(28.)
.to_owned()
.to_element_builder(0, ctx)
.with_visuals(vis2)
.build();
// Add the element to the box. This cannot fail for non-grid containers, if ver_box were not an actual container
// or a container that requires a special method for adding, like e.g. GridBox, it would simply consume the element and do nothing.
ver_box.add(element);
}
// After adding all children, we can convert to a UiElement and style the box like we would style an other element. The usual pattern here is to shadow the variable to avoid use-after-move.
let ver_box = ver_box
.to_element_builder(0, ctx)
.with_visuals(cont_vis)
// Using larger padding to accomodate our thick borders.
.with_padding((24., 16., 16., 16.))
.build();
// Another container we can use is GridBox. A GridBox needs to be initialized with a set height and width and cannot be extended.
let mut grid = ui::containers::GridBox::new(4, 4);
// The contents of a grid box are initialized as empty elements. We'll add buttons to the diagonal of the grid.
for i in 0..4 {
// Create an element.
let element = graphics::Text::new(format!("{}", i))
.set_font("Bahnschrift")
.set_scale(28.)
.to_owned()
.to_element_builder(0, ctx)
.with_visuals(vis)
// Elements can be given alignment and will align within their respecitive cell in the grid.
.with_alignment(ui::Alignment::Max, None)
.build();
// Add the element to the box. This can fail, if ver_box were not an actual container
// or a container that requires a special method for adding, like e.g. GridBox.
grid.add(element, i, i)?;
}
// We'll also create our usual back button and put it into the top right of the grid.
let back = graphics::Text::new("Back!")
.set_font("Bahnschrift")
.set_scale(28.)
.to_owned()
.to_element_builder(1, ctx)
.with_visuals(vis)
.with_hover_visuals(hover_vis)
.build();
// This time, we'll enhance our back button a bit by using an icon that is displayed over the top left corner.
// To achieve this, we'll use a StackBox.
let mut stack = ui::containers::StackBox::new();
stack.add(back);
// The add_top function adds something to the top of a stack box. Creating and adding an element can be done inline.
stack.add_top(
graphics::Image::from_path(ctx, "/moo.png")?
.to_element_builder(0, ctx)
// We'll align the icon to the top right
.with_alignment(ui::Alignment::Min, ui::Alignment::Min)
// and offset it slightly
.with_offset(-10., -10.)
.build(),
)?;
// to_element is a shorthand for to_element_builder().build() if we want to simply take the default builder and not change anything.
let stack = stack.to_element(0, ctx);
// Now, we add the stack to the grid.
grid.add(stack, 3, 0)?;
// And finally build the grid.
let grid = grid
.to_element_builder(0, ctx)
.with_visuals(cont_vis)
.with_padding((24., 16., 16., 16.))
.build();
// The horizontal box is exactly the same as the vertical box except for orientation.
// We will use a horizontal box to contain the boxes created so far.
// if you don't want to create multiple variables, adding of children can be done inline for non-grid
// containers by using .with_child.
Ok(Self {
gui: ui::containers::HorizontalBox::new()
.to_element_builder(0, ctx)
.with_child(ver_box)
.with_child(grid)
.build(),
})
}
}
impl scene_manager::Scene for DScene {
fn update(&mut self, ctx: &mut Context) -> Result<scene_manager::SceneSwitch, GameError> {
// Nothing much to do here, except implement the back button functionality.
let messages = self.gui.manage_messages(ctx, None);
if messages.contains(&ui::UiMessage::Triggered(1)) {
// If it is, we end the current scene (and return to the previous one) by popping it off the stack.
return Ok(scene_manager::SceneSwitch::pop(1));
}
Ok(scene_manager::SceneSwitch::None)
}
fn draw(&mut self, ctx: &mut Context, mouse_listen: bool) -> Result<(), GameError> {
// Once again, we first create a canvas and set a pixel sampler. Note that this time, we dont clear the background.
let mut canvas = ggez::graphics::Canvas::from_frame(ctx, None);
// Since we don't set the sampler to 'nearest', our corners will look more round, but the pixel-cow will look blurry.
//canvas.set_sampler(ggez::graphics::Sampler::nearest_clamp());
self.gui.draw_to_screen(ctx, &mut canvas, mouse_listen);
canvas.finish(ctx)?;
Ok(())
}
}