Skip to content

Multi window Application makes window not responding when button clicked #5835

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Dangujba opened this issue Mar 20, 2025 · 0 comments
Open

Comments

@Dangujba
Copy link

Hello Good day, i am working on a library based on eframe/egui to integrate GUI into my custom language, the aim i am trying to achieve is to create multi window ability where user can create a multiple form like form1, form2 and etc by calling createform just like winform. show_form is used for showing form which is equivalent to winform show(), runapp is use for starting the application which is equivalent to winform Application.run. Now when i tried to run this script:

import gui

function onclick()
    gui.showform(form2)
end function

set form to gui.form("This is First", 500, 500)

set label to gui.label(form, "This is test", 200, 30, 200, 50, false)
gui.setbackcolor(label, "blue")
gui.setfontname(label, "Algerian")
gui.settextalignment(label, "center")
gui.setpadding(label, 20, 0, 0, 0)

set button to gui.button(form, "Show Form 2", onclick, 250, 80)
gui.setautosize(button, false)
gui.settextalignment(button, "center")

set form2 to gui.form("This is First", 500, 500)
//gui.showform(form2)
gui.runapp(form)

if i click the button to it can show the form it will just freeze/hook and become unresponsive i.e the form1 which is the main entry, but i try that gui.showform that is outside onclick it will just popup and go down when the app started, i have tried many trial to solve the issue but can't find solution. Please help review and solve thank you, i have been on this thing for almost five days now. Below are code related to the form from the library

lazy_static::lazy_static! {
    static ref COMMAND_QUEUE: Mutex<VecDeque<Command>> = Mutex::new(VecDeque::new());
    static ref FORMS: RwLock<HashMap<String, FormSettings>> = RwLock::new(HashMap::new());
    static ref CONTROLS: RwLock<HashMap<String, ControlSettings>> = RwLock::new(HashMap::new());
    static ref EXIT_SENDER: RwLock<Option<Sender<()>>> = RwLock::new(None);
    static ref TEXT_UPDATE_SENDERS: RwLock<HashMap<String, Sender<(String, String)>>> = RwLock::new(HashMap::new());
    static ref MSGBOX_RESULT: RwLock<Option<(String, String)>> = RwLock::new(None); 
}
#[derive(Clone, Debug)]
enum DockStyle {
    None,
    Top,
    Bottom,
    Left,
    Right,
    Fill,
}

#[derive(Clone, Debug)]
struct FormSettings {
    title: String,
    width: f32,
    height: f32,
    visible: bool,
    bg_color: Color32,
    maximized: bool,
    fullscreen: bool,
    startposition: String, // e.g., "centerscreen" or "manual"
    resizable: bool,
    position: Option<(f32, f32)>, // For manual positioning
    border: bool,
    controls_order: Vec<String>,
}

// Create Form 
pub fn createform(args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 3 {
        return Err(format!("createform() expects 3 arguments, got {}", args.len()));
    }

    let title = match &args[0] {
        Value::String(s) => s.clone(),
        _ => return Err("createform() expects a string for title".to_string()),
    };
    let width = match &args[1] {
        Value::Number(n) => *n as f32,
        _ => return Err("createform() expects a number for width".to_string()),
    };
    let height = match &args[2] {
        Value::Number(n) => *n as f32,
        _ => return Err("createform() expects a number for height".to_string()),
    };

    let form_id = Uuid::new_v4().to_string();
    let settings = FormSettings {
        title,
        width,
        height,
        visible: false,
        bg_color: Color32::from_rgb(230, 230, 230), // Light gray background
        maximized: false,
        fullscreen: false,
        startposition: "centerscreen".to_string(),
        resizable: true,
        position: None,
        border: true,
        controls_order: Vec::new(),
    };
    
    println!("Created form {} with visible: {}", form_id, settings.visible);
    FORMS.write().unwrap().insert(form_id.clone(), settings);
    Ok(Value::FormObject(form_id))
}

