page layout tool
git clone https://git.lucas.co/cce-layout-interface.git
Align paginator vertical sidebar tab widths and style
src/main.rs | 1328 +++++++++++++++++++++++++++++++----------------------------
1 file changed, 699 insertions(+), 629 deletions(-)
diff --git a/src/main.rs b/src/main.rs
index bb4f789..9be405e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,7 +3,8 @@ use glyphon::{FontSystem, Buffer, Metrics, Attrs};
use clear_ui::engine::{Application, EngineState, LogicalPosition, LogicalSize, WindowSettings};
use clear_ui::widget::{
MouseButton, ElementState, MouseScrollDelta, KeyEvent, TextItem, Widget,
- MenuBar, TextBox, Slider, TextLabel, Paginator, Button, Dropdown, Toggle, ColorSelector
+ MenuBar, TextBox, Slider, TextLabel, Paginator, Button, Dropdown, Toggle, ColorSelector,
+ Label, SectionHeader, Spinbox
};
#[derive(Debug, Clone)]
@@ -32,6 +33,7 @@ enum Element {
h: f32,
font_size: f32,
color: [f32; 4],
+ font_family: String,
},
Shape {
shape_type: ShapeType,
@@ -55,6 +57,7 @@ const PRESETS: &[PagePreset] = &[
PagePreset { name: "A4 (210 x 297 mm)", w: 496.0, h: 701.0 },
PagePreset { name: "A5 (148 x 210 mm)", w: 350.0, h: 496.0 },
PagePreset { name: "Poster (8\" x 8\")", w: 480.0, h: 480.0 },
+ PagePreset { name: "Custom", w: 510.0, h: 660.0 },
];
struct LayoutApp {
@@ -73,14 +76,28 @@ struct LayoutApp {
// Page 0: Layout properties controls
dropdown_presets: Dropdown,
+ slider_page_x: Slider,
+ slider_page_y: Slider,
page_color_selector: ColorSelector,
page_color: [f32; 4],
+ toggle_margin: Toggle,
+ dropdown_margin_units: Dropdown,
+ slider_margin_x: Slider,
+ slider_margin_y: Slider,
+ margin_enabled: bool,
+ margin_x: f32,
+ margin_y: f32,
+ toggle_word_processor: Toggle,
+ word_processor_enabled: bool,
+ wp_text_box: TextBox,
sidebar_x: TextBox,
sidebar_y: TextBox,
sidebar_w: TextBox,
sidebar_h: TextBox,
sidebar_text: TextBox,
- sidebar_size: TextBox,
+ sidebar_size: Spinbox,
+ dropdown_font_family: Dropdown,
+ wp_base_font_size: f32,
slider_r: Slider,
slider_g: Slider,
@@ -98,11 +115,21 @@ struct LayoutApp {
toggle_rulers: Toggle,
dropdown_units: Dropdown,
+ section_rulers: SectionHeader,
+ label_width: Label,
+ label_height: Label,
+ label_sel_status: Label,
+ label_sel_desc1: Label,
+ label_sel_desc2: Label,
+ label_grid_snap: Label,
+ label_total_elements: Label,
+
last_selected: Option<usize>,
page_w: f32,
page_h: f32,
pan_x: f32,
pan_y: f32,
+ widgets_registered: bool,
}
fn get_monitor_ppi() -> f32 {
@@ -146,14 +173,68 @@ fn get_monitor_ppi() -> f32 {
}
impl LayoutApp {
+ fn sync_page_units(&mut self) {
+ let unit_idx = self.dropdown_margin_units.selected;
+ let px_per_unit = match unit_idx {
+ 0 => 1.0, // Pixels
+ 1 => 60.0, // Inches
+ 2 => 23.622047, // Centimeters
+ 3 => 2.3622047, // Millimeters
+ _ => 1.0,
+ };
+ let max_val = 100.0 / px_per_unit;
+ self.slider_margin_x.set_range(0.0, max_val);
+ self.slider_margin_x.set_value(self.margin_x / 100.0);
+
+ self.slider_margin_y.set_range(0.0, max_val);
+ self.slider_margin_y.set_value(self.margin_y / 100.0);
+
+ self.slider_page_x.set_range(100.0 / px_per_unit, 1500.0 / px_per_unit);
+ self.slider_page_x.set_value((self.page_w - 100.0) / (1500.0 - 100.0));
+
+ self.slider_page_y.set_range(100.0 / px_per_unit, 1500.0 / px_per_unit);
+ self.slider_page_y.set_value((self.page_h - 100.0) / (1500.0 - 100.0));
+
+ let unit_suffix = match unit_idx {
+ 0 => "px",
+ 1 => "in",
+ 2 => "cm",
+ 3 => "mm",
+ _ => "px",
+ };
+ self.slider_margin_x.set_label(&format!("Margin Width (X) ({})", unit_suffix));
+ self.slider_margin_y.set_label(&format!("Margin Height (Y) ({})", unit_suffix));
+ self.slider_page_x.set_label(&format!("Page Width (X) ({})", unit_suffix));
+ self.slider_page_y.set_label(&format!("Page Height (Y) ({})", unit_suffix));
+ }
+
fn render_zoom(&self) -> f32 {
let ppi = get_monitor_ppi();
let scale_factor = self.scale_factor as f32;
let zoom_multiplier = ppi / (60.0 * scale_factor);
- self.slider_zoom.value() * zoom_multiplier
+ self.slider_zoom.get_scaled_value() * zoom_multiplier
}
fn rebuild_text_items(&mut self) {
+ // Sync labels with current state
+ self.label_width.set_text(&format!("Width: {} px", self.page_w));
+ self.label_height.set_text(&format!("Height: {} px", self.page_h));
+
+ if let Some(idx) = self.selected_idx {
+ self.label_sel_status.set_text(&format!("Selected Element #{}", idx + 1));
+ self.label_sel_status.set_color([0x3b, 0x82, 0xf6]);
+ self.label_sel_desc1.set_text("");
+ self.label_sel_desc2.set_text("");
+ } else {
+ self.label_sel_status.set_text("No Selection");
+ self.label_sel_status.set_color([0x83, 0x83, 0x8a]);
+ self.label_sel_desc1.set_text("Click canvas elements");
+ self.label_sel_desc2.set_text("to edit properties.");
+ }
+
+ self.label_grid_snap.set_text(&format!("Grid Snapping: {}", if self.grid_enabled { "ON (20px)" } else { "OFF" }));
+ self.label_total_elements.set_text(&format!("Total Elements: {}", self.elements.len()));
+
self.text_items.clear();
let mut labels = Vec::new();
@@ -171,165 +252,26 @@ impl LayoutApp {
// 2. Paginator sidebar tabs
labels.extend(self.paginator.text_labels());
+ if let Ok(mut file) = std::fs::OpenOptions::new().create(true).append(true).open("/tmp/layout_labels_debug.txt") {
+ use std::io::Write;
+ let _ = writeln!(file, "rebuild_text_items: labels={:?}", labels.iter().map(|l| &l.text).collect::<Vec<_>>());
+ }
- // 3. Conditional Page rendering
- if self.paginator.selected_page() == 0 {
- // Page Settings tab (no header)
- labels.extend(self.dropdown_presets.text_labels());
- labels.extend(self.page_color_selector.text_labels());
-
- // Add drop down list labels dynamically if open
- if self.dropdown_presets.open {
- let mut collector = clear_ui::layout::PopoverCollector::new();
- self.dropdown_presets.render_popover(&mut collector);
- for (text, size, x, y, color, _font) in collector.texts {
- labels.push(TextLabel {
- text,
- x,
- y,
- font_size: size,
- color: [
- (color[0] * 255.0).clamp(0.0, 255.0) as u8,
- (color[1] * 255.0).clamp(0.0, 255.0) as u8,
- (color[2] * 255.0).clamp(0.0, 255.0) as u8,
- ],
- });
- }
- }
-
- // Display active page width and height (shifted up from 160/180)
- labels.push(TextLabel {
- text: format!("Width: {} px", self.page_w),
- x: 20.0,
- y: 140.0,
- font_size: 11.0,
- color: [0x83, 0x83, 0x8a],
- });
- labels.push(TextLabel {
- text: format!("Height: {} px", self.page_h),
- x: 20.0,
- y: 160.0,
- font_size: 11.0,
- color: [0x83, 0x83, 0x8a],
- });
- } else if self.paginator.selected_page() == 1 {
- // Element Properties tab (renamed from Layout)
- labels.extend(self.sidebar_x.text_labels());
- labels.extend(self.sidebar_y.text_labels());
- labels.extend(self.sidebar_w.text_labels());
- labels.extend(self.sidebar_h.text_labels());
- labels.extend(self.sidebar_text.text_labels());
- labels.extend(self.sidebar_size.text_labels());
-
- labels.push(TextLabel {
- text: format!("Red Color: {:.2}", self.slider_r.value()),
- x: 20.0,
- y: 320.0,
- font_size: 11.0,
- color: [0x83, 0x83, 0x8a],
- });
- labels.push(TextLabel {
- text: format!("Green Color: {:.2}", self.slider_g.value()),
- x: 20.0,
- y: 360.0,
- font_size: 11.0,
- color: [0x83, 0x83, 0x8a],
- });
- labels.push(TextLabel {
- text: format!("Blue Color: {:.2}", self.slider_b.value()),
- x: 20.0,
- y: 400.0,
- font_size: 11.0,
- color: [0x83, 0x83, 0x8a],
- });
-
- if let Some(idx) = self.selected_idx {
- labels.push(TextLabel {
- text: format!("Selected Element #{}", idx + 1),
- x: 20.0,
- y: 445.0,
- font_size: 12.0,
- color: [0x3b, 0x82, 0xf6],
- });
- } else {
- labels.push(TextLabel {
- text: "No Selection".to_string(),
- x: 20.0,
- y: 445.0,
- font_size: 12.0,
- color: [0x83, 0x83, 0x8a],
- });
- labels.push(TextLabel {
- text: "Click canvas elements".to_string(),
- x: 20.0,
- y: 467.0,
- font_size: 10.0,
- color: [0x60, 0x60, 0x68],
- });
- labels.push(TextLabel {
- text: "to edit properties.".to_string(),
- x: 20.0,
- y: 483.0,
- font_size: 10.0,
- color: [0x60, 0x60, 0x68],
- });
- }
- } else if self.paginator.selected_page() == 2 {
- // Canvas Options Page (no header)
- labels.extend(self.btn_toggle_grid.text_labels());
- labels.extend(self.btn_clear_canvas.text_labels());
- labels.extend(self.btn_add_text.text_labels());
- labels.extend(self.btn_add_rect.text_labels());
- labels.extend(self.btn_add_banner.text_labels());
-
- labels.push(TextLabel {
- text: format!("Grid Snapping: {}", if self.grid_enabled { "ON (20px)" } else { "OFF" }),
- x: 20.0,
- y: 350.0,
- font_size: 11.0,
- color: [0x83, 0x83, 0x8a],
- });
- labels.push(TextLabel {
- text: format!("Total Elements: {}", self.elements.len()),
- x: 20.0,
- y: 370.0,
- font_size: 11.0,
- color: [0x83, 0x83, 0x8a],
- });
- } else {
- // View Options Page (no header)
+ // 3. Collect and add active page popover labels
+ let mut collector = clear_ui::layout::PopoverCollector::new();
+ self.paginator.render_popover(&mut collector);
+ for (text, size, x, y, color, _font) in collector.texts {
labels.push(TextLabel {
- text: format!("Zoom: {:.0}%", self.slider_zoom.value() * 100.0),
- x: 20.0,
- y: 90.0,
- font_size: 11.0,
- color: [0x83, 0x83, 0x8a],
+ text,
+ x,
+ y,
+ font_size: size,
+ color: [
+ (color[0] * 255.0).clamp(0.0, 255.0) as u8,
+ (color[1] * 255.0).clamp(0.0, 255.0) as u8,
+ (color[2] * 255.0).clamp(0.0, 255.0) as u8,
+ ],
});
-
- // Section Widget: "Rulers"
- let mut collector = clear_ui::layout::PopoverCollector::new();
- let mut sec = clear_ui::layout::Section::new(&mut collector, 15.0, 155.0, 250.0, "RULERS");
- if self.dropdown_units.open {
- self.dropdown_units.render_popover(&mut collector);
- }
- let _ = sec.finish(&mut collector);
-
- for (text, size, x, y, color, _font) in collector.texts {
- labels.push(TextLabel {
- text,
- x,
- y,
- font_size: size,
- color: [
- (color[0] * 255.0).clamp(0.0, 255.0) as u8,
- (color[1] * 255.0).clamp(0.0, 255.0) as u8,
- (color[2] * 255.0).clamp(0.0, 255.0) as u8,
- ],
- });
- }
-
- labels.extend(self.toggle_rulers.text_labels());
- labels.extend(self.dropdown_units.text_labels());
}
if self.toggle_rulers.toggled() {
@@ -408,34 +350,59 @@ impl LayoutApp {
});
// 5. Canvas Element Labels (drawn relative to the paper sheet)
-
- for (idx, element) in self.elements.iter().enumerate() {
- match element {
- Element::Text { text, x, y, w: _, h: _, font_size, color } => {
- labels.push(TextLabel {
- text: text.clone(),
- x: page_x + *x * zoom + 8.0 * zoom,
- y: page_y + *y * zoom + 6.0 * zoom,
- font_size: *font_size * zoom,
- color: [
+ if self.word_processor_enabled {
+ let font_family = self.wp_text_box.font_family.clone();
+ for label in self.wp_text_box.text_labels() {
+ let metrics = Metrics::new(label.font_size, label.font_size * 1.4);
+ let mut buf = Buffer::new(&mut self.font_system, metrics);
+ let attrs = Attrs::new().family(glyphon::Family::Name(&font_family));
+ buf.set_text(&mut self.font_system, &label.text, attrs, glyphon::Shaping::Advanced);
+ buf.shape_until_scroll(&mut self.font_system, true);
+ self.text_items.push(TextItem {
+ buffer: buf,
+ x: label.x,
+ y: label.y,
+ color: glyphon::Color::rgb(label.color[0], label.color[1], label.color[2]),
+ });
+ }
+ } else {
+ for (idx, element) in self.elements.iter().enumerate() {
+ match element {
+ Element::Text { text, x, y, w: _, h: _, font_size, color, font_family } => {
+ let label_x = page_x + *x * zoom + 8.0 * zoom;
+ let label_y = page_y + *y * zoom + 6.0 * zoom;
+ let label_font_size = *font_size * zoom;
+ let label_color = [
(color[0] * 255.0).clamp(0.0, 255.0) as u8,
(color[1] * 255.0).clamp(0.0, 255.0) as u8,
(color[2] * 255.0).clamp(0.0, 255.0) as u8,
- ],
- });
- }
- Element::Shape { shape_type, x, y, w: _, h: _, color: _ } => {
- let type_str = match shape_type {
- ShapeType::Rectangle => "Rectangle",
- ShapeType::Banner => "Banner",
- };
- labels.push(TextLabel {
- text: format!("{} #{}", type_str, idx + 1),
- x: page_x + *x * zoom + 8.0 * zoom,
- y: page_y + *y * zoom + 6.0 * zoom,
- font_size: 11.0 * zoom,
- color: [0xee, 0xee, 0xf5],
- });
+ ];
+
+ let metrics = Metrics::new(label_font_size, label_font_size * 1.4);
+ let mut buf = Buffer::new(&mut self.font_system, metrics);
+ let attrs = Attrs::new().family(glyphon::Family::Name(font_family));
+ buf.set_text(&mut self.font_system, text, attrs, glyphon::Shaping::Advanced);
+ buf.shape_until_scroll(&mut self.font_system, true);
+ self.text_items.push(TextItem {
+ buffer: buf,
+ x: label_x,
+ y: label_y,
+ color: glyphon::Color::rgb(label_color[0], label_color[1], label_color[2]),
+ });
+ }
+ Element::Shape { shape_type, x, y, w: _, h: _, color: _ } => {
+ let type_str = match shape_type {
+ ShapeType::Rectangle => "Rectangle",
+ ShapeType::Banner => "Banner",
+ };
+ labels.push(TextLabel {
+ text: format!("{} #{}", type_str, idx + 1),
+ x: page_x + *x * zoom + 8.0 * zoom,
+ y: page_y + *y * zoom + 6.0 * zoom,
+ font_size: 11.0 * zoom,
+ color: [0xee, 0xee, 0xf5],
+ });
+ }
}
}
}
@@ -454,8 +421,73 @@ impl LayoutApp {
}
}
+ fn rebuild_element_tab_widgets(&mut self) {
+ self.paginator.clear_page_widgets(1);
+
+ let self_ptr = self as *mut Self;
+ unsafe {
+ if self.word_processor_enabled {
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).dropdown_font_family as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).sidebar_size as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).slider_r as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).slider_g as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).slider_b as *mut (dyn Widget + 'static));
+ } else if let Some(idx) = self.selected_idx {
+ match &self.elements[idx] {
+ Element::Text { .. } => {
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).sidebar_x as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).sidebar_y as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).sidebar_w as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).sidebar_h as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).sidebar_text as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).dropdown_font_family as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).sidebar_size as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).slider_r as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).slider_g as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).slider_b as *mut (dyn Widget + 'static));
+ }
+ Element::Shape { .. } => {
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).sidebar_x as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).sidebar_y as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).sidebar_w as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).sidebar_h as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).slider_r as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).slider_g as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).slider_b as *mut (dyn Widget + 'static));
+ }
+ }
+ } else {
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).label_sel_status as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).label_sel_desc1 as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(1, &mut (*self_ptr).label_sel_desc2 as *mut (dyn Widget + 'static));
+ }
+ }
+ let (px, py, pw, ph) = self.paginator.rect();
+ self.paginator.set_rect(px, py, pw, ph);
+ }
+
fn sync_sidebar_fields(&mut self) {
- if let Some(idx) = self.selected_idx {
+ if self.word_processor_enabled {
+ if !self.sidebar_size.editing {
+ self.sidebar_size.value = self.wp_base_font_size.round() as i32;
+ }
+ let dropdown_idx = match self.wp_text_box.font_family.as_str() {
+ "sans-serif" => 1,
+ "serif" => 2,
+ _ => 0, // monospace
+ };
+ self.dropdown_font_family.selected = dropdown_idx;
+
+ if let Some(rgb) = self.wp_text_box.text_color {
+ self.slider_r.set_value(rgb[0] as f32 / 255.0);
+ self.slider_g.set_value(rgb[1] as f32 / 255.0);
+ self.slider_b.set_value(rgb[2] as f32 / 255.0);
+ } else {
+ self.slider_r.set_value(0.1);
+ self.slider_g.set_value(0.1);
+ self.slider_b.set_value(0.14);
+ }
+ } else if let Some(idx) = self.selected_idx {
if self.last_selected != Some(idx) {
self.sidebar_text.unfocus();
self.sidebar_x.unfocus();
@@ -468,17 +500,24 @@ impl LayoutApp {
let element = &self.elements[idx];
match element {
- Element::Text { text, x, y, w, h, font_size, color } => {
+ Element::Text { text, x, y, w, h, font_size, color, font_family } => {
if !self.sidebar_text.editing { self.sidebar_text.text = text.clone(); }
if !self.sidebar_x.editing { self.sidebar_x.text = format!("{:.0}", x); }
if !self.sidebar_y.editing { self.sidebar_y.text = format!("{:.0}", y); }
if !self.sidebar_w.editing { self.sidebar_w.text = format!("{:.0}", w); }
if !self.sidebar_h.editing { self.sidebar_h.text = format!("{:.0}", h); }
- if !self.sidebar_size.editing { self.sidebar_size.text = format!("{:.0}", font_size); }
+ if !self.sidebar_size.editing { self.sidebar_size.value = font_size.round() as i32; }
self.slider_r.set_value(color[0]);
self.slider_g.set_value(color[1]);
self.slider_b.set_value(color[2]);
+
+ let dropdown_idx = match font_family.as_str() {
+ "sans-serif" => 1,
+ "serif" => 2,
+ _ => 0, // monospace
+ };
+ self.dropdown_font_family.selected = dropdown_idx;
}
Element::Shape { shape_type: _, x, y, w, h, color } => {
if !self.sidebar_text.editing { self.sidebar_text.text = "Shape Mode".to_string(); }
@@ -486,7 +525,6 @@ impl LayoutApp {
if !self.sidebar_y.editing { self.sidebar_y.text = format!("{:.0}", y); }
if !self.sidebar_w.editing { self.sidebar_w.text = format!("{:.0}", w); }
if !self.sidebar_h.editing { self.sidebar_h.text = format!("{:.0}", h); }
- if !self.sidebar_size.editing { self.sidebar_size.text = "N/A".to_string(); }
self.slider_r.set_value(color[0]);
self.slider_g.set_value(color[1]);
@@ -497,20 +535,49 @@ impl LayoutApp {
}
fn apply_sidebar_changes(&mut self) {
- if let Some(idx) = self.selected_idx {
+ if self.word_processor_enabled {
+ let family_str = match self.dropdown_font_family.selected {
+ 1 => "sans-serif",
+ 2 => "serif",
+ _ => "monospace",
+ }.to_string();
+ self.wp_text_box.font_family = family_str;
+
+ let size_val = if self.sidebar_size.editing {
+ self.sidebar_size.edit_buffer.parse::<f32>().unwrap_or(14.0)
+ } else {
+ self.sidebar_size.value as f32
+ };
+ self.wp_base_font_size = size_val;
+
+ let r = (self.slider_r.value() * 255.0).clamp(0.0, 255.0) as u8;
+ let g = (self.slider_g.value() * 255.0).clamp(0.0, 255.0) as u8;
+ let b = (self.slider_b.value() * 255.0).clamp(0.0, 255.0) as u8;
+ self.wp_text_box.text_color = Some([r, g, b]);
+ } else if let Some(idx) = self.selected_idx {
let text_val = if self.sidebar_text.editing { self.sidebar_text.edit_buffer.clone() } else { self.sidebar_text.text.clone() };
let x_val = if self.sidebar_x.editing { &self.sidebar_x.edit_buffer } else { &self.sidebar_x.text }.parse::<f32>().unwrap_or(0.0);
let y_val = if self.sidebar_y.editing { &self.sidebar_y.edit_buffer } else { &self.sidebar_y.text }.parse::<f32>().unwrap_or(0.0);
let w_val = if self.sidebar_w.editing { &self.sidebar_w.edit_buffer } else { &self.sidebar_w.text }.parse::<f32>().unwrap_or(100.0);
let h_val = if self.sidebar_h.editing { &self.sidebar_h.edit_buffer } else { &self.sidebar_h.text }.parse::<f32>().unwrap_or(50.0);
- let size_val = if self.sidebar_size.editing { &self.sidebar_size.edit_buffer } else { &self.sidebar_size.text }.parse::<f32>().unwrap_or(16.0);
+ let size_val = if self.sidebar_size.editing {
+ self.sidebar_size.edit_buffer.parse::<f32>().unwrap_or(16.0)
+ } else {
+ self.sidebar_size.value as f32
+ };
let r_val = self.slider_r.value();
let g_val = self.slider_g.value();
let b_val = self.slider_b.value();
+
+ let family_str = match self.dropdown_font_family.selected {
+ 1 => "sans-serif",
+ 2 => "serif",
+ _ => "monospace",
+ }.to_string();
match &mut self.elements[idx] {
- Element::Text { text, x, y, w, h, font_size, color } => {
+ Element::Text { text, x, y, w, h, font_size, color, font_family } => {
*text = text_val;
*x = x_val;
*y = y_val;
@@ -518,6 +585,7 @@ impl LayoutApp {
*h = h_val;
*font_size = size_val;
*color = [r_val, g_val, b_val, 1.0];
+ *font_family = family_str;
}
Element::Shape { shape_type: _, x, y, w, h, color } => {
*x = x_val;
@@ -543,8 +611,9 @@ impl Application for LayoutApp {
.with_item("View", &["Toggle Grid"]);
// Build the sidebar Paginator
- let paginator = Paginator::new(80.0, vec!["Page".to_string(), "Element".to_string(), "Canvas".to_string(), "View".to_string()])
- .with_tabs_at_top(true);
+ let paginator = Paginator::new(56.0, vec!["Page".to_string(), "Element".to_string(), "Canvas".to_string(), "View".to_string()])
+ .with_tabs_at_top(false)
+ .with_tabs_rotated(true);
// Default paper sheet sizing (Letter)
let page_w = 510.0;
@@ -560,6 +629,7 @@ impl Application for LayoutApp {
h: 40.0,
font_size: 20.0,
color: [0.3, 0.7, 1.0, 1.0],
+ font_family: "monospace".to_string(),
},
Element::Shape {
shape_type: ShapeType::Rectangle,
@@ -577,6 +647,7 @@ impl Application for LayoutApp {
h: 30.0,
font_size: 14.0,
color: [0.8, 0.8, 0.85, 1.0],
+ font_family: "monospace".to_string(),
},
Element::Shape {
shape_type: ShapeType::Rectangle,
@@ -594,43 +665,100 @@ impl Application for LayoutApp {
h: 20.0,
font_size: 11.0,
color: [0.9, 0.9, 0.9, 1.0],
+ font_family: "monospace".to_string(),
},
];
// Create sidebar dropdown for page size presets
- let dropdown_presets = Dropdown::new(
+ let mut dropdown_presets = Dropdown::new(
PRESETS.iter().map(|p| p.name.to_string()).collect(),
0
).with_label("Page Size Preset");
+ dropdown_presets.set_rect(0.0, 0.0, 240.0, 26.0);
- let page_color_selector = ColorSelector::new([245, 245, 250])
+ let mut slider_page_x = Slider::new().with_range(100.0, 1500.0).with_value((page_w - 100.0) / (1500.0 - 100.0)).with_readout(true).with_label("Page Width (X)");
+ slider_page_x.set_rect(0.0, 0.0, 240.0, 18.0);
+ let mut slider_page_y = Slider::new().with_range(100.0, 1500.0).with_value((page_h - 100.0) / (1500.0 - 100.0)).with_readout(true).with_label("Page Height (Y)");
+ slider_page_y.set_rect(0.0, 0.0, 240.0, 18.0);
+
+ let mut page_color_selector = ColorSelector::new([245, 245, 250])
.with_label("Page Color");
+ page_color_selector.set_rect(0.0, 0.0, 240.0, 26.0);
let page_color = [0.96, 0.96, 0.98, 1.0];
+ let mut toggle_margin = Toggle::new().with_label("Show Page Margin");
+ toggle_margin.set_rect(0.0, 0.0, 240.0, 26.0);
+
+ let mut toggle_word_processor = Toggle::new().with_label("Word Processor");
+ toggle_word_processor.set_rect(0.0, 0.0, 240.0, 26.0);
+
+ let wp_text_box = TextBox::new(String::new())
+ .with_multiline(true)
+ .with_draw_bg_border(false)
+ .with_text_color(Some([0x1a, 0x1a, 0x24]))
+ .with_max_width(None);
+
+ let mut dropdown_font_family = Dropdown::new(
+ vec![
+ "Monospace".to_string(),
+ "Sans-Serif".to_string(),
+ "Serif".to_string(),
+ ],
+ 0,
+ ).with_label("Font Family");
+ dropdown_font_family.set_rect(0.0, 0.0, 240.0, 26.0);
+
+ let mut dropdown_margin_units = Dropdown::new(
+ vec![
+ "Pixels (px)".to_string(),
+ "Inches (in)".to_string(),
+ "Centimeters (cm)".to_string(),
+ "Millimeters (mm)".to_string(),
+ ],
+ 0,
+ ).with_label("Units");
+ dropdown_margin_units.set_rect(0.0, 0.0, 240.0, 26.0);
+
+ let mut slider_margin_x = Slider::new().with_range(0.0, 100.0).with_value(0.2).with_readout(true).with_label("Margin Width (X)");
+ slider_margin_x.set_rect(0.0, 0.0, 240.0, 18.0);
+ let mut slider_margin_y = Slider::new().with_range(0.0, 100.0).with_value(0.2).with_readout(true).with_label("Margin Height (Y)");
+ slider_margin_y.set_rect(0.0, 0.0, 240.0, 18.0);
+
// Create sidebar textbox inputs
- let sidebar_x = TextBox::new(String::new()).with_label("X Position");
- let sidebar_y = TextBox::new(String::new()).with_label("Y Position");
- let sidebar_w = TextBox::new(String::new()).with_label("Width");
- let sidebar_h = TextBox::new(String::new()).with_label("Height");
- let sidebar_text = TextBox::new(String::new()).with_label("Text Content");
- let sidebar_size = TextBox::new(String::new()).with_label("Text Size");
-
- // Create color sliders
- let slider_r = Slider::new().with_range(0.0, 1.0).with_value(0.5);
- let slider_g = Slider::new().with_range(0.0, 1.0).with_value(0.5);
- let slider_b = Slider::new().with_range(0.0, 1.0).with_value(0.5);
- let slider_zoom = Slider::new().with_range(0.5, 2.0).with_value(1.0).with_scroll(true);
+ let mut sidebar_x = TextBox::new(String::new()).with_label("X Position");
+ sidebar_x.set_rect(0.0, 0.0, 110.0, 26.0);
+ let mut sidebar_y = TextBox::new(String::new()).with_label("Y Position");
+ sidebar_y.set_rect(0.0, 0.0, 110.0, 26.0);
+ let mut sidebar_w = TextBox::new(String::new()).with_label("Width");
+ sidebar_w.set_rect(0.0, 0.0, 110.0, 26.0);
+ let mut sidebar_h = TextBox::new(String::new()).with_label("Height");
+ sidebar_h.set_rect(0.0, 0.0, 110.0, 26.0);
+ let mut sidebar_text = TextBox::new(String::new()).with_label("Text Content");
+ sidebar_text.set_rect(0.0, 0.0, 240.0, 26.0);
+ let mut sidebar_size = Spinbox::new(14, 4, 120, 1).with_label("Text Size");
+ sidebar_size.set_rect(0.0, 0.0, 240.0, 26.0);
+
+ // Create color sliders with readout
+ let mut slider_r = Slider::new().with_range(0.0, 1.0).with_value(0.5).with_readout(true).with_label("Red Color");
+ slider_r.set_rect(0.0, 0.0, 240.0, 18.0);
+ let mut slider_g = Slider::new().with_range(0.0, 1.0).with_value(0.5).with_readout(true).with_label("Green Color");
+ slider_g.set_rect(0.0, 0.0, 240.0, 18.0);
+ let mut slider_b = Slider::new().with_range(0.0, 1.0).with_value(0.5).with_readout(true).with_label("Blue Color");
+ slider_b.set_rect(0.0, 0.0, 240.0, 18.0);
+ let mut slider_zoom = Slider::new().with_range(0.5, 2.0).with_value((1.0 - 0.5) / (2.0 - 0.5)).with_scroll(true).with_readout(true).with_label("Zoom");
+ slider_zoom.set_rect(0.0, 0.0, 240.0, 18.0);
// Page 1 Buttons
- let btn_toggle_grid = Button::new(0.0, 0.0, 0.0, 0.0).with_label("Toggle Grid");
- let btn_clear_canvas = Button::new(0.0, 0.0, 0.0, 0.0).with_label("Clear Canvas");
- let btn_add_text = Button::new(0.0, 0.0, 0.0, 0.0).with_label("Add Text Box");
- let btn_add_rect = Button::new(0.0, 0.0, 0.0, 0.0).with_label("Add Rectangle");
- let btn_add_banner = Button::new(0.0, 0.0, 0.0, 0.0).with_label("Add Banner");
+ let btn_toggle_grid = Button::new(0.0, 0.0, 240.0, 26.0).with_label("Toggle Grid");
+ let btn_clear_canvas = Button::new(0.0, 0.0, 240.0, 26.0).with_label("Clear Canvas");
+ let btn_add_text = Button::new(0.0, 0.0, 240.0, 26.0).with_label("Add Text Box");
+ let btn_add_rect = Button::new(0.0, 0.0, 240.0, 26.0).with_label("Add Rectangle");
+ let btn_add_banner = Button::new(0.0, 0.0, 240.0, 26.0).with_label("Add Banner");
// Page 3 Buttons
- let toggle_rulers = Toggle::new().with_label("Show Rulers");
- let dropdown_units = Dropdown::new(
+ let mut toggle_rulers = Toggle::new().with_label("Show Rulers");
+ toggle_rulers.set_rect(0.0, 0.0, 240.0, 26.0);
+ let mut dropdown_units = Dropdown::new(
vec![
"Pixels (px)".to_string(),
"Inches (in)".to_string(),
@@ -639,6 +767,31 @@ impl Application for LayoutApp {
],
0,
).with_label("Ruler Units");
+ dropdown_units.set_rect(0.0, 0.0, 240.0, 26.0);
+
+ // Setup the new Label and SectionHeader widgets
+ let mut section_rulers = SectionHeader::new("RULERS");
+ section_rulers.set_rect(0.0, 0.0, 240.0, 26.0);
+
+ let mut label_width = Label::new("Width: 510 px");
+ label_width.set_rect(0.0, 0.0, 240.0, 18.0);
+ let mut label_height = Label::new("Height: 660 px");
+ label_height.set_rect(0.0, 0.0, 240.0, 18.0);
+
+ let mut label_sel_status = Label::new("No Selection").with_color([0x83, 0x83, 0x8a]);
+ label_sel_status.set_rect(0.0, 0.0, 240.0, 18.0);
+
+ let mut label_sel_desc1 = Label::new("Click canvas elements").with_color([0x60, 0x60, 0x68]).with_font_size(10.0);
+ label_sel_desc1.set_rect(0.0, 0.0, 240.0, 14.0);
+
+ let mut label_sel_desc2 = Label::new("to edit properties.").with_color([0x60, 0x60, 0x68]).with_font_size(10.0);
+ label_sel_desc2.set_rect(0.0, 0.0, 240.0, 14.0);
+
+ let mut label_grid_snap = Label::new("Grid Snapping: ON (20px)");
+ label_grid_snap.set_rect(0.0, 0.0, 240.0, 18.0);
+
+ let mut label_total_elements = Label::new("Total Elements: 5");
+ label_total_elements.set_rect(0.0, 0.0, 240.0, 18.0);
let mut app = Self {
menu_bar,
@@ -655,20 +808,34 @@ impl Application for LayoutApp {
needs_rebuild: true,
dropdown_presets,
+ slider_page_x,
+ slider_page_y,
page_color_selector,
page_color,
+ toggle_margin,
+ dropdown_margin_units,
+ slider_margin_x,
+ slider_margin_y,
+ margin_enabled: false,
+ margin_x: 20.0,
+ margin_y: 20.0,
+ toggle_word_processor,
+ word_processor_enabled: false,
+ wp_text_box,
sidebar_x,
sidebar_y,
sidebar_w,
sidebar_h,
sidebar_text,
sidebar_size,
+ dropdown_font_family,
+ wp_base_font_size: 14.0,
slider_r,
slider_g,
slider_b,
slider_zoom,
-
+
btn_toggle_grid,
btn_clear_canvas,
btn_add_text,
@@ -676,15 +843,27 @@ impl Application for LayoutApp {
btn_add_banner,
toggle_rulers,
dropdown_units,
+
+ section_rulers,
+ label_width,
+ label_height,
+ label_sel_status,
+ label_sel_desc1,
+ label_sel_desc2,
+ label_grid_snap,
+ label_total_elements,
last_selected: None,
page_w,
page_h,
pan_x: 0.0,
pan_y: 0.0,
+ widgets_registered: false,
};
+ app.rebuild_element_tab_widgets();
app.sync_sidebar_fields();
+ app.sync_page_units();
app.rebuild_text_items();
app
}
@@ -713,6 +892,29 @@ impl Application for LayoutApp {
self.pan_y = 0.0;
self.page_color = [0.96, 0.96, 0.98, 1.0];
self.page_color_selector.color = [245, 245, 250];
+ self.margin_enabled = false;
+ self.margin_x = 20.0;
+ self.margin_y = 20.0;
+ self.toggle_margin.set_toggled(false);
+
+ self.dropdown_presets.selected = 0;
+ self.page_w = PRESETS[0].w;
+ self.page_h = PRESETS[0].h;
+ let val_x = (self.page_w - 100.0) / (1500.0 - 100.0);
+ self.slider_page_x.set_value(val_x);
+ let val_y = (self.page_h - 100.0) / (1500.0 - 100.0);
+ self.slider_page_y.set_value(val_y);
+
+ self.dropdown_margin_units.selected = 0;
+ self.sync_page_units();
+
+ self.toggle_word_processor.set_toggled(false);
+ self.word_processor_enabled = false;
+ self.wp_text_box.text = String::new();
+ self.wp_text_box.edit_buffer = String::new();
+ self.wp_text_box.editing = false;
+
+ self.rebuild_element_tab_widgets();
self.sync_sidebar_fields();
*needs_rebuild = true;
self.needs_rebuild = true;
@@ -727,8 +929,10 @@ impl Application for LayoutApp {
h: 30.0,
font_size: 14.0,
color: [0.0, 0.0, 0.0, 1.0],
+ font_family: "monospace".to_string(),
});
self.selected_idx = Some(next_idx);
+ self.rebuild_element_tab_widgets();
self.sync_sidebar_fields();
*needs_rebuild = true;
self.needs_rebuild = true;
@@ -744,6 +948,7 @@ impl Application for LayoutApp {
color: [0.2, 0.6, 0.4, 1.0],
});
self.selected_idx = Some(next_idx);
+ self.rebuild_element_tab_widgets();
self.sync_sidebar_fields();
*needs_rebuild = true;
self.needs_rebuild = true;
@@ -759,6 +964,7 @@ impl Application for LayoutApp {
color: [0.8, 0.4, 0.2, 1.0],
});
self.selected_idx = Some(next_idx);
+ self.rebuild_element_tab_widgets();
self.sync_sidebar_fields();
*needs_rebuild = true;
self.needs_rebuild = true;
@@ -786,11 +992,138 @@ impl Application for LayoutApp {
*needs_rebuild = true;
self.needs_rebuild = true;
}
+
+ // Preset dropdown selection change
+ if self.dropdown_presets.take_change() {
+ let sel = self.dropdown_presets.selected;
+ if sel < PRESETS.len() - 1 {
+ self.page_w = PRESETS[sel].w;
+ self.page_h = PRESETS[sel].h;
+ let val_x = (self.page_w - 100.0) / (1500.0 - 100.0);
+ self.slider_page_x.set_value(val_x);
+ let val_y = (self.page_h - 100.0) / (1500.0 - 100.0);
+ self.slider_page_y.set_value(val_y);
+ self.pan_x = 0.0;
+ self.pan_y = 0.0;
+ *needs_rebuild = true;
+ self.needs_rebuild = true;
+ }
+ }
+
+ // Margin units dropdown change
+ if self.dropdown_margin_units.take_change() {
+ self.sync_page_units();
+ *needs_rebuild = true;
+ self.needs_rebuild = true;
+ }
+
+ // Ruler units dropdown change
+ if self.dropdown_units.take_change() {
+ *needs_rebuild = true;
+ self.needs_rebuild = true;
+ }
+
+ let unit_idx = self.dropdown_margin_units.selected;
+ let px_per_unit = match unit_idx {
+ 0 => 1.0, // Pixels
+ 1 => 60.0, // Inches
+ 2 => 23.622047, // Centimeters
+ 3 => 2.3622047, // Millimeters
+ _ => 1.0,
+ };
+
+ // Page width slider change
+ let page_w = self.slider_page_x.get_scaled_value() * px_per_unit;
+ if (self.page_w - page_w).abs() > 0.01 {
+ self.page_w = page_w;
+ self.dropdown_presets.selected = PRESETS.len() - 1; // "Custom"
+ *needs_rebuild = true;
+ self.needs_rebuild = true;
+ }
+
+ // Page height slider change
+ let page_h = self.slider_page_y.get_scaled_value() * px_per_unit;
+ if (self.page_h - page_h).abs() > 0.01 {
+ self.page_h = page_h;
+ self.dropdown_presets.selected = PRESETS.len() - 1; // "Custom"
+ *needs_rebuild = true;
+ self.needs_rebuild = true;
+ }
+
+ let margin_enabled = self.toggle_margin.toggled();
+ if self.margin_enabled != margin_enabled {
+ self.margin_enabled = margin_enabled;
+ *needs_rebuild = true;
+ self.needs_rebuild = true;
+ }
+
+ let margin_x = self.slider_margin_x.get_scaled_value() * px_per_unit;
+ if (self.margin_x - margin_x).abs() > 0.01 {
+ self.margin_x = margin_x;
+ *needs_rebuild = true;
+ self.needs_rebuild = true;
+ }
+
+ let margin_y = self.slider_margin_y.get_scaled_value() * px_per_unit;
+ if (self.margin_y - margin_y).abs() > 0.01 {
+ self.margin_y = margin_y;
+ *needs_rebuild = true;
+ self.needs_rebuild = true;
+ }
+
+ let wp_enabled = self.toggle_word_processor.toggled();
+ if self.word_processor_enabled != wp_enabled {
+ self.word_processor_enabled = wp_enabled;
+ if wp_enabled {
+ self.wp_text_box.focus();
+ self.selected_idx = None;
+ self.dragging = None;
+ } else {
+ self.wp_text_box.unfocus();
+ }
+ self.rebuild_element_tab_widgets();
+ self.sync_sidebar_fields();
+ *needs_rebuild = true;
+ self.needs_rebuild = true;
+ }
}
fn view(&mut self, quads: &mut Vec<(f32, f32, f32, f32, [f32; 4])>, size: LogicalSize, scale: f64) {
let size_changed = self.width != size.width as u32 || self.height != size.height as u32 || self.scale_factor != scale;
if self.needs_rebuild || size_changed {
+ if !self.widgets_registered {
+ let self_ptr = self as *mut Self;
+ unsafe {
+ (*self_ptr).paginator.add_widget_to_page(0, &mut (*self_ptr).dropdown_margin_units as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(0, &mut (*self_ptr).dropdown_presets as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(0, &mut (*self_ptr).slider_page_x as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(0, &mut (*self_ptr).slider_page_y as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(0, &mut (*self_ptr).page_color_selector as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(0, &mut (*self_ptr).toggle_margin as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(0, &mut (*self_ptr).toggle_word_processor as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(0, &mut (*self_ptr).slider_margin_x as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(0, &mut (*self_ptr).slider_margin_y as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(0, &mut (*self_ptr).label_width as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(0, &mut (*self_ptr).label_height as *mut (dyn Widget + 'static));
+
+ // Page 1 (Element) widgets are registered dynamically via rebuild_element_tab_widgets()
+
+ (*self_ptr).paginator.add_widget_to_page(2, &mut (*self_ptr).btn_toggle_grid as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(2, &mut (*self_ptr).btn_clear_canvas as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(2, &mut (*self_ptr).btn_add_text as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(2, &mut (*self_ptr).btn_add_rect as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(2, &mut (*self_ptr).btn_add_banner as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(2, &mut (*self_ptr).label_grid_snap as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(2, &mut (*self_ptr).label_total_elements as *mut (dyn Widget + 'static));
+
+ (*self_ptr).paginator.add_widget_to_page(3, &mut (*self_ptr).slider_zoom as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(3, &mut (*self_ptr).section_rulers as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(3, &mut (*self_ptr).toggle_rulers as *mut (dyn Widget + 'static));
+ (*self_ptr).paginator.add_widget_to_page(3, &mut (*self_ptr).dropdown_units as *mut (dyn Widget + 'static));
+ }
+ self.widgets_registered = true;
+ }
+
self.width = size.width as u32;
self.height = size.height as u32;
self.scale_factor = scale;
@@ -801,37 +1134,6 @@ impl Application for LayoutApp {
// Set paginator bounds in the sidebar region
self.paginator.set_rect(0.0, 26.0, 280.0, size.height - 26.0);
- // Layout presets dropdown selector (shifted up to y=90.0)
- self.dropdown_presets.set_rect(20.0, 90.0, 240.0, 26.0);
- self.page_color_selector.set_rect(20.0, 200.0, 240.0, 26.0);
-
- // Position Layout properties control widgets (widened to 240px and realigned below top tabs)
- self.sidebar_x.set_rect(20.0, 90.0, 110.0, 26.0);
- self.sidebar_y.set_rect(150.0, 90.0, 110.0, 26.0);
- self.sidebar_w.set_rect(20.0, 146.0, 110.0, 26.0);
- self.sidebar_h.set_rect(150.0, 146.0, 110.0, 26.0);
- self.sidebar_text.set_rect(20.0, 202.0, 240.0, 26.0);
- self.sidebar_size.set_rect(20.0, 258.0, 240.0, 26.0);
-
- self.slider_r.set_rect(20.0, 330.0, 240.0, 18.0);
- self.slider_g.set_rect(20.0, 370.0, 240.0, 18.0);
- self.slider_b.set_rect(20.0, 410.0, 240.0, 18.0);
- self.slider_zoom.set_rect(20.0, 110.0, 240.0, 18.0);
-
- // Canvas Page buttons (shifted up by 20px)
- self.btn_toggle_grid.set_rect(20.0, 90.0, 240.0, 26.0);
- self.btn_clear_canvas.set_rect(20.0, 140.0, 240.0, 26.0);
- self.btn_add_text.set_rect(20.0, 190.0, 240.0, 26.0);
- self.btn_add_rect.set_rect(20.0, 240.0, 240.0, 26.0);
- self.btn_add_banner.set_rect(20.0, 290.0, 240.0, 26.0);
-
- // View Page Rulers Section
- let mut collector = clear_ui::layout::PopoverCollector::new();
- let mut sec = clear_ui::layout::Section::new(&mut collector, 15.0, 155.0, 250.0, "RULERS");
- sec.widget(&mut collector, &mut self.toggle_rulers, 10.0, 230.0, 26.0);
- sec.widget(&mut collector, &mut self.dropdown_units, 10.0, 230.0, 26.0);
- let _ = sec.finish(&mut collector);
-
self.rebuild_text_items();
self.needs_rebuild = false;
}
@@ -842,85 +1144,6 @@ impl Application for LayoutApp {
quads.push(hq);
}
- // 2. Render Page Content
- if self.paginator.selected_page() == 0 {
- // Render Page 0: Presets Dropdown (shifted up to y=90)
- quads.push((20.0, 90.0, 240.0, 26.0, self.dropdown_presets.color()));
- quads.extend(self.dropdown_presets.extra_quads());
-
- // Render Page 0: Page Color Selector
- let (cx, cy, cw, ch) = self.page_color_selector.rect();
- // We can draw a background for the text input region, similar to other text boxes:
- let bg_color = if self.page_color_selector.editing {
- [0.06, 0.10, 0.18, 1.0]
- } else {
- [0.08, 0.08, 0.12, 1.0]
- };
- let border_color = if self.page_color_selector.editing {
- [0.20, 0.50, 0.85, 1.0]
- } else if self.page_color_selector.base().map_or(false, |b| b.hovered) {
- [0.25, 0.25, 0.35, 1.0]
- } else {
- [0.18, 0.18, 0.24, 1.0]
- };
- quads.push((cx, cy, cw * 0.65, ch, border_color));
- quads.push((cx + 1.0, cy + 1.0, cw * 0.65 - 2.0, ch - 2.0, bg_color));
-
- quads.extend(self.page_color_selector.extra_quads());
- } else if self.paginator.selected_page() == 1 {
- // Render Page 1: Element Properties
- quads.extend(self.sidebar_x.extra_quads());
- quads.extend(self.sidebar_y.extra_quads());
- quads.extend(self.sidebar_w.extra_quads());
- quads.extend(self.sidebar_h.extra_quads());
- quads.extend(self.sidebar_text.extra_quads());
- quads.extend(self.sidebar_size.extra_quads());
-
- let (rx, ry, rw, rh) = self.slider_r.rect();
- quads.push((rx, ry, rw, rh, self.slider_r.color()));
- quads.extend(self.slider_r.extra_quads());
-
- let (gx, gy, gw, gh) = self.slider_g.rect();
- quads.push((gx, gy, gw, gh, self.slider_g.color()));
- quads.extend(self.slider_g.extra_quads());
-
- let (bx, by, bw, bh) = self.slider_b.rect();
- quads.push((bx, by, bw, bh, self.slider_b.color()));
- quads.extend(self.slider_b.extra_quads());
- } else if self.paginator.selected_page() == 2 {
- // Render Page 2: Canvas settings (shifted up by 20px)
- quads.push((20.0, 90.0, 240.0, 26.0, self.btn_toggle_grid.color()));
- quads.extend(self.btn_toggle_grid.extra_quads());
-
- quads.push((20.0, 140.0, 240.0, 26.0, self.btn_clear_canvas.color()));
- quads.extend(self.btn_clear_canvas.extra_quads());
-
- quads.push((20.0, 190.0, 240.0, 26.0, self.btn_add_text.color()));
- quads.extend(self.btn_add_text.extra_quads());
-
- quads.push((20.0, 240.0, 240.0, 26.0, self.btn_add_rect.color()));
- quads.extend(self.btn_add_rect.extra_quads());
-
- quads.push((20.0, 290.0, 240.0, 26.0, self.btn_add_banner.color()));
- quads.extend(self.btn_add_banner.extra_quads());
- } else {
- // Render Page 3: View settings
- let (zx, zy, zw, zh) = self.slider_zoom.rect();
- quads.push((zx, zy, zw, zh, self.slider_zoom.color()));
- quads.extend(self.slider_zoom.extra_quads());
-
- // "Rulers" Section Container using Section layout helper
- let mut collector = clear_ui::layout::PopoverCollector::new();
- let mut sec = clear_ui::layout::Section::new(&mut collector, 15.0, 155.0, 250.0, "RULERS");
- sec.widget(&mut collector, &mut self.toggle_rulers, 10.0, 230.0, 26.0);
- sec.widget(&mut collector, &mut self.dropdown_units, 10.0, 230.0, 26.0);
- let _ = sec.finish(&mut collector);
-
- for (color, x, y, w, h) in collector.rects {
- quads.push((x, y, w, h, color));
- }
- }
-
// Divider between Sidebar and Canvas
quads.push((280.0, 26.0, 1.0, self.height as f32 - 26.0, [0.20, 0.20, 0.25, 1.0]));
@@ -1006,6 +1229,21 @@ impl Application for LayoutApp {
quads.push((page_x, page_y, 1.0, self.page_h * zoom, border_col));
quads.push((page_x + self.page_w * zoom - 1.0, page_y, 1.0, self.page_h * zoom, border_col));
+ // Draw page margin inside the paper sheet if enabled
+ if self.margin_enabled {
+ let mx = page_x + self.margin_x * zoom;
+ let my = page_y + self.margin_y * zoom;
+ let mw = (self.page_w - 2.0 * self.margin_x) * zoom;
+ let mh = (self.page_h - 2.0 * self.margin_y) * zoom;
+ if mw > 0.0 && mh > 0.0 {
+ let margin_col = [0.20, 0.50, 0.85, 0.35]; // Translucent blue margin line
+ quads.push((mx, my, mw, 1.0, margin_col));
+ quads.push((mx, my + mh - 1.0, mw, 1.0, margin_col));
+ quads.push((mx, my, 1.0, mh, margin_col));
+ quads.push((mx + mw - 1.0, my, 1.0, mh, margin_col));
+ }
+ }
+
// 4. Snapping Grid inside the paper bounds
if self.grid_enabled {
let spacing = 20.0 * zoom;
@@ -1021,85 +1259,87 @@ impl Application for LayoutApp {
}
}
- // 5. Render Layout Elements (drawn relative to the paper sheet)
- for (idx, element) in self.elements.iter().enumerate() {
- match element {
- Element::Text { text: _, x, y, w, h, font_size: _, color: _ } => {
- // Translucent text block container overlay
- quads.push((page_x + *x * zoom, page_y + *y * zoom, *w * zoom, *h * zoom, [0.20, 0.30, 0.70, 0.05]));
-
- let border_color = [0.45, 0.45, 0.55, 0.22];
- quads.push((page_x + *x * zoom, page_y + *y * zoom, *w * zoom, 1.0, border_color));
- quads.push((page_x + *x * zoom, page_y + *y * zoom + *h * zoom - 1.0, *w * zoom, 1.0, border_color));
- quads.push((page_x + *x * zoom, page_y + *y * zoom, 1.0, *h * zoom, border_color));
- quads.push((page_x + *x * zoom + *w * zoom - 1.0, page_y + *y * zoom, 1.0, *h * zoom, border_color));
- }
- Element::Shape { shape_type, x, y, w, h, color } => {
- match shape_type {
- ShapeType::Rectangle => {
- quads.push((page_x + *x * zoom, page_y + *y * zoom, *w * zoom, *h * zoom, *color));
- }
- ShapeType::Banner => {
- // Banner drop shadow
- quads.push((page_x + *x * zoom + 3.0, page_y + *y * zoom + 3.0, *w * zoom, *h * zoom, [0.05, 0.05, 0.08, 0.3]));
-
- quads.push((page_x + *x * zoom, page_y + *y * zoom, *w * zoom, *h * zoom, *color));
-
- let inner_col = [1.0, 1.0, 1.0, 0.2];
- quads.push((page_x + *x * zoom + 2.0, page_y + *y * zoom + 2.0, *w * zoom - 4.0, 1.0, inner_col));
- quads.push((page_x + *x * zoom + 2.0, page_y + *y * zoom + *h * zoom - 3.0, *w * zoom - 4.0, 1.0, inner_col));
- quads.push((page_x + *x * zoom + 2.0, page_y + *y * zoom + 2.0, 1.0, *h * zoom - 4.0, inner_col));
- quads.push((page_x + *x * zoom + *w * zoom - 3.0, page_y + *y * zoom + 2.0, 1.0, *h * zoom - 4.0, inner_col));
+ // 5. Render Layout Elements or Word Processor
+ if self.word_processor_enabled {
+ let mx = page_x + self.margin_x * zoom;
+ let my = page_y + self.margin_y * zoom;
+ let mw = (self.page_w - 2.0 * self.margin_x) * zoom;
+ let mh = (self.page_h - 2.0 * self.margin_y) * zoom;
+ if mw > 0.0 && mh > 0.0 {
+ self.wp_text_box.set_rect(mx, my, mw, mh);
+ self.wp_text_box.font_size = self.wp_base_font_size * zoom;
+ quads.extend(self.wp_text_box.extra_quads());
+ }
+ } else {
+ for (idx, element) in self.elements.iter().enumerate() {
+ match element {
+ Element::Text { text: _, x, y, w, h, font_size: _, color: _, .. } => {
+ // Translucent text block container overlay
+ quads.push((page_x + *x * zoom, page_y + *y * zoom, *w * zoom, *h * zoom, [0.20, 0.30, 0.70, 0.05]));
+
+ let border_color = [0.45, 0.45, 0.55, 0.22];
+ quads.push((page_x + *x * zoom, page_y + *y * zoom, *w * zoom, 1.0, border_color));
+ quads.push((page_x + *x * zoom, page_y + *y * zoom + *h * zoom - 1.0, *w * zoom, 1.0, border_color));
+ quads.push((page_x + *x * zoom, page_y + *y * zoom, 1.0, *h * zoom, border_color));
+ quads.push((page_x + *x * zoom + *w * zoom - 1.0, page_y + *y * zoom, 1.0, *h * zoom, border_color));
+ }
+ Element::Shape { shape_type, x, y, w, h, color } => {
+ match shape_type {
+ ShapeType::Rectangle => {
+ quads.push((page_x + *x * zoom, page_y + *y * zoom, *w * zoom, *h * zoom, *color));
+ }
+ ShapeType::Banner => {
+ // Banner drop shadow
+ quads.push((page_x + *x * zoom + 3.0, page_y + *y * zoom + 3.0, *w * zoom, *h * zoom, [0.05, 0.05, 0.08, 0.3]));
+
+ quads.push((page_x + *x * zoom, page_y + *y * zoom, *w * zoom, *h * zoom, *color));
+
+ let inner_col = [1.0, 1.0, 1.0, 0.2];
+ quads.push((page_x + *x * zoom + 2.0, page_y + *y * zoom + 2.0, *w * zoom - 4.0, 1.0, inner_col));
+ quads.push((page_x + *x * zoom + 2.0, page_y + *y * zoom + *h * zoom - 3.0, *w * zoom - 4.0, 1.0, inner_col));
+ quads.push((page_x + *x * zoom + 2.0, page_y + *y * zoom + 2.0, 1.0, *h * zoom - 4.0, inner_col));
+ quads.push((page_x + *x * zoom + *w * zoom - 3.0, page_y + *y * zoom + 2.0, 1.0, *h * zoom - 4.0, inner_col));
+ }
}
}
}
- }
- // Draw highlight border if selected
- if self.selected_idx == Some(idx) {
- let (ex, ey, ew, eh) = match element {
- Element::Text { x, y, w, h, .. } => (*x, *y, *w, *h),
- Element::Shape { x, y, w, h, .. } => (*x, *y, *w, *h),
- };
- let sel_col = [0.15, 0.45, 0.90, 1.0]; // Bright neon blue
- quads.push((page_x + ex * zoom - 1.0, page_y + ey * zoom - 1.0, ew * zoom + 2.0, 1.0, sel_col));
- quads.push((page_x + ex * zoom - 1.0, page_y + ey * zoom + eh * zoom, ew * zoom + 2.0, 1.0, sel_col));
- quads.push((page_x + ex * zoom - 1.0, page_y + ey * zoom - 1.0, 1.0, eh * zoom + 2.0, sel_col));
- quads.push((page_x + ex * zoom + ew * zoom, page_y + ey * zoom - 1.0, 1.0, eh * zoom + 2.0, sel_col));
-
- // Corner handles
- let hs = 5.0;
- let hc = [1.0, 1.0, 1.0, 1.0];
- let hborder = [0.15, 0.45, 0.90, 1.0];
- let handles = [
- (page_x + ex * zoom - 2.0, page_y + ey * zoom - 2.0),
- (page_x + ex * zoom + ew * zoom - 3.0, page_y + ey * zoom - 2.0),
- (page_x + ex * zoom - 2.0, page_y + ey * zoom + eh * zoom - 3.0),
- (page_x + ex * zoom + ew * zoom - 3.0, page_y + ey * zoom + eh * zoom - 3.0),
- ];
- for (hx, hy) in handles {
- quads.push((hx, hy, hs, hs, hborder));
- quads.push((hx + 1.0, hy + 1.0, hs - 2.0, hs - 2.0, hc));
+ // Draw highlight border if selected
+ if self.selected_idx == Some(idx) {
+ let (ex, ey, ew, eh) = match element {
+ Element::Text { x, y, w, h, .. } => (*x, *y, *w, *h),
+ Element::Shape { x, y, w, h, .. } => (*x, *y, *w, *h),
+ };
+ let sel_col = [0.15, 0.45, 0.90, 1.0]; // Bright neon blue
+ quads.push((page_x + ex * zoom - 1.0, page_y + ey * zoom - 1.0, ew * zoom + 2.0, 1.0, sel_col));
+ quads.push((page_x + ex * zoom - 1.0, page_y + ey * zoom + eh * zoom, ew * zoom + 2.0, 1.0, sel_col));
+ quads.push((page_x + ex * zoom - 1.0, page_y + ey * zoom - 1.0, 1.0, eh * zoom + 2.0, sel_col));
+ quads.push((page_x + ex * zoom + ew * zoom, page_y + ey * zoom - 1.0, 1.0, eh * zoom + 2.0, sel_col));
+
+ // Corner handles
+ let hs = 5.0;
+ let hc = [1.0, 1.0, 1.0, 1.0];
+ let hborder = [0.15, 0.45, 0.90, 1.0];
+ let handles = [
+ (page_x + ex * zoom - 2.0, page_y + ey * zoom - 2.0),
+ (page_x + ex * zoom + ew * zoom - 3.0, page_y + ey * zoom - 2.0),
+ (page_x + ex * zoom - 2.0, page_y + ey * zoom + eh * zoom - 3.0),
+ (page_x + ex * zoom + ew * zoom - 3.0, page_y + ey * zoom + eh * zoom - 3.0),
+ ];
+ for (hx, hy) in handles {
+ quads.push((hx, hy, hs, hs, hborder));
+ quads.push((hx + 1.0, hy + 1.0, hs - 2.0, hs - 2.0, hc));
+ }
}
}
}
- // Render Page presets drop down popover box (if open, overlaps other sidebar elements)
- if self.paginator.selected_page() == 0 && self.dropdown_presets.open {
- let mut collector = clear_ui::layout::PopoverCollector::new();
- self.dropdown_presets.render_popover(&mut collector);
- for (color, x, y, w, h) in collector.rects {
- quads.push((x, y, w, h, color));
- }
- }
-
- // Render Page units drop down popover box (if open, overlaps other sidebar elements)
- if self.paginator.selected_page() == 3 && self.dropdown_units.open {
- let mut collector = clear_ui::layout::PopoverCollector::new();
- self.dropdown_units.render_popover(&mut collector);
- for (color, x, y, w, h) in collector.rects {
- quads.push((x, y, w, h, color));
- }
+ // Render Paginator active page popovers (e.g. presets and units dropdowns)
+ // This is done near the end of view() to ensure popovers overlap canvas elements
+ let mut collector = clear_ui::layout::PopoverCollector::new();
+ self.paginator.render_popover(&mut collector);
+ for (color, x, y, w, h) in collector.rects {
+ quads.push((x, y, w, h, color));
}
// 6. MenuBar Background
@@ -1133,56 +1373,8 @@ impl Application for LayoutApp {
let sidebar_w = 280.0;
if px < sidebar_w {
if self.paginator.on_cursor_moved(px, py) { changed = true; }
-
- if self.paginator.selected_page() == 0 {
- // Check dropdown presets moves first
- if self.dropdown_presets.open || self.dropdown_presets.hit_test(px, py) {
- if self.dropdown_presets.on_cursor_moved(px, py) {
- changed = true;
- }
- }
- if !self.dropdown_presets.open {
- if self.page_color_selector.on_cursor_moved(px, py) {
- changed = true;
- }
- }
- } else if self.paginator.selected_page() == 1 {
- if self.sidebar_x.on_cursor_moved(px, py) { changed = true; }
- if self.sidebar_y.on_cursor_moved(px, py) { changed = true; }
- if self.sidebar_w.on_cursor_moved(px, py) { changed = true; }
- if self.sidebar_h.on_cursor_moved(px, py) { changed = true; }
- if self.sidebar_text.on_cursor_moved(px, py) { changed = true; }
- if self.sidebar_size.on_cursor_moved(px, py) { changed = true; }
-
- if self.slider_r.is_dragging() {
- if self.slider_r.drag_update(px, py) { changed = true; }
- } else if self.slider_r.on_cursor_moved(px, py) { changed = true; }
-
- if self.slider_g.is_dragging() {
- if self.slider_g.drag_update(px, py) { changed = true; }
- } else if self.slider_g.on_cursor_moved(px, py) { changed = true; }
-
- if self.slider_b.is_dragging() {
- if self.slider_b.drag_update(px, py) { changed = true; }
- } else if self.slider_b.on_cursor_moved(px, py) { changed = true; }
-
- if changed {
- self.apply_sidebar_changes();
- }
- } else if self.paginator.selected_page() == 2 {
- if self.btn_toggle_grid.on_cursor_moved(px, py) { changed = true; }
- if self.btn_clear_canvas.on_cursor_moved(px, py) { changed = true; }
- if self.btn_add_text.on_cursor_moved(px, py) { changed = true; }
- if self.btn_add_rect.on_cursor_moved(px, py) { changed = true; }
- if self.btn_add_banner.on_cursor_moved(px, py) { changed = true; }
- } else {
- if self.dropdown_units.open || self.dropdown_units.hit_test(px, py) {
- if self.dropdown_units.on_cursor_moved(px, py) { changed = true; }
- }
- if self.slider_zoom.is_dragging() {
- if self.slider_zoom.drag_update(px, py) { changed = true; }
- } else if self.slider_zoom.on_cursor_moved(px, py) { changed = true; }
- if self.toggle_rulers.on_cursor_moved(px, py) { changed = true; }
+ if self.paginator.selected_page() == 1 && changed {
+ self.apply_sidebar_changes();
}
} else {
// Compute centering coordinates for canvas elements
@@ -1197,7 +1389,11 @@ impl Application for LayoutApp {
let cx = (px - page_x) / zoom;
let cy = (py - page_y) / zoom;
- if let Some((idx, ox, oy)) = self.dragging {
+ if self.word_processor_enabled {
+ if self.wp_text_box.on_cursor_moved(px, py) {
+ changed = true;
+ }
+ } else if let Some((idx, ox, oy)) = self.dragging {
let mut new_x = cx - ox;
let mut new_y = cy - oy;
@@ -1263,147 +1459,49 @@ impl Application for LayoutApp {
changed = true;
if self.paginator.take_click() {
self.dropdown_presets.unfocus();
+ self.slider_page_x.unfocus();
+ self.slider_page_y.unfocus();
self.dropdown_units.unfocus();
self.page_color_selector.unfocus();
+ self.toggle_margin.unfocus();
+ self.toggle_word_processor.unfocus();
+ self.dropdown_margin_units.unfocus();
+ self.slider_margin_x.unfocus();
+ self.slider_margin_y.unfocus();
self.sidebar_x.unfocus();
self.sidebar_y.unfocus();
self.sidebar_w.unfocus();
self.sidebar_h.unfocus();
self.sidebar_text.unfocus();
self.sidebar_size.unfocus();
- }
- } else if self.paginator.selected_page() == 0 {
- if state == ElementState::Pressed {
- let mut clicked = false;
-
- // Check presets dropdown clicks first
- if self.dropdown_presets.open || self.dropdown_presets.hit_test(px, py) {
- if self.dropdown_presets.mouse_input(button, state, px, py) {
- clicked = true;
- if self.dropdown_presets.take_change() {
- let sel = self.dropdown_presets.selected;
- if sel < PRESETS.len() {
- self.page_w = PRESETS[sel].w;
- self.page_h = PRESETS[sel].h;
- self.pan_x = 0.0;
- self.pan_y = 0.0;
- }
- }
- }
- }
-
- if !self.dropdown_presets.hit_test(px, py) { self.dropdown_presets.unfocus(); }
-
- if !self.dropdown_presets.open {
- if self.page_color_selector.mouse_input(button, state, px, py) {
- clicked = true;
- }
- }
-
- if !self.page_color_selector.hit_test(px, py) { self.page_color_selector.unfocus(); }
-
- if clicked {
- changed = true;
- }
- }
- } else if self.paginator.selected_page() == 1 {
- if state == ElementState::Pressed {
- let mut clicked = false;
- if self.sidebar_x.mouse_input(button, state, px, py) { clicked = true; }
- if self.sidebar_y.mouse_input(button, state, px, py) { clicked = true; }
- if self.sidebar_w.mouse_input(button, state, px, py) { clicked = true; }
- if self.sidebar_h.mouse_input(button, state, px, py) { clicked = true; }
- if self.sidebar_text.mouse_input(button, state, px, py) { clicked = true; }
- if self.sidebar_size.mouse_input(button, state, px, py) { clicked = true; }
-
- if self.slider_r.mouse_input(button, state, px, py) { clicked = true; }
- if self.slider_g.mouse_input(button, state, px, py) { clicked = true; }
- if self.slider_b.mouse_input(button, state, px, py) { clicked = true; }
-
- if !self.sidebar_x.hit_test(px, py) { self.sidebar_x.unfocus(); }
- if !self.sidebar_y.hit_test(px, py) { self.sidebar_y.unfocus(); }
- if !self.sidebar_w.hit_test(px, py) { self.sidebar_w.unfocus(); }
- if !self.sidebar_h.hit_test(px, py) { self.sidebar_h.unfocus(); }
- if !self.sidebar_text.hit_test(px, py) { self.sidebar_text.unfocus(); }
- if !self.sidebar_size.hit_test(px, py) { self.sidebar_size.unfocus(); }
-
- if clicked {
- self.apply_sidebar_changes();
- changed = true;
- }
} else {
- self.slider_r.mouse_input(button, state, px, py);
- self.slider_g.mouse_input(button, state, px, py);
- self.slider_b.mouse_input(button, state, px, py);
- self.apply_sidebar_changes();
- changed = true;
- }
- } else if self.paginator.selected_page() == 2 {
- // Canvas settings page input
- if state == ElementState::Pressed {
- if self.btn_toggle_grid.mouse_input(button, state, px, py) { changed = true; }
- if self.btn_clear_canvas.mouse_input(button, state, px, py) { changed = true; }
- if self.btn_add_text.mouse_input(button, state, px, py) { changed = true; }
- if self.btn_add_rect.mouse_input(button, state, px, py) { changed = true; }
- if self.btn_add_banner.mouse_input(button, state, px, py) { changed = true; }
- } else if state == ElementState::Released {
- if self.btn_toggle_grid.mouse_input(button, state, px, py) {
- if self.btn_toggle_grid.take_click() {
- msg_out = Some(AppMessage::ToggleGrid);
- }
- changed = true;
- }
- if self.btn_clear_canvas.mouse_input(button, state, px, py) {
- if self.btn_clear_canvas.take_click() {
- msg_out = Some(AppMessage::NewDocument);
- }
- changed = true;
- }
- if self.btn_add_text.mouse_input(button, state, px, py) {
- if self.btn_add_text.take_click() {
- msg_out = Some(AppMessage::AddText);
- }
- changed = true;
- }
- if self.btn_add_rect.mouse_input(button, state, px, py) {
- if self.btn_add_rect.take_click() {
- msg_out = Some(AppMessage::AddRectangle);
- }
- changed = true;
- }
- if self.btn_add_banner.mouse_input(button, state, px, py) {
- if self.btn_add_banner.take_click() {
- msg_out = Some(AppMessage::AddBanner);
- }
- changed = true;
- }
- }
- } else {
- // View page input (zoom slider & rulers button & units dropdown)
- if state == ElementState::Pressed {
- let mut clicked = false;
- if self.dropdown_units.open || self.dropdown_units.hit_test(px, py) {
- if self.dropdown_units.mouse_input(button, state, px, py) {
- clicked = true;
- let _ = self.dropdown_units.take_change();
+ let active_page = self.paginator.selected_page();
+ if active_page == 0 {
+ let _ = self.toggle_margin.take_click();
+ let _ = self.toggle_word_processor.take_click();
+ } else if active_page == 1 {
+ self.apply_sidebar_changes();
+ } else if active_page == 2 {
+ if state == ElementState::Released {
+ if self.btn_toggle_grid.take_click() {
+ msg_out = Some(AppMessage::ToggleGrid);
+ }
+ if self.btn_clear_canvas.take_click() {
+ msg_out = Some(AppMessage::NewDocument);
+ }
+ if self.btn_add_text.take_click() {
+ msg_out = Some(AppMessage::AddText);
+ }
+ if self.btn_add_rect.take_click() {
+ msg_out = Some(AppMessage::AddRectangle);
+ }
+ if self.btn_add_banner.take_click() {
+ msg_out = Some(AppMessage::AddBanner);
+ }
}
- }
- if !self.dropdown_units.hit_test(px, py) {
- self.dropdown_units.unfocus();
- }
-
- if clicked {
- changed = true;
- } else {
- if self.toggle_rulers.mouse_input(button, state, px, py) { changed = true; }
- if self.slider_zoom.mouse_input(button, state, px, py) { changed = true; }
- }
- } else if state == ElementState::Released {
- if self.toggle_rulers.mouse_input(button, state, px, py) {
+ } else if active_page == 3 {
let _ = self.toggle_rulers.take_click();
- changed = true;
}
- if self.slider_zoom.mouse_input(button, state, px, py) { changed = true; }
}
}
} else {
@@ -1419,7 +1517,14 @@ impl Application for LayoutApp {
let cx = (px - page_x) / zoom;
let cy = (py - page_y) / zoom;
- if state == ElementState::Pressed {
+ if self.word_processor_enabled {
+ if self.wp_text_box.mouse_input(button, state, px, py) {
+ changed = true;
+ } else if state == ElementState::Pressed {
+ self.wp_text_box.unfocus();
+ changed = true;
+ }
+ } else if state == ElementState::Pressed {
let mut found = None;
for (idx, element) in self.elements.iter().enumerate().rev() {
let (ex, ey, ew, eh) = match element {
@@ -1439,11 +1544,14 @@ impl Application for LayoutApp {
Element::Shape { x, y, .. } => (*x, *y),
};
self.dragging = Some((idx, cx - ex, cy - ey));
+ self.rebuild_element_tab_widgets();
self.sync_sidebar_fields();
changed = true;
} else {
self.selected_idx = None;
self.dragging = None;
+ self.rebuild_element_tab_widgets();
+ self.sync_sidebar_fields();
changed = true;
}
} else if state == ElementState::Released {
@@ -1464,21 +1572,12 @@ impl Application for LayoutApp {
let px = pos.x as f32;
let py = pos.y as f32;
if px < 280.0 {
- if self.paginator.selected_page() == 1 {
- let mut changed = false;
- if self.slider_r.mouse_wheel(delta, px, py) { changed = true; }
- if self.slider_g.mouse_wheel(delta, px, py) { changed = true; }
- if self.slider_b.mouse_wheel(delta, px, py) { changed = true; }
- if changed {
+ if self.paginator.mouse_wheel(delta, px, py) {
+ if self.paginator.selected_page() == 1 {
self.apply_sidebar_changes();
- *needs_rebuild = true;
- self.needs_rebuild = true;
- }
- } else if self.paginator.selected_page() == 3 {
- if self.slider_zoom.mouse_wheel(delta, px, py) {
- *needs_rebuild = true;
- self.needs_rebuild = true;
}
+ *needs_rebuild = true;
+ self.needs_rebuild = true;
}
} else {
let (dx, dy) = match delta {
@@ -1495,52 +1594,23 @@ impl Application for LayoutApp {
fn handle_key_input(&mut self, event: &KeyEvent, needs_rebuild: &mut bool) -> Option<Self::Message> {
let mut handled = false;
- if self.paginator.selected_page() == 0 {
- if self.dropdown_presets.open {
- if self.dropdown_presets.keyboard_input(event) {
- handled = true;
- if self.dropdown_presets.take_change() {
- let sel = self.dropdown_presets.selected;
- if sel < PRESETS.len() {
- self.page_w = PRESETS[sel].w;
- self.page_h = PRESETS[sel].h;
- self.pan_x = 0.0;
- self.pan_y = 0.0;
- }
- }
- }
- } else {
- if self.page_color_selector.keyboard_input(event) {
- handled = true;
- }
+ if self.word_processor_enabled {
+ if self.wp_text_box.keyboard_input(event) {
+ handled = true;
}
- } else if self.paginator.selected_page() == 1 {
- if self.sidebar_x.editing {
- if self.sidebar_x.keyboard_input(event) { handled = true; }
- } else if self.sidebar_y.editing {
- if self.sidebar_y.keyboard_input(event) { handled = true; }
- } else if self.sidebar_w.editing {
- if self.sidebar_w.keyboard_input(event) { handled = true; }
- } else if self.sidebar_h.editing {
- if self.sidebar_h.keyboard_input(event) { handled = true; }
- } else if self.sidebar_text.editing {
- if self.sidebar_text.keyboard_input(event) { handled = true; }
- } else if self.sidebar_size.editing {
- if self.sidebar_size.keyboard_input(event) { handled = true; }
- }
- } else if self.paginator.selected_page() == 3 {
- if self.dropdown_units.open {
- if self.dropdown_units.keyboard_input(event) {
- handled = true;
- let _ = self.dropdown_units.take_change();
- }
+ }
+
+ if self.paginator.keyboard_input(event) {
+ handled = true;
+ let active_page = self.paginator.selected_page();
+ if active_page == 1 {
+ self.apply_sidebar_changes();
}
}
if handled {
*needs_rebuild = true;
self.needs_rebuild = true;
- self.apply_sidebar_changes();
}
None