fltk-rs / fltk-table

A smart table widget for fltk-rs
MIT License
16 stars 4 forks source link

Possible dead locking when spawning dialogs in the same app #11

Open realtica opened 2 years ago

realtica commented 2 years ago

Hello, I'm trying to change the number of cols and rows at runtime, with:

                table.set_opts(TableOpts {
                    cell_align: enums::Align::Wrap,
                    editable: false,
                    rows: 55,
                    cols: 66,
                    ..Default::default()
                });

Is there any other way to change those? I've tried with: table.set_rows(55); But, not working.

MoAlyousef commented 2 years ago

set_opts should work. Can you confirm this code works for you:

use fltk::{
    app, button,
    prelude::{GroupExt, WidgetExt, WidgetBase},
    window,
};
use fltk_table::{SmartTable, TableOpts};

fn main() {
    let app = app::App::default().with_scheme(app::Scheme::Gtk);
    let mut wind = window::Window::default().with_size(800, 600);

    let mut table = SmartTable::default()
    .with_size(790, 590)
    .center_of_parent()
    .with_opts(TableOpts {
        rows: 5,
        cols: 5,
        editable: true,
        ..Default::default()
    });
    let mut btn = button::Button::new(100, 400, 80, 30, "Click");
    wind.end();
    wind.show();

    table.set_cell_value(3, 4, "10");

    btn.set_callback(move |_| {
        table.set_opts(
            TableOpts {
                rows: 6,
                cols: 6,
                editable: true,
                ..Default::default()
            }
        );
    });

    app.run().unwrap();
}

You might need to call table.redraw() after it.

realtica commented 2 years ago

Hello, I not sure if the problem is the Dialog or the Table: Steps to reproduce the error: Open the Dialog, close it then select an Item, and try to open the Dialog again. Now, comment the table.set_opts(); And everything is OK

// use fltk::{app, button::Button, frame::Frame, group::Flex, prelude::*, window::Window};
use fltk::{prelude::*, *};
use fltk_evented::Listener;
use fltk_table::{SmartTable, TableOpts};

fn main() {
    let a = app::App::default();
    app::set_font_size(20);

    let mut wind = window::Window::default()
        .with_size(260, 400)
        .center_screen()
        .with_label("Counter");
    let flex = group::Flex::default()
        .with_size(220, 360)
        .center_of_parent()
        .column();
    let but_inc: Listener<_> = button::Button::default().with_label("Open dialog").into();
    let mut hold_browser: Listener<_> = browser::HoldBrowser::default().into();
    let mut table = SmartTable::default()
        .center_of_parent()
        .with_opts(TableOpts {
            rows: 30,
            cols: 15,
            editable: true,
            ..Default::default()
        });
    flex.end();
    wind.make_resizable(true);
    wind.end();
    wind.show();

    while a.wait() {
        if but_inc.triggered() {
            MyDialog::default();
            hold_browser.clear();
            hold_browser.add("item A");
            hold_browser.add("item b");
            hold_browser.add("item C");
        }
        if hold_browser.triggered() {
            table.set_opts(TableOpts {
                cell_align: enums::Align::Wrap,
                editable: false,
                rows: 30,
                cols: 15,
                ..Default::default()
            });
            for i in 0..30 {
                for j in 0..15 {
                    table.set_cell_value(i, j, &(i + j).to_string());
                }
            }
            table.redraw();
        }
        // frame.set_label(&val.to_string());
    }
    a.run().unwrap();
}

pub struct MyDialog {
    inp: input::Input,
}

impl MyDialog {
    pub fn default() -> Self {
        let mut win = window::Window::default()
            .with_size(400, 100)
            .with_label("My Dialog");
        let mut pack = group::Pack::default()
            .with_size(300, 30)
            .center_of_parent()
            .with_type(group::PackType::Horizontal);
        pack.set_spacing(20);
        frame::Frame::default()
            .with_size(80, 0)
            .with_label("Enter name:");
        let mut inp = input::Input::default().with_size(100, 0);
        let mut ok = button::Button::default().with_size(80, 0).with_label("Ok");
        pack.end();
        win.end();
        win.make_modal(true);
        win.show();
        ok.set_callback({
            let mut win = win.clone();
            move |_| {
                win.hide();
            }
        });
        while win.shown() {
            app::wait();
        }
        Self { inp }
    }
    pub fn value(&self) -> String {
        self.inp.value()
    }
}
MoAlyousef commented 2 years ago

It might be that the table is dead locking, I'll have to investigate further. Anyways, the issue is reproducible with the following:

