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(())
    }
}