b09795ccff
- Add `persistence.rs` for stable serialization of dock layouts without runtime identifiers. - Introduce `preview.rs` for geometry handling of dock drop previews. - Create `sizing.rs` to manage content-aware sizing policies and constraint solving for dock panels. - Implement `welcome.rs` to provide a welcome surface when no documents are open, featuring New and Open actions.
240 lines
9.6 KiB
Rust
240 lines
9.6 KiB
Rust
#![allow(deprecated)]
|
|
//! Visual test GUI for the decoupled `hcie-draw` library using `egui`.
|
|
//! Compiles only when running `cargo run --example draw_tester`.
|
|
|
|
use eframe::egui;
|
|
use hcie_draw::{draw_filled_circle, draw_filled_rect, draw_line, flood_fill};
|
|
use hcie_protocol::Layer;
|
|
|
|
fn main() -> eframe::Result {
|
|
let options = eframe::NativeOptions {
|
|
viewport: egui::ViewportBuilder::default()
|
|
.with_inner_size([700.0, 500.0])
|
|
.with_title("HCIE Draw Engine Visual Tester"),
|
|
..Default::default()
|
|
};
|
|
eframe::run_native(
|
|
"HCIE Draw Visual Tester",
|
|
options,
|
|
Box::new(|cc| Ok(Box::new(DrawTesterApp::new(cc)))),
|
|
)
|
|
}
|
|
|
|
struct DrawTesterApp {
|
|
layer: Layer,
|
|
current_color: [u8; 4],
|
|
thickness: f32,
|
|
selected_tool: &'static str,
|
|
last_drag_pos: Option<egui::Pos2>,
|
|
texture: Option<egui::TextureHandle>,
|
|
}
|
|
|
|
impl DrawTesterApp {
|
|
fn new(_cc: &eframe::CreationContext<'_>) -> Self {
|
|
let width = 256;
|
|
let height = 256;
|
|
let len = (width * height * 4) as usize;
|
|
|
|
let mut pixels = vec![255u8; len];
|
|
for y in 0..height {
|
|
for x in 0..width {
|
|
let i = ((y * width + x) * 4) as usize;
|
|
let is_gray = ((x / 16) + (y / 16)) % 2 == 0;
|
|
let c = if is_gray { 220 } else { 255 };
|
|
pixels[i] = c;
|
|
pixels[i + 1] = c;
|
|
pixels[i + 2] = c;
|
|
pixels[i + 3] = 255;
|
|
}
|
|
}
|
|
|
|
let layer = Layer::from_rgba("canvas", width, height, pixels);
|
|
|
|
Self {
|
|
layer,
|
|
current_color: [255, 0, 0, 255],
|
|
thickness: 5.0,
|
|
selected_tool: "Line",
|
|
last_drag_pos: None,
|
|
texture: None,
|
|
}
|
|
}
|
|
|
|
fn clear_canvas(&mut self) {
|
|
let w = self.layer.width;
|
|
let h = self.layer.height;
|
|
for y in 0..h {
|
|
for x in 0..w {
|
|
let i = ((y * w + x) * 4) as usize;
|
|
let is_gray = ((x / 16) + (y / 16)) % 2 == 0;
|
|
let c = if is_gray { 220 } else { 255 };
|
|
self.layer.pixels[i] = c;
|
|
self.layer.pixels[i + 1] = c;
|
|
self.layer.pixels[i + 2] = c;
|
|
self.layer.pixels[i + 3] = 255;
|
|
}
|
|
}
|
|
self.texture = None;
|
|
}
|
|
}
|
|
|
|
impl eframe::App for DrawTesterApp {
|
|
fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
|
|
let ctx = ui.ctx().clone();
|
|
if self.texture.is_none() {
|
|
let color_image = egui::ColorImage::from_rgba_unmultiplied(
|
|
[self.layer.width as usize, self.layer.height as usize],
|
|
&self.layer.pixels,
|
|
);
|
|
self.texture =
|
|
Some(ctx.load_texture("canvas_preview", color_image, Default::default()));
|
|
}
|
|
let texture = self.texture.as_ref().unwrap().clone();
|
|
|
|
egui::CentralPanel::default().show(&ctx, |ui| {
|
|
ui.heading("HCIE Draw Engine Visual Tester");
|
|
ui.add_space(10.0);
|
|
|
|
ui.horizontal(|ui| {
|
|
ui.vertical(|ui| {
|
|
ui.label("Click & drag to draw on this canvas:");
|
|
ui.add_space(5.0);
|
|
|
|
let image_widget = egui::Image::new((texture.id(), texture.size_vec2()))
|
|
.sense(egui::Sense::click_and_drag());
|
|
let resp = ui.add(image_widget);
|
|
|
|
if resp.dragged() || resp.clicked() {
|
|
if let Some(hover_pos) = resp.hover_pos() {
|
|
let rect = resp.rect;
|
|
let px = ((hover_pos.x - rect.min.x) / rect.width()
|
|
* self.layer.width as f32)
|
|
.clamp(0.0, self.layer.width as f32 - 1.0);
|
|
let py = ((hover_pos.y - rect.min.y) / rect.height()
|
|
* self.layer.height as f32)
|
|
.clamp(0.0, self.layer.height as f32 - 1.0);
|
|
|
|
match self.selected_tool {
|
|
"Line" => {
|
|
if let Some(last_pos) = self.last_drag_pos {
|
|
draw_line(
|
|
&mut self.layer,
|
|
last_pos.x,
|
|
last_pos.y,
|
|
px,
|
|
py,
|
|
self.current_color,
|
|
self.thickness,
|
|
None,
|
|
);
|
|
self.texture = None;
|
|
}
|
|
self.last_drag_pos = Some(egui::pos2(px, py));
|
|
}
|
|
"Circle" => {
|
|
if resp.drag_started() || resp.clicked() {
|
|
draw_filled_circle(
|
|
&mut self.layer,
|
|
px,
|
|
py,
|
|
self.thickness * 2.0,
|
|
self.current_color,
|
|
None,
|
|
);
|
|
self.texture = None;
|
|
}
|
|
}
|
|
"Rect" => {
|
|
if resp.drag_started() || resp.clicked() {
|
|
let size = self.thickness * 3.0;
|
|
draw_filled_rect(
|
|
&mut self.layer,
|
|
px - size,
|
|
py - size,
|
|
px + size,
|
|
py + size,
|
|
self.current_color,
|
|
None,
|
|
);
|
|
self.texture = None;
|
|
}
|
|
}
|
|
"Flood Fill" => {
|
|
if resp.clicked() {
|
|
flood_fill(
|
|
&mut self.layer,
|
|
px as u32,
|
|
py as u32,
|
|
self.current_color,
|
|
20,
|
|
None,
|
|
);
|
|
self.texture = None;
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
} else {
|
|
self.last_drag_pos = None;
|
|
}
|
|
});
|
|
|
|
ui.add_space(20.0);
|
|
|
|
ui.vertical(|ui| {
|
|
ui.label("Toolbar Tools:");
|
|
ui.add_space(5.0);
|
|
|
|
ui.selectable_value(&mut self.selected_tool, "Line", "Brush/Line");
|
|
ui.selectable_value(&mut self.selected_tool, "Circle", "Filled Circle");
|
|
ui.selectable_value(&mut self.selected_tool, "Rect", "Filled Rectangle");
|
|
ui.selectable_value(&mut self.selected_tool, "Flood Fill", "Flood Fill");
|
|
|
|
ui.add_space(15.0);
|
|
ui.separator();
|
|
ui.add_space(15.0);
|
|
|
|
ui.label(format!("Size / Radius: {:.1}", self.thickness));
|
|
ui.add(egui::Slider::new(&mut self.thickness, 1.0..=50.0));
|
|
|
|
ui.add_space(15.0);
|
|
|
|
ui.label("Brush Color:");
|
|
let mut color_f32 = [
|
|
self.current_color[0] as f32 / 255.0,
|
|
self.current_color[1] as f32 / 255.0,
|
|
self.current_color[2] as f32 / 255.0,
|
|
self.current_color[3] as f32 / 255.0,
|
|
];
|
|
if ui
|
|
.color_edit_button_rgba_unmultiplied(&mut color_f32)
|
|
.changed()
|
|
{
|
|
self.current_color = [
|
|
(color_f32[0] * 255.0).round() as u8,
|
|
(color_f32[1] * 255.0).round() as u8,
|
|
(color_f32[2] * 255.0).round() as u8,
|
|
(color_f32[3] * 255.0).round() as u8,
|
|
];
|
|
}
|
|
|
|
ui.add_space(25.0);
|
|
|
|
if ui.button("Clear Canvas").clicked() {
|
|
self.clear_canvas();
|
|
}
|
|
|
|
ui.add_space(35.0);
|
|
ui.colored_label(egui::Color32::LIGHT_GREEN, "Layer-based drawing API");
|
|
ui.colored_label(egui::Color32::LIGHT_GREEN, "Decoupled from hcie-protocol");
|
|
ui.colored_label(
|
|
egui::Color32::LIGHT_GREEN,
|
|
"Operating with 100% GUI neutrality",
|
|
);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
}
|