pub fn runapp(args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("runapp() expects 1 argument, got {}", args.len()));
    }
    let form_id = match &args[0] {
        Value::FormObject(id) => id.clone(),
        _ => return Err("runapp() expects a Form identifier".to_string()),
    };

    // Fix: Set the main form to visible
    {
        let mut forms = FORMS.write().unwrap();
        if let Some(settings) = forms.get_mut(&form_id) {
            settings.visible = true;  // Added to ensure the main form is visible
        } else {
            return Err("Form not found".to_string());
        }
    }

    let (exit_tx, exit_rx) = channel();
    {
        let mut exit_sender = EXIT_SENDER.write().unwrap();
        *exit_sender = Some(exit_tx);
    }

    let forms = FORMS.read().unwrap();
    let form_settings = forms.get(&form_id)
        .ok_or("Form not found".to_string())?
        .clone();

    let mut viewport = egui::ViewportBuilder::default()
        .with_maximized(form_settings.maximized)
        .with_fullscreen(form_settings.fullscreen)
        .with_resizable(form_settings.resizable)
        .with_decorations(form_settings.border);

    if !form_settings.maximized && !form_settings.fullscreen {
        viewport = viewport.with_inner_size([form_settings.width, form_settings.height]);
    }

    if form_settings.startposition == "manual" {
        if let Some((x, y)) = form_settings.position {
            viewport = viewport.with_position([x, y]);
        }
    }

    let options = eframe::NativeOptions {
        centered: true,
        viewport,
        ..Default::default()
    };

    println!(
        "Launching app - Maximized: {}, Fullscreen: {}, Border: {}, Size: {:?}",
        form_settings.maximized,
        form_settings.fullscreen,
        form_settings.border,
        if form_settings.maximized || form_settings.fullscreen {
            "Max/Full".to_string()
        } else {
            format!("{}x{}", form_settings.width, form_settings.height)
        }
    );

    eframe::run_native(
        &form_settings.title,
        options,
        Box::new(|cc| Ok(Box::new(MyApp::new(form_id.clone(), exit_rx, cc)))),
    )
    .map_err(|e| format!("Failed to run app: {}", e))?;

    {
        let mut exit_sender = EXIT_SENDER.write().unwrap();
        *exit_sender = None;
    }
    Ok(Value::Null)
}

The App

struct MyApp {
    form_id: String,
    exit_rx: Arc<Mutex<Receiver<()>>>,
    textbox_texts: HashMap<String, String>,
    text_update_rx: Receiver<(String, String)>,
    shown_viewports: HashSet<String>,
}

impl MyApp {
    fn new(form_id: String, exit_rx: Receiver<()>, cc: &eframe::CreationContext<'_>) -> Self {
        // Define custom fonts
        let mut fonts = FontDefinitions::default();

        
        let exe_path = std::env::current_exe().expect("Failed to get executable path");
        let exe_dir = exe_path.parent().expect("Failed to get executable directory");

        
        let fonts_dir = exe_dir.join("fonts");

        // List of font files to load from the 'fonts' subfolder
        let font_files = [
            ("SegoeUI", "SegoeUI.ttf"),
            ("SegoeUIBold", "SegoeUIBold.ttf"),
            ("SegoeUIItalic", "SegoeUIItalic.ttf"),
            ("SegoeUIBoldItalic", "SegoeUIBoldItalic.ttf"),
            ("SansSerif", "SansSerif.otf"),
            ("Algerian", "algerian.ttf")
        ];

        
        for (font_name, file_name) in font_files.iter() {
            let font_path = fonts_dir.join(file_name);
            if font_path.exists() {
                match fs::read(&font_path) {
                    Ok(font_data) => {
                        fonts.font_data.insert(
                            font_name.to_string(),
                            FontData::from_owned(font_data).into(),
                        );
                        fonts.families.insert(
                            FontFamily::Name(font_name.to_string().into()),
                            vec![font_name.to_string()],
                        );
                    }
                    Err(e) => eprintln!("Failed to load font {}: {}", font_path.display(), e),
                }
            } else {
                eprintln!("Font file not found: {}", font_path.display());
            }
        }

        // Set the fonts in the egui context
        cc.egui_ctx.set_fonts(fonts);

        let (text_update_tx, text_update_rx) = channel();
        TEXT_UPDATE_SENDERS.write().unwrap().insert(form_id.clone(), text_update_tx);

        Self {
            form_id,
            exit_rx: Arc::new(Mutex::new(exit_rx)),
            textbox_texts: HashMap::new(),
            text_update_rx,
            shown_viewports: HashSet::new(),
            
        }
    }
}

Update function

