mod support;
use macroquad::prelude::*;
use rae::{compute_shell_layout, Insets, Palette, Rect, Rgba};
use support::*;
fn window_conf() -> Conf {
Conf {
window_title: "rae layout lab".to_string(),
window_width: 1440,
window_height: 900,
high_dpi: true,
sample_count: 4,
..Default::default()
}
}
#[macroquad::main(window_conf)]
async fn main() {
let palette = Palette::midnight();
let max_frames = max_frames_from_env();
let mut frames = 0u64;
loop {
frames = frames.saturating_add(1);
if should_stop(frames, max_frames) {
break;
}
let time = get_time() as f32;
let viewport = Rect::new(0.0, 0.0, screen_width(), screen_height());
let layout = compute_shell_layout(viewport);
draw_scene_backdrop(&palette, time);
draw_header(layout.header, &palette, layout.sidebar_collapsed, time);
draw_main_lab(layout, &palette, time);
if layout.sidebar_collapsed {
draw_compact_preview(layout.main, &palette);
} else {
draw_breakpoint_rail(layout.sidebar, &palette, time);
}
draw_prompt(layout.prompt, &palette);
draw_status(layout.status, &palette, layout);
next_frame().await;
}
}
fn draw_header(rect: Rect, palette: &Palette, collapsed: bool, time: f32) {
if rect.is_empty() {
return;
}
draw_glass_panel(rect, 24.0, palette, palette.accent, true);
draw_glow(
rect.x + 72.0,
rect.y + rect.height * 0.5,
72.0,
palette.accent.with_alpha(0.20),
5,
);
draw_text_line(
"rae",
rect.x + 26.0,
rect.y + 39.0,
31.0,
to_mq(palette.text),
);
draw_text_line(
"layout lab",
rect.x + 92.0,
rect.y + 35.0,
18.0,
to_mq(palette.accent),
);
draw_text_line(
"live shell geometry with responsive breakpoints and desktop-grade spacing",
rect.x + 208.0,
rect.y + 36.0,
16.0,
to_mq(palette.muted),
);
let right = rect.right() - 424.0;
draw_pill(
Rect::new(right, rect.y + 14.0, 128.0, 30.0),
if collapsed { "compact" } else { "expanded" },
if collapsed {
palette.warning
} else {
palette.success
},
palette.text,
);
draw_pill(
Rect::new(right + 142.0, rect.y + 14.0, 124.0, 30.0),
"resize me",
palette.accent,
palette.text,
);
draw_pill(
Rect::new(right + 280.0, rect.y + 14.0, 132.0, 30.0),
&format!("{:.1}s", time),
Rgba::rgb8(190, 94, 255),
palette.text,
);
}
fn draw_main_lab(layout: rae::AppLayout, palette: &Palette, time: f32) {
draw_glass_panel(layout.main, 30.0, palette, palette.success, true);
let content = layout.main.inset(Insets {
top: 26.0,
right: 28.0,
bottom: 28.0,
left: 28.0,
});
draw_text_line(
"Live Layout",
content.x,
content.y + 24.0,
28.0,
to_mq(palette.text),
);
draw_text_line(
"The same rectangle model feeds renderers, hit maps, scroll math, and widgets.",
content.x,
content.y + 50.0,
16.0,
to_mq(palette.muted),
);
let metric_y = content.y + 72.0;
let metric_w = (content.width - 36.0) / 3.0;
draw_metric(
Rect::new(content.x, metric_y, metric_w, 76.0),
"viewport",
&format!("{:.0} x {:.0}", layout.root.width, layout.root.height),
palette.accent,
palette,
);
draw_metric(
Rect::new(content.x + metric_w + 18.0, metric_y, metric_w, 76.0),
"main",
&format!("{:.0} x {:.0}", layout.main.width, layout.main.height),
palette.success,
palette,
);
draw_metric(
Rect::new(
content.x + (metric_w + 18.0) * 2.0,
metric_y,
metric_w,
76.0,
),
"sidebar",
if layout.sidebar_collapsed {
"collapsed"
} else {
"visible"
},
if layout.sidebar_collapsed {
palette.warning
} else {
Rgba::rgb8(91, 255, 211)
},
palette,
);
let stage = Rect::new(
content.x,
metric_y + 104.0,
content.width,
(content.bottom() - metric_y - 104.0).max(220.0),
);
draw_layout_stage(stage, layout, palette, time);
}
fn draw_layout_stage(stage: Rect, layout: rae::AppLayout, palette: &Palette, time: f32) {
draw_shadow(stage, 28.0, 1.0);
draw_rounded_rect(stage, 28.0, rgba8(2, 7, 18, 210));
draw_rectangle_lines(
stage.x,
stage.y,
stage.width,
stage.height,
1.0,
to_mq(palette.accent.with_alpha(0.24)),
);
for index in 0..9 {
let y = stage.y + 28.0 + index as f32 * 42.0;
draw_line(
stage.x + 24.0,
y,
stage.right() - 24.0,
y + (time * 0.9 + index as f32).sin() * 8.0,
1.0,
to_mq(palette.accent.with_alpha(0.055)),
);
}
let root = layout.root;
let target = stage.inset(Insets::all(34.0));
let scale = (target.width / root.width).min(target.height / root.height);
let scaled = Rect::new(
target.x + (target.width - root.width * scale) * 0.5,
target.y + (target.height - root.height * scale) * 0.5,
root.width * scale,
root.height * scale,
);
draw_rounded_rect(scaled, 18.0, to_mq(palette.background.with_alpha(0.86)));
draw_rectangle_lines(
scaled.x,
scaled.y,
scaled.width,
scaled.height,
1.2,
to_mq(palette.text.with_alpha(0.18)),
);
draw_scaled_rect(
layout.header,
scale,
scaled,
Rgba::rgb8(122, 185, 255),
"header",
);
draw_scaled_rect(layout.main, scale, scaled, Rgba::rgb8(83, 225, 147), "main");
draw_scaled_rect(
layout.sidebar,
scale,
scaled,
Rgba::rgb8(255, 190, 94),
"side",
);
draw_scaled_rect(
layout.prompt,
scale,
scaled,
Rgba::rgb8(122, 185, 255),
"prompt",
);
draw_scaled_rect(layout.status, scale, scaled, palette.muted, "status");
}
fn draw_scaled_rect(rect: Rect, scale: f32, root: Rect, color: Rgba, label: &str) {
if rect.is_empty() {
return;
}
let scaled = Rect::new(
root.x + rect.x * scale,
root.y + rect.y * scale,
rect.width * scale,
rect.height * scale,
);
draw_rounded_rect(scaled, 8.0, to_mq(color.with_alpha(0.34)));
draw_rectangle_lines(
scaled.x,
scaled.y,
scaled.width,
scaled.height,
1.0,
to_mq(color.with_alpha(0.72)),
);
if scaled.width > 72.0 && scaled.height > 24.0 {
draw_text_line(label, scaled.x + 8.0, scaled.y + 18.0, 11.0, to_mq(color));
}
}
fn draw_breakpoint_rail(rect: Rect, palette: &Palette, time: f32) {
draw_glass_panel(rect, 30.0, palette, palette.warning, false);
let content = rect.inset(Insets {
top: 28.0,
right: 22.0,
bottom: 24.0,
left: 22.0,
});
draw_text_line(
"Breakpoints",
content.x,
content.y + 23.0,
24.0,
to_mq(palette.text),
);
draw_text_line(
"Scaled previews from the same layout function.",
content.x,
content.y + 47.0,
14.0,
to_mq(palette.muted),
);
let presets = [
(
"desktop",
Rect::new(0.0, 0.0, 1920.0, 1080.0),
palette.success,
),
("laptop", Rect::new(0.0, 0.0, 1366.0, 768.0), palette.accent),
("tablet", Rect::new(0.0, 0.0, 900.0, 720.0), palette.warning),
(
"phone",
Rect::new(0.0, 0.0, 430.0, 860.0),
Rgba::rgb8(205, 130, 255),
),
];
let mut y = content.y + 86.0;
for (index, (label, viewport, color)) in presets.into_iter().enumerate() {
let card = Rect::new(content.x, y, content.width, 118.0);
draw_breakpoint_card(
card,
label,
viewport,
color,
palette,
time + index as f32 * 0.33,
);
y += 134.0;
}
}
fn draw_breakpoint_card(
card: Rect,
label: &str,
viewport: Rect,
color: Rgba,
palette: &Palette,
time: f32,
) {
draw_rounded_rect(card, 20.0, rgba8(3, 9, 23, 192));
draw_rectangle_lines(
card.x,
card.y,
card.width,
card.height,
1.0,
to_mq(color.with_alpha(0.36)),
);
draw_text_line(label, card.x + 14.0, card.y + 24.0, 15.0, to_mq(color));
draw_text_line(
&format!("{:.0} x {:.0}", viewport.width, viewport.height),
card.right() - 96.0,
card.y + 24.0,
12.0,
to_mq(palette.muted),
);
let target = Rect::new(
card.x + 14.0,
card.y + 38.0,
card.width - 28.0,
card.height - 52.0,
);
let scale = (target.width / viewport.width).min(target.height / viewport.height);
let root = Rect::new(
target.x + (target.width - viewport.width * scale) * 0.5,
target.y + (target.height - viewport.height * scale) * 0.5,
viewport.width * scale,
viewport.height * scale,
);
let layout = compute_shell_layout(viewport);
draw_rounded_rect(root, 8.0, to_mq(palette.background.with_alpha(0.78)));
draw_scaled_rect(layout.header, scale, root, palette.accent, "");
draw_scaled_rect(layout.main, scale, root, palette.success, "");
draw_scaled_rect(layout.sidebar, scale, root, palette.warning, "");
draw_scaled_rect(layout.prompt, scale, root, color, "");
draw_scaled_rect(layout.status, scale, root, palette.muted, "");
draw_line(
root.x,
root.bottom() + 8.0,
root.x + root.width * (0.45 + time.sin() * 0.04),
root.bottom() + 8.0,
2.0,
to_mq(color.with_alpha(0.58)),
);
}
fn draw_compact_preview(main: Rect, palette: &Palette) {
let badge = Rect::new(main.right() - 218.0, main.y + 22.0, 188.0, 36.0);
draw_pill(badge, "sidebar collapsed", palette.warning, palette.text);
}
fn draw_prompt(rect: Rect, palette: &Palette) {
draw_glass_panel(rect, 26.0, palette, palette.accent, false);
let input = rect.inset(Insets {
top: 44.0,
right: 26.0,
bottom: 22.0,
left: 26.0,
});
draw_text_line(
"constraint notes",
rect.x + 24.0,
rect.y + 30.0,
15.0,
to_mq(palette.accent),
);
draw_rounded_rect(input, 18.0, rgba8(1, 5, 15, 134));
draw_wrapped_text(
"Panels keep safe gutters, prompt/status stay pinned, and the sidebar collapses below desktop width. This example is a renderer consuming library geometry.",
input.x + 18.0,
input.y + 29.0,
input.width - 36.0,
17.0,
23.0,
to_mq(palette.text),
);
}
fn draw_status(rect: Rect, palette: &Palette, layout: rae::AppLayout) {
draw_rounded_rect(rect, 12.0, to_mq(palette.panel.with_alpha(0.66)));
draw_text_line(
&format!(
"demo_layout | live rectangles {:.0}x{:.0} | sidebar:{} | Esc closes",
layout.root.width,
layout.root.height,
if layout.sidebar_collapsed {
"collapsed"
} else {
"expanded"
}
),
rect.x + 16.0,
rect.y + 22.0,
14.0,
to_mq(palette.muted),
);
}