use fltk::{prelude::*, *};
use fltk_table::{SmartTable, TableOpts};

fn main() {
    let a = app::App::default();
    app::set_font_size(20);

    let mut wind = window::Window::default()
        .with_size(260, 400)
        .center_screen()
        .with_label("Counter");
    let flex = group::Flex::default()
        .with_size(220, 360)
        .center_of_parent()
        .column();
    let mut but_inc= button::Button::default().with_label("Open dialog");
    let mut hold_browser= browser::HoldBrowser::default();
    let mut table = SmartTable::default()
        .center_of_parent()
        .with_opts(TableOpts {
            rows: 30,
            cols: 15,
            editable: true,
            ..Default::default()
        });
    flex.end();
    wind.make_resizable(true);
    wind.end();
    wind.show();

    but_inc.set_callback({
            let mut hold_browser = hold_browser.clone();
        move |_| {
        MyDialog::default();
        hold_browser.clear();
        hold_browser.add("item A");
        hold_browser.add("item b");
        hold_browser.add("item C");
    }});

    hold_browser.set_callback(move |_| {
        table.set_opts(TableOpts {
            cell_align: enums::Align::Wrap,
            editable: false,
            rows: 30,
            cols: 15,
            ..Default::default()
        });
        for i in 0..30 {
            for j in 0..15 {
                table.set_cell_value(i, j, &(i + j).to_string());
            }
        }
        table.redraw();
    });
    a.run().unwrap();
}
realtica commented 2 years ago

Thanks! I hope so much this will have a solution, because I don't know how to wrap variables.., that is the reason why fltk-evented help me a lot.

MoAlyousef commented 2 years ago

I've tried replacing std::sync::Mutex with parking_lot::Mutex and enabling deadlock_detection and it seems there are no deadlocks. I think the issue is mainly is within FLTK but I can't pinpoint it. A workaround is to not create a window with each button trigger, instead spawn the dialog as a hidden window, and show it when the button is triggered:

use fltk::{prelude::*, *};
use fltk_evented::Listener;
use fltk_table::{SmartTable, TableOpts};

fn main() {
    let a = app::App::default();
    app::set_font_size(20);
    let mut dlg = MyDialog::default();

    let mut wind = window::Window::default()
        .with_size(260, 400)
        .center_screen()
        .with_label("Counter");
    let flex = group::Flex::default()
        .with_size(220, 360)
        .center_of_parent()
        .column();
    let but_inc: Listener<_> = button::Button::default().with_label("Open dialog").into();
    let mut hold_browser: Listener<_> = browser::HoldBrowser::default().into();
    let mut table = SmartTable::default()
        .center_of_parent()
        .with_opts(TableOpts {
            rows: 30,
            cols: 15,
            editable: true,
            ..Default::default()
        });
    flex.end();
    wind.make_resizable(true);
    wind.end();
    wind.show();

    while a.wait() {
        if but_inc.triggered() {
            dlg.show();
            while dlg.shown() {
                a.wait();
            }
            hold_browser.clear();
            hold_browser.add("item A");
            hold_browser.add("item b");
            hold_browser.add("item C");
        }
        if hold_browser.triggered() {
            table.set_opts(TableOpts {
                cell_align: enums::Align::Wrap,
                editable: false,
                rows: 30,
                cols: 15,
                ..Default::default()
            });
            for i in 0..30 {
                for j in 0..15 {
                    table.set_cell_value(i, j, &(i + j).to_string());
                }
            }
        }
    }
}

pub struct MyDialog {
    win: window::Window,
    inp: input::Input,
}

impl MyDialog {
    pub fn default() -> Self {
        let mut win = window::Window::default()
            .with_size(400, 100)
            .with_label("My Dialog");
        let mut pack = group::Pack::default()
            .with_size(300, 30)
            .center_of_parent()
            .with_type(group::PackType::Horizontal);
        pack.set_spacing(20);
        frame::Frame::default()
            .with_size(80, 0)
            .with_label("Enter name:");
        let mut inp = input::Input::default().with_size(100, 0);
        let mut ok = button::Button::default().with_size(80, 0).with_label("Ok");
        pack.end();
        win.end();
        win.make_modal(true);
        win.hide();
        ok.set_callback({
            let mut win = win.clone();
            move |_| {
                win.hide();
            }
        });
        Self { win, inp }
    }
    pub fn value(&self) -> String {
        self.inp.value()
    }
    pub fn show(&mut self) {
        self.win.show();
    }
    pub fn hide(&mut self) {
        self.win.hide();
    }
    pub fn shown(&self) -> bool {
        self.win.shown()
    }
}