impl eframe::App for MyApp {
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {

        // Check if the application should exit (Receiving Signal)
        if self.exit_rx.lock().unwrap().try_recv().is_ok() {
            ctx.send_viewport_cmd(egui::ViewportCommand::Close);
            return;
        }

        // Clone the forms data and release the lock immediately
        let forms_data = {
            let forms = FORMS.read().unwrap(); // Acquire read lock
            forms.clone()                      // Clone the data
        }; // Lock is released here

        // Render the main form if it’s visible
        if let Some(settings) = forms_data.get(&self.form_id) {
            if settings.visible {
                egui::CentralPanel::default().show(ctx, |ui| {
                    // Autosize and constraint logic
                    let (autosize_controls, constraint_controls): (Vec<_>, Vec<_>) = {
                        let controls = CONTROLS.read().unwrap();
                        let autosize = controls.iter()
                            .filter(|(_, c)| c.form_id == self.form_id && c.autosize)
                            .map(|(id, c)| (id.clone(), c.text.clone(), c.fontname.clone(), c.fontsize, c.forecolor, c.padding))
                            .collect();
                        let constraints = controls.iter()
                            .filter(|(_, c)| c.form_id == self.form_id && c.layout_constraint.is_some())
                            .map(|(id, c)| (id.clone(), c.layout_constraint.clone().unwrap()))
                            .collect();
                        (autosize, constraints)
                    };

                    let autosize_sizes: HashMap<String, (f32, f32)> = autosize_controls.into_iter()
                        .map(|(id, text, fontname, fontsize, forecolor, padding)| {
                            let control = ControlSettings {
                                fontname,
                                fontsize,
                                forecolor,
                                padding,
                                ..Default::default()
                            };
                            let (text_width, text_height) = ui_text_size(ui, &text, &control);
                            let total_width = text_width + padding.0 + padding.2;
                            let total_height = text_height + padding.1 + padding.3;
                            (id, (total_width, total_height))
                        })
                        .collect();

                    {
                        let mut controls = CONTROLS.write().unwrap();
                        for (id, (total_width, total_height)) in &autosize_sizes {
                            if let Some(control) = controls.get_mut(id) {
                                control.width = *total_width;
                                control.height = *total_height;
                            }
                        }
                        let target_data: HashMap<String, (egui::Pos2, f32, f32)> = constraint_controls.iter()
                            .filter_map(|(_, constraint)| match constraint {
                                LayoutConstraint::LeftOf { target_id, .. } |
                                LayoutConstraint::RightOf { target_id, .. } |
                                LayoutConstraint::Above { target_id, .. } |
                                LayoutConstraint::Below { target_id, .. } => {
                                    controls.get(target_id).map(|target| {
                                        (target_id.clone(), (target.position, target.width, target.height))
                                    })
                                }
                            })
                            .collect();
                        for (id, constraint) in &constraint_controls {
                            if let Some(control) = controls.get_mut(id) {
                                match constraint {
                                    LayoutConstraint::LeftOf { target_id, space } => {
                                        if let Some((target_pos, target_width, _)) = target_data.get(target_id) {
                                            control.position.x = target_pos.x - control.width - *space;
                                            control.position.y = target_pos.y;
                                        }
                                    }
                                    LayoutConstraint::RightOf { target_id, space } => {
                                        if let Some((target_pos, target_width, _)) = target_data.get(target_id) {
                                            control.position.x = target_pos.x + *target_width + *space;
                                            control.position.y = target_pos.y;
                                        }
                                    }
                                    LayoutConstraint::Above { target_id, space } => {
                                        if let Some((target_pos, _, target_height)) = target_data.get(target_id) {
                                            control.position.x = target_pos.x;
                                            control.position.y = target_pos.y - control.height - *space;
                                        }
                                    }
                                    LayoutConstraint::Below { target_id, space } => {
                                        if let Some((target_pos, _, target_height)) = target_data.get(target_id) {
                                            control.position.x = target_pos.x;
                                            control.position.y = target_pos.y + *target_height + *space;
                                        }
                                    }
                                }
                            }
                        }
                    }

                    // Gather visible controls for rendering
                    let controls_data = {
                        let controls = CONTROLS.read().unwrap();
                        controls.iter()
                            .filter(|(_, c)| c.form_id == self.form_id && c.visible)
                            .map(|(id, c)| (id.clone(), c.clone()))
                            .collect::<Vec<_>>()
                    };

                    let mut textbox_updates = Vec::new();
                    let form_rect = ui.available_rect_before_wrap();
                    ui.painter().rect_filled(form_rect, 0.0, settings.bg_color);

                    // Render controls
                    for (control_id, control) in controls_data.iter() {
                        let mut pos;
                        let mut size;

                        if matches!(control.dock, DockStyle::None) {
                            pos = control.position + egui::Vec2::new(control.margin.0, control.margin.1);
                            size = egui::Vec2::new(control.width, control.height);
                        } else {
                            let (text_width, text_height) = if control.autosize {
                                ui_text_size(ui, &control.text, control)
                            } else {
                                (0.0, 0.0)
                            };

                            match control.dock {
                                DockStyle::Top => {
                                    let height = if control.autosize { text_height + control.padding.1 + control.padding.3 } else { control.height };
                                    pos = form_rect.min;
                                    size = egui::Vec2::new(form_rect.width(), height);
                                }
                                DockStyle::Bottom => {
                                    let height = if control.autosize { text_height + control.padding.1 + control.padding.3 } else { control.height };
                                    pos = egui::pos2(form_rect.min.x, form_rect.max.y - height);
                                    size = egui::Vec2::new(form_rect.width(), height);
                                }
                                DockStyle::Left => {
                                    let width = if control.autosize { text_width + control.padding.0 + control.padding.2 } else { control.width };
                                    pos = form_rect.min;
                                    size = egui::Vec2::new(width, form_rect.height());
                                }
                                DockStyle::Right => {
                                    let width = if control.autosize { text_width + control.padding.0 + control.padding.2 } else { control.width };
                                    pos = egui::pos2(form_rect.max.x - width, form_rect.min.y);
                                    size = egui::Vec2::new(width, form_rect.height());
                                }
                                DockStyle::Fill => {
                                    pos = form_rect.min;
                                    size = form_rect.size();
                                }
                                DockStyle::None => unreachable!(),
                            }
                        }

                        let rect = egui::Rect::from_min_size(pos, size);

                        match control.control_type.as_str() {
                            "label" => {
                                ui.painter().rect_filled(rect, 0.0, control.backcolor);
                                let inner_rect = egui::Rect {
                                    min: rect.min + egui::Vec2::new(control.padding.0, control.padding.1),
                                    max: rect.max - egui::Vec2::new(control.padding.2, control.padding.3),
                                };
                                let font_family = get_font_family(&control.fontname);
                                let font_id = FontId::new(control.fontsize, font_family);
                                let mut job = LayoutJob::default();
                                job.append(
                                    &control.text,
                                    0.0,
                                    TextFormat {
                                        font_id,
                                        color: control.forecolor,
                                        ..Default::default()
                                    },
                                );
                                let galley = ui.fonts(|f| f.layout_job(job));
                                let text_size = galley.rect.size();
                                let align_x = control.text_alignment[0];
                                let align_y = control.text_alignment[1];
                                let offset_x = match align_x {
                                    Align::Min => 0.0,
                                    Align::Center => (inner_rect.width() - text_size.x) / 2.0,
                                    Align::Max => inner_rect.width() - text_size.x,
                                };
                                let offset_y = match align_y {
                                    Align::Min => 0.0,
                                    Align::Center => (inner_rect.height() - text_size.y) / 2.0,
                                    Align::Max => inner_rect.height() - text_size.y,
                                };
                                let text_pos = inner_rect.min + egui::Vec2::new(offset_x, offset_y);
                                ui.painter().galley(text_pos, galley, control.forecolor);
                            }
                            "button" => {
                                // Updated button rendering with text alignment and padding
                                let mut bg_color = control.backcolor;
                                let response = ui.interact(rect, ui.next_auto_id(), Sense::click());
                                if response.hovered() {
                                    bg_color = Color32::from_rgb(
                                        (bg_color.r() as f32 * 1.1).min(255.0) as u8,
                                        (bg_color.g() as f32 * 1.1).min(255.0) as u8,
                                        (bg_color.b() as f32 * 1.1).min(255.0) as u8,
                                    );
                                }
                                if response.clicked() {
                                    bg_color = Color32::from_rgb(
                                        (bg_color.r() as f32 * 0.9).max(0.0) as u8,
                                        (bg_color.g() as f32 * 0.9).max(0.0) as u8,
                                        (bg_color.b() as f32 * 0.9).max(0.0) as u8,
                                    );
                                }
                                ui.painter().rect_filled(rect, 0.0, bg_color);
                                let inner_rect = Rect {
                                    min: rect.min + Vec2::new(control.padding.0, control.padding.1),
                                    max: rect.max - Vec2::new(control.padding.2, control.padding.3),
                                };
                                let font_id = FontId::new(control.fontsize, get_font_family(&control.fontname));
                                let mut job = LayoutJob::default();
                                job.append(&control.text, 0.0, TextFormat {
                                    font_id,
                                    color: control.forecolor,
                                    ..Default::default()
                                });
                                let galley = ui.fonts(|f| f.layout_job(job));
                                let text_size = galley.rect.size();
                                let align_x = control.text_alignment[0];
                                let align_y = control.text_alignment[1];
                                let offset_x = match align_x {
                                    Align::Min => 0.0,
                                    Align::Center => (inner_rect.width() - text_size.x) / 2.0,
                                    Align::Max => inner_rect.width() - text_size.x,
                                };
                                let offset_y = match align_y {
                                    Align::Min => 0.0,
                                    Align::Center => (inner_rect.height() - text_size.y) / 2.0,
                                    Align::Max => inner_rect.height() - text_size.y,
                                };
                                let text_pos = inner_rect.min + Vec2::new(offset_x, offset_y);
                                ui.painter().galley(text_pos, galley, control.forecolor);
                                if response.clicked() {
                                    if let Some(callback) = &control.callback {
                                        
                                        match callback {
                                            Value::Function { name, params, body, closure, object } => {
                                                let mut interpreter = GLOBAL_INTERPRETER.lock().unwrap();
                                                
                                                let mut local_env = Arc::new(Mutex::new(
                                                    Environment::new(Some(closure.clone()))
                                                ));
                                                if !params.is_empty() {
                                                   
                                                }
                                                if let Some(obj) = object {
                                                    let value = obj.lock().unwrap().clone();
                                                    local_env.lock().unwrap().define("this".to_string(), value);
                                                }
                                                let _ = interpreter.visit_block(body, &mut local_env);
                                               
                                            }
                                            Value::String(s) => {
                                                match s.as_str() {
                                                    "close_msgbox" => {
                                                        let mut forms = FORMS.write().unwrap();
                                                        if let Some(settings) = forms.get_mut(&control.form_id) {
                                                            settings.visible = false;
                                                        }
                                                        
                                                    }
                                                    "close_msgbox_yes" => {
                                                        let mut forms = FORMS.write().unwrap();
                                                        if let Some(settings) = forms.get_mut(&control.form_id) {
                                                            settings.visible = false;
                                                        }
                                                        
                                                    }
                                                    "close_msgbox_no" => {
                                                        let mut forms = FORMS.write().unwrap();
                                                        if let Some(settings) = forms.get_mut(&control.form_id) {
                                                            settings.visible = false;
                                                        }
                                                       
                                                    }
                                                    "close_msgbox_cancel" => {
                                                        let mut forms = FORMS.write().unwrap();
                                                        if let Some(settings) = forms.get_mut(&control.form_id) {
                                                            settings.visible = false;
                                                        }
                                                        
                                                    }
                                                    _ => {}
                                                }
                                            }
                                            _ => {}
                                        }
                                    }
                                }
                                if response.hovered() && !control.cursor.is_empty() {
                                    let cursor_icon = match control.cursor.to_lowercase().as_str() {
                                        "hand" | "pointer" => CursorIcon::PointingHand,
                                        "default" => CursorIcon::Default,
                                        "crosshair" => CursorIcon::Crosshair,
                                        "text" => CursorIcon::Text,
                                        "move" => CursorIcon::Move,
                                        "grab" => CursorIcon::Grab,
                                        "grabbing" => CursorIcon::Grabbing,
                                        _ => CursorIcon::Default,
                                    };
                                    ui.output_mut(|o| o.cursor_icon = cursor_icon);
                                }
                            }
                            "textbox" => {
                                let text = self.textbox_texts.entry(control_id.clone()).or_insert_with(|| control.text.clone());
                                ui.painter().rect_filled(rect, 0.0, control.backcolor);
                                let response = ui.allocate_ui_at_rect(rect, |ui| {
                                    let font_family = get_font_family(&control.fontname);
                                    let font_id = FontId::new(control.fontsize, font_family);
                                    if control.multiline {
                                        egui::ScrollArea::vertical().show(ui, |ui| {
                                            let text_edit = egui::TextEdit::multiline(text)
                                                .text_color(control.forecolor)
                                                .font(font_id)
                                                .frame(false)
                                                .desired_width(control.width);
                                            ui.add_sized(size, text_edit)
                                        }).inner
                                    } else {
                                        let text_edit = egui::TextEdit::singleline(text)
                                            .text_color(control.forecolor)
                                            .font(font_id)
                                            .frame(false)
                                            .desired_width(control.width);
                                        ui.add_sized(size, text_edit)
                                    }
                                });
                                if response.inner.changed() {
                                    textbox_updates.push((control_id.clone(), text.clone()));
                                }
                            }
                            _ => {
                                ui.painter().rect_filled(rect, 0.0, Color32::RED);
                            }
                        }
                    }
                    if !textbox_updates.is_empty() {
                        let mut controls = CONTROLS.write().unwrap();
                        for (control_id, new_text) in textbox_updates {
                            if let Some(control) = controls.get_mut(&control_id) {
                                control.text = new_text;
                            }
                        }
                    }
                });
            }
        }

        // Clean up shown_viewports by removing hidden forms
        self.shown_viewports.retain(|form_id| {
            forms_data.get(form_id).map_or(false, |settings| settings.visible)
        });

        // Schedule additional viewports for visible forms
        for (form_id, settings) in forms_data.iter() {
            
            if settings.visible && form_id != &self.form_id {
                
                if !self.shown_viewports.contains(form_id) {
                    
                    self.shown_viewports.insert(form_id.clone());
                    
                    let form_id_clone = form_id.clone();
                    let settings_clone = settings.clone();
                    let textbox_texts_clone = std::sync::Mutex::new(self.textbox_texts.clone());
                    ctx.show_viewport_deferred(
                        egui::ViewportId::from_hash_of(form_id_clone.clone()),
                        egui::ViewportBuilder::default()
                            .with_title(&settings_clone.title)
                            .with_inner_size([settings_clone.width, settings_clone.height])
                            .with_maximized(settings_clone.maximized)
                            .with_fullscreen(settings_clone.fullscreen)
                            .with_resizable(settings_clone.resizable)
                            .with_decorations(settings_clone.border),
                        move |ctx, _class| {
                            let forms = FORMS.read().unwrap();
                            if let Some(settings) = forms.get(&form_id_clone) {
                                
                                if !settings.visible {
                                    
                                    ctx.send_viewport_cmd(egui::ViewportCommand::Close);
                                    return;
                                }
                                if ctx.input(|i| i.viewport().close_requested()) {
                                    
                                    let mut forms = FORMS.write().unwrap();
                                    if let Some(settings) = forms.get_mut(&form_id_clone) {
                                        settings.visible = false;
                                        
                                    }
                                } else {
                                    egui::CentralPanel::default().show(ctx, |ui| {
                                        
                                        let (autosize_controls, constraint_controls): (Vec<_>, Vec<_>) = {
                                            let controls = CONTROLS.read().unwrap();
                                            let autosize = controls.iter()
                                                .filter(|(_, c)| c.form_id == form_id_clone && c.autosize)
                                                .map(|(id, c)| (id.clone(), c.text.clone(), c.fontname.clone(), c.fontsize, c.forecolor, c.padding))
                                                .collect();
                                            let constraints = controls.iter()
                                                .filter(|(_, c)| c.form_id == form_id_clone && c.layout_constraint.is_some())
                                                .map(|(id, c)| (id.clone(), c.layout_constraint.clone().unwrap()))
                                                .collect();
                                            (autosize, constraints)
                                        };

                                        let autosize_sizes: HashMap<String, (f32, f32)> = autosize_controls.into_iter()
                                            .map(|(id, text, fontname, fontsize, forecolor, padding)| {
                                                let control = ControlSettings {
                                                    fontname,
                                                    fontsize,
                                                    forecolor,
                                                    padding,
                                                    ..Default::default()
                                                };
                                                let (text_width, text_height) = ui_text_size(ui, &text, &control);
                                                let total_width = text_width + padding.0 + padding.2;
                                                let total_height = text_height + padding.1 + padding.3;
                                                (id, (total_width, total_height))
                                            })
                                            .collect();

                                        {
                                            let mut controls = CONTROLS.write().unwrap();
                                            for (id, (total_width, total_height)) in &autosize_sizes {
                                                if let Some(control) = controls.get_mut(id) {
                                                    control.width = *total_width;
                                                    control.height = *total_height;
                                                }
                                            }
                                            let target_data: HashMap<String, (egui::Pos2, f32, f32)> = constraint_controls.iter()
                                                .filter_map(|(_, constraint)| match constraint {
                                                    LayoutConstraint::LeftOf { target_id, .. } |
                                                    LayoutConstraint::RightOf { target_id, .. } |
                                                    LayoutConstraint::Above { target_id, .. } |
                                                    LayoutConstraint::Below { target_id, .. } => {
                                                        controls.get(target_id).map(|target| {
                                                            (target_id.clone(), (target.position, target.width, target.height))
                                                        })
                                                    }
                                                })
                                                .collect();
                                            for (id, constraint) in &constraint_controls {
                                                if let Some(control) = controls.get_mut(id) {
                                                    match constraint {
                                                        LayoutConstraint::LeftOf { target_id, space } => {
                                                            if let Some((target_pos, target_width, _)) = target_data.get(target_id) {
                                                                control.position.x = target_pos.x - control.width - *space;
                                                                control.position.y = target_pos.y;
                                                            }
                                                        }
                                                        LayoutConstraint::RightOf { target_id, space } => {
                                                            if let Some((target_pos, target_width, _)) = target_data.get(target_id) {
                                                                control.position.x = target_pos.x + *target_width + *space;
                                                                control.position.y = target_pos.y;
                                                            }
                                                        }
                                                        LayoutConstraint::Above { target_id, space } => {
                                                            if let Some((target_pos, _, target_height)) = target_data.get(target_id) {
                                                                control.position.x = target_pos.x;
                                                                control.position.y = target_pos.y - control.height - *space;
                                                            }
                                                        }
                                                        LayoutConstraint::Below { target_id, space } => {
                                                            if let Some((target_pos, _, target_height)) = target_data.get(target_id) {
                                                                control.position.x = target_pos.x;
                                                                control.position.y = target_pos.y + *target_height + *space;
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }

                                        let controls_data = {
                                            let controls = CONTROLS.read().unwrap();
                                            controls.iter()
                                                .filter(|(_, c)| c.form_id == form_id_clone && c.visible)
                                                .map(|(id, c)| (id.clone(), c.clone()))
                                                .collect::<Vec<_>>()
                                        };

                                        let mut textbox_updates = Vec::new();
                                        let form_rect = ui.available_rect_before_wrap();
                                        ui.painter().rect_filled(form_rect, 0.0, settings.bg_color);

                                        for (control_id, control) in controls_data.iter() {
                                            let mut pos;
                                            let mut size;

                                            if matches!(control.dock, DockStyle::None) {
                                                pos = control.position + Vec2::new(control.margin.0, control.margin.1);
                                                size = Vec2::new(control.width, control.height);
                                            } else {
                                                let (text_width, text_height) = if control.autosize {
                                                    ui_text_size(ui, &control.text, control)
                                                } else {
                                                    (0.0, 0.0)
                                                };

                                                match control.dock {
                                                    DockStyle::Top => {
                                                        let height = if control.autosize { text_height + control.padding.1 + control.padding.3 } else { control.height };
                                                        pos = form_rect.min;
                                                        size = Vec2::new(form_rect.width(), height);
                                                    }
                                                    DockStyle::Bottom => {
                                                        let height = if control.autosize { text_height + control.padding.1 + control.padding.3 } else { control.height };
                                                        pos = egui::pos2(form_rect.min.x, form_rect.max.y - height);
                                                        size = Vec2::new(form_rect.width(), height);
                                                    }
                                                    DockStyle::Left => {
                                                        let width = if control.autosize { text_width + control.padding.0 + control.padding.2 } else { control.width };
                                                        pos = form_rect.min;
                                                        size = Vec2::new(width, form_rect.height());
                                                    }
                                                    DockStyle::Right => {
                                                        let width = if control.autosize { text_width + control.padding.0 + control.padding.2 } else { control.width };
                                                        pos = egui::pos2(form_rect.max.x - width, form_rect.min.y);
                                                        size = Vec2::new(width, form_rect.height());
                                                    }
                                                    DockStyle::Fill => {
                                                        pos = form_rect.min;
                                                        size = form_rect.size();
                                                    }
                                                    DockStyle::None => unreachable!(),
                                                }
                                            }

                                            let rect = Rect::from_min_size(pos, size);

                                            match control.control_type.as_str() {
                                                "label" => {
                                                    ui.painter().rect_filled(rect, 0.0, control.backcolor);
                                                    let inner_rect = Rect {
                                                        min: rect.min + Vec2::new(control.padding.0, control.padding.1),
                                                        max: rect.max - Vec2::new(control.padding.2, control.padding.3),
                                                    };
                                                    let font_family = get_font_family(&control.fontname);
                                                    let font_id = FontId::new(control.fontsize, font_family);
                                                    let mut job = LayoutJob::default();
                                                    job.append(
                                                        &control.text,
                                                        0.0,
                                                        TextFormat {
                                                            font_id,
                                                            color: control.forecolor,
                                                            ..Default::default()
                                                        },
                                                    );
                                                    let galley = ui.fonts(|f| f.layout_job(job));
                                                    let text_size = galley.rect.size();
                                                    let align_x = control.text_alignment[0];
                                                    let align_y = control.text_alignment[1];
                                                    let offset_x = match align_x {
                                                        Align::Min => 0.0,
                                                        Align::Center => (inner_rect.width() - text_size.x) / 2.0,
                                                        Align::Max => inner_rect.width() - text_size.x,
                                                    };
                                                    let offset_y = match align_y {
                                                        Align::Min => 0.0,
                                                        Align::Center => (inner_rect.height() - text_size.y) / 2.0,
                                                        Align::Max => inner_rect.height() - text_size.y,
                                                    };
                                                    let text_pos = inner_rect.min + Vec2::new(offset_x, offset_y);
                                                    ui.painter().galley(text_pos, galley, control.forecolor);
                                                }
                                                "button" => {
                                                    let mut bg_color = control.backcolor;
                                                    let response = ui.interact(rect, ui.next_auto_id(), Sense::click());
                                                    if response.hovered() {
                                                        bg_color = Color32::from_rgb(
                                                            (bg_color.r() as f32 * 1.1).min(255.0) as u8,
                                                            (bg_color.g() as f32 * 1.1).min(255.0) as u8,
                                                            (bg_color.b() as f32 * 1.1).min(255.0) as u8,
                                                        );
                                                    }
                                                    if response.clicked() {
                                                        bg_color = Color32::from_rgb(
                                                            (bg_color.r() as f32 * 0.9).max(0.0) as u8,
                                                            (bg_color.g() as f32 * 0.9).max(0.0) as u8,
                                                            (bg_color.b() as f32 * 0.9).max(0.0) as u8,
                                                        );
                                                    }
                                                    ui.painter().rect_filled(rect, 0.0, bg_color);
                                                    let inner_rect = Rect {
                                                        min: rect.min + Vec2::new(control.padding.0, control.padding.1),
                                                        max: rect.max - Vec2::new(control.padding.2, control.padding.3),
                                                    };
                                                    let font_id = FontId::new(control.fontsize, get_font_family(&control.fontname));
                                                    let mut job = LayoutJob::default();
                                                    job.append(&control.text, 0.0, TextFormat {
                                                        font_id,
                                                        color: control.forecolor,
                                                        ..Default::default()
                                                    });
                                                    let galley = ui.fonts(|f| f.layout_job(job));
                                                    let text_size = galley.rect.size();
                                                    let align_x = control.text_alignment[0];
                                                    let align_y = control.text_alignment[1];
                                                    let offset_x = match align_x {
                                                        Align::Min => 0.0,
                                                        Align::Center => (inner_rect.width() - text_size.x) / 2.0,
                                                        Align::Max => inner_rect.width() - text_size.x,
                                                    };
                                                    let offset_y = match align_y {
                                                        Align::Min => 0.0,
                                                        Align::Center => (inner_rect.height() - text_size.y) / 2.0,
                                                        Align::Max => inner_rect.height() - text_size.y,
                                                    };
                                                    let text_pos = inner_rect.min + Vec2::new(offset_x, offset_y);
                                                    ui.painter().galley(text_pos, galley, control.forecolor);
                                                    if response.clicked() {
                                                        if let Some(callback) = &control.callback {
                                                            match callback {
                                                                Value::Function { name, params, body, closure, object } => {
                                                                    let mut interpreter = GLOBAL_INTERPRETER.lock().unwrap();
                                                                    let mut local_env = Arc::new(Mutex::new(
                                                                        Environment::new(Some(closure.clone()))
                                                                    ));
                                                                    if !params.is_empty() {
                                                                        
                                                                    }
                                                                    if let Some(obj) = object {
                                                                        let value = obj.lock().unwrap().clone();
                                                                        local_env.lock().unwrap().define("this".to_string(), value);
                                                                    }
                                                                    let _ = interpreter.visit_block(body, &mut local_env);
                                                                }
                                                                Value::String(s) => {
                                                                    match s.as_str() {
                                                                        "close_msgbox" => {
                                                                            let mut forms = FORMS.write().unwrap();
                                                                            if let Some(settings) = forms.get_mut(&control.form_id) {
                                                                                settings.visible = false;
                                                                            }
                                                                            
                                                                        }
                                                                        "close_msgbox_yes" => {
                                                                            let mut forms = FORMS.write().unwrap();
                                                                            if let Some(settings) = forms.get_mut(&control.form_id) {
                                                                                settings.visible = false;
                                                                            }
                                                                            
                                                                        }
                                                                        "close_msgbox_no" => {
                                                                            let mut forms = FORMS.write().unwrap();
                                                                            if let Some(settings) = forms.get_mut(&control.form_id) {
                                                                                settings.visible = false;
                                                                            }
                                                                            
                                                                        }
                                                                        "close_msgbox_cancel" => {
                                                                            let mut forms = FORMS.write().unwrap();
                                                                            if let Some(settings) = forms.get_mut(&control.form_id) {
                                                                                settings.visible = false;
                                                                            }
                                                                            
                                                                        }
                                                                        _ => {}
                                                                    }
                                                                }
                                                                _ => {}
                                                            }
                                                        }
                                                    }
                                                    if response.hovered() && !control.cursor.is_empty() {
                                                        let cursor_icon = match control.cursor.to_lowercase().as_str() {
                                                            "hand" | "pointer" => CursorIcon::PointingHand,
                                                            "default" => CursorIcon::Default,
                                                            "crosshair" => CursorIcon::Crosshair,
                                                            "text" => CursorIcon::Text,
                                                            "move" => CursorIcon::Move,
                                                            "grab" => CursorIcon::Grab,
                                                            "grabbing" => CursorIcon::Grabbing,
                                                            _ => CursorIcon::Default,
                                                        };
                                                        ui.output_mut(|o| o.cursor_icon = cursor_icon);
                                                    }
                                                }
                                                "textbox" => {
                                                    let mut textbox_texts = textbox_texts_clone.lock().unwrap();
                                                    let text = textbox_texts.entry(control_id.clone()).or_insert_with(|| control.text.clone());
                                                    ui.painter().rect_filled(rect, 0.0, control.backcolor);
                                                    let response = ui.allocate_ui_at_rect(rect, |ui| {
                                                        let font_family = get_font_family(&control.fontname);
                                                        let font_id = FontId::new(control.fontsize, font_family);
                                                        if control.multiline {
                                                            egui::ScrollArea::vertical().show(ui, |ui| {
                                                                let text_edit = egui::TextEdit::multiline(text)
                                                                    .text_color(control.forecolor)
                                                                    .font(font_id)
                                                                    .frame(false)
                                                                    .desired_width(control.width);
                                                                ui.add_sized(size, text_edit)
                                                            }).inner
                                                        } else {
                                                            let text_edit = egui::TextEdit::singleline(text)
                                                                .text_color(control.forecolor)
                                                                .font(font_id)
                                                                .frame(false)
                                                                .desired_width(control.width);
                                                            ui.add_sized(size, text_edit)
                                                        }
                                                    });
                                                    if response.inner.changed() {
                                                        textbox_updates.push((control_id.clone(), text.clone()));
                                                    }
                                                }
                                                _ => {
                                                    ui.painter().rect_filled(rect, 0.0, Color32::RED);
                                                }
                                            }
                                        }
                                        if !textbox_updates.is_empty() {
                                            let mut controls = CONTROLS.write().unwrap();
                                            for (control_id, new_text) in textbox_updates {
                                                if let Some(control) = controls.get_mut(&control_id) {
                                                    control.text = new_text;
                                                }
                                            }
                                        }
                                    });
                                }
                            }
                        },
                    );
                } 
            } 
        }
    }
}

Show form code

pub fn show_form(args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("show_form() expects 1 argument, got {}", args.len()));
    }
    let form_id = match &args[0] {
        Value::FormObject(id) => id.clone(),
        _ => return Err("show_form() expects a Form identifier".to_string()),
    };

    let mut forms = FORMS.write().unwrap();
    if let Some(settings) = forms.get_mut(&form_id) {
        settings.visible = true;
        println!("Form {} set to visible", form_id);
        Ok(Value::Null)
    } else {
        Err("Form not found".to_string())
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant