how to restore position of hidden modeless form

87 views
Skip to first unread message

Mark

unread,
Dec 13, 2021, 8:19:32 AM12/13/21
to fltk.general
I have a simple Window which is modeless (make_modal(false)).
At startup the window is null. But if the user requests the window it is created and then shown. If the user closes the window is is merely hidden, with all its state preserved.
However, when the user requests the window a second or subsequent time the (now) preexisting window is merely shown.

Unfortunately, when the window is shown, although it preserves its internal state, scrollbars etc., it is _always_ repositioned to its original position, even if the user moved it the last time they had it shown.

My code correctly records the window's position after any resize (i.e., move) and tries to restore this, but it doesn't work. Here's the essence of the code:

```
pub struct Form {
    form: fltk::window::Window,
    pos: Rc<RefCell<Pos>>,
}

impl Form {
    pub fn new(...) -> Self {
        let mut form = fltk::window::Window::default();
        ...
        form.end();
        form.make_modal(false);
        let pos = Rc::new(RefCell::new(Pos::default()));
        form.handle({
            let pos = pos.clone();
            move |form, event| {
                if event == fltk::enums::Event::Resize {
                    // This records the position moved to
                    *pos.borrow_mut() = Pos::new(*&form.x(), *&form.y());
                }
                false
            }
        });
        form.show();
        ok_button.set_callback({
            let mut form = form.clone();
            move |_| {
                form.hide();
            }
        });
        Self { form, pos }
    }

    pub fn show(&mut self) {
        self.form.show();
        let pos = *self.pos.borrow();
        if pos.is_valid() { // This block has no effect
            // This sets the last position moved to
            self.form.set_pos(pos.x, pos.y);
            // The docs say this is needed for changing pos
            self.form.redraw();
        }
    }
}
```

Ian MacArthur

unread,
Dec 13, 2021, 8:59:42 AM12/13/21
to fltk.general
On Monday, 13 December 2021 at 13:19:32 UTC Mark wrote:
I have a simple Window which is modeless (make_modal(false)).

Hmmm, careful now...

What does make_modal do in the Rust port? Or, what do you think it is doing?

Window modality (the term "modal" here is something of a MS'ism) is to do with how transient windows and etc. are handled, and they key point is that it is NOT boolean, it has three (3, count 'em all) states:  Normal, modal and non-modal.

Of these, the one you want is (almost always) "normal", except in the specific case that the window is a modal transient for another window (e.g. a menu popup or an alert dialog) or a "non-modal" toolbox that remains "on top" of its parent, but does not block it...

So, generally, you do not want to set or change the "modal" state of the window at all. And if you are changing it, then "true" or "false" don't seem like the correct options, since there are three things a window can be.
Under C++, the thing to clear any modality and return the window to "normal" would be:  Fl_Window::clear_modal_states();

But I have no idea what it is in Rust - nor, indeed, what you actually are hoping to achieve...!

FWIW, a "normal" widow will generally retain it's position on hide/show, so that fact it is not here suggests something else is awry - it may well be that fiddling with the modality is triggering some misbehaviour. (Modal windows may try to position themselves w.r.t. their parent...)

Mark

unread,
Dec 13, 2021, 2:07:38 PM12/13/21
to fltk.general
I use this type for both modal (application-modal) and modeless dialogs. In rust make_modal takes a bool:
I use it twice, once for a modal about box:
```
    fn on_about(&mut self) {
        html_form::Form::new("About", &about_html(), true, 400, 300);
    }
```
This works fine, creates and shows the modal (true) window and hides and is destroyed on close.
An this is where I use it as a modeless dialog:
```
    fn on_help(&mut self) {
        if let Some(helpform) = &mut self.helpform {
            helpform.show();
        } else {
            self.helpform = Some(html_form::Form::new(
                "Help", HELP_HTML, false, 460, 480,
            ));
        }
    }
```
The first time on_help is called the else arm is executed which creates a modeless (modal = false) window. When this window is closed it just hides. The second and subsequent times on_help is called the now existing window's show method is called. But it always shows itself repositioned to its original position rather than where the user last left it, and this is what my recording & restoring of its position is for -- but which doesn't work.

Mo_Al_

unread,
Dec 13, 2021, 3:57:09 PM12/13/21
to fltk.general
make_modal() just calls Fl_Window::set_modal and set_non_modal depending on the flag. 

I think Mark's program can be translated into this in C++:


#include <FL/Enumerations.H>
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>

static int last_x = 0;
static int last_y = 0;

struct Form: public Fl_Window {
    Form(int x, int y, int w, int h, const char *label): Fl_Window(x, y, w, h, label) {}
    virtual void resize(int x, int y, int w, int h) override {
        last_x = x;
        last_y = y;
        printf("last pos: %d %d\n", last_x, last_y);
    }
    void show_again() {
        printf("shown again at: %d %d\n", last_x, last_y);
        show();
        resize(last_x, last_y, w(), h());
    }
};

void btn_cb(Fl_Widget *w, void *data) {
    auto form = (Form *)data;
    form->show_again();
}

int main(int argc, char **argv) {
    auto win = new Fl_Window(400, 300, "Main Win");
    auto btn = new Fl_Button(160, 200, 80, 30, "Show");
    win->end();
    win->show();

    auto form = new Form(100, 100, 200, 100, "Form");
    form->show();
    btn->callback(btn_cb, form);
    return Fl::run();
}

Albrecht Schlosser

unread,
Dec 13, 2021, 4:48:44 PM12/13/21
to fltkg...@googlegroups.com
On 12/13/21 9:57 PM Mo_Al_ wrote:
make_modal() just calls Fl_Window::set_modal and set_non_modal depending on the flag. 

I think Mark's program can be translated into this in C++:

Thanks!

If this is a correct transcription, then only one statement is missing. The virtual resize() method must call the resize() method of the base class (Fl_Window). See below.



#include <FL/Enumerations.H>
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
static int last_x = 0;
static int last_y = 0;
struct Form: public Fl_Window {
    Form(int x, int y, int w, int h, const char *label): Fl_Window(x, y, w, h, label) {}
    virtual void resize(int x, int y, int w, int h) override {

    Fl_Window::resize(x, y, w, h); // ADD THIS HERE


        last_x = x;
        last_y = y;
        printf("last pos: %d %d\n", last_x, last_y);
    }
    void show_again() {
        printf("shown again at: %d %d\n", last_x, last_y);
        show();
        resize(last_x, last_y, w(), h());
    }
};
void btn_cb(Fl_Widget *w, void *data) {
    auto form = (Form *)data;
    form->show_again();
}
int main(int argc, char **argv) {
    auto win = new Fl_Window(400, 300, "Main Win");
    auto btn = new Fl_Button(160, 200, 80, 30, "Show");
    win->end();
    win->show();
    auto form = new Form(100, 100, 200, 100, "Form");
    form->show();
    btn->callback(btn_cb, form);
    return Fl::run();
}

I would however suggest to store the current position (only) when the window is actually hidden. The attached file x.cxx shows both ways, the "original" and the one which stores the position when the window is hidden if you

#define USE_HIDE (1)

(instead of 0)

Yet another way might be to use the callback of the Fl_Window class which is called when the user clicks the close button. In that case you'd have to call hide() explicitly after storing the coordinates. Since the callback is not a class method it you require write access to last_x and last_y which is a given for a struct but might not be true for a class unless declared public or using an accessor method.

x.cxx

Mo_Al_

unread,
Dec 13, 2021, 5:36:36 PM12/13/21
to fltk.general
So the C bindings did call the base class's constructor correctly. It was a mistake in the code example I put. 

It's actually a bug in fltk-rs. Calling the window's ::default constructor:
Window::default().with_size(400, 100)

Actually calls force_position(0), instead of actually just passing 0 for x and y. This causes the window to ignore the x and y when reshown. 
So in fltk-rs, creating a window using Window::new(x, y, w, h, label) doesn't exhibit this behavior. 

Mark, can you pull the latest changes using cargo update then rebuilding. You either have to give your form an initial x and y positions, otherwise you need to call form.force_position(true) before calling set_pos() or resize().

Mo_Al_

unread,
Dec 13, 2021, 6:07:10 PM12/13/21
to fltk.general
Sorry I mean't the base class's resize method. 

Is there a way to modify posted messages?

Albrecht Schlosser

unread,
Dec 13, 2021, 6:42:38 PM12/13/21
to fltkg...@googlegroups.com
On 12/14/21 12:07 AM Mo_Al_ wrote:
> Sorry I mean't the base class's resize method.
>
> Is there a way to modify posted messages?

No.

Mark

unread,
Dec 14, 2021, 3:28:58 AM12/14/21
to fltk.general
The code worked correctly on Windows as is. However, I have now switched to using Window::new() and it works on both Linux and Windows. I've also pushed the change.

Once again, thank you all for your help!

Ian MacArthur

unread,
Dec 14, 2021, 3:32:17 AM12/14/21
to fltk.general
On Monday, 13 December 2021 at 20:57:09 UTC Mo wrote:
make_modal() just calls Fl_Window::set_modal and set_non_modal depending on the flag. 

OK - but for the avoidance of any doubt here, I feel it is worth emphasising that the "modal-ness" of a window has three states, not two, so a binary true/false option may not cover it. Does the Rust port also provide a method to remove the "modality" altogether and set the window back to normal?  (cf;  Fl_Window::clear_modal_states(); )

For those following along who may be unfamiliar with the (somewhat odd, in my opinion) terminology, it comes out as something like:

modal - a window that remains on top of its parent, and grabs the focus so that all interactions go to that window rather than the parent (menus, alert dialogs, etc.)

non-modal -  a window that remains on top of its parent, but DOES NOT grab the focus so  that the parent can still be used (toolboxes, typically and other types of dialog)

normal - a regular window, that does not remain on top or prevent access to its peers: this is the default for a window


So... I'm still not really clear on why Mark had a non-modal window at all; it seemed like a normal window would have been what was wanted?

Anyway, notwithstanding, it sounds like it has been sorted out anyway, which is good.

Mark

unread,
Dec 14, 2021, 3:39:18 AM12/14/21
to fltk.general
I'm used to your definition of modal (which I call application-modal). There's also system-modal (e.g., a login dialog) which normal applications should never use. And for me your normal is what I call modeless. In this case I use a modal dialog for the application's about box (which is traditional), and a modeless dialog for the application's help window, so that the user can still interact with the main application while seeing the help text.

And yes, it is all working now. I've just got one more (modal) dialog to go.

Mohammed

unread,
Dec 14, 2021, 4:09:38 AM12/14/21
to fltkg...@googlegroups.com
I wasn’t aware of the 3 states of modality, but I’ve added Window::clear_modal_states() in the latest release 1.2.21. 

Sent from my iPhone

Ian MacArthur

unread,
Dec 14, 2021, 11:42:23 AM12/14/21
to fltk.general
On Tuesday, 14 December 2021 at 08:39:18 UTC Mark wrote:
I'm used to your definition of modal (which I call application-modal). There's also system-modal (e.g., a login dialog) which normal applications should never use. And for me your normal is what I call modeless.

I think the terminology - at least the term "non-modal" comes from MS, I'm not sure, but I do find it somewhat unhelpful.
 
In this case I use a modal dialog for the application's about box (which is traditional), and a modeless dialog for the application's help window, so that the user can still interact with the main application while seeing the help text.

OK - and your "modeless" is my "normal" I think?
FWIW, I often make a "help" window as "non-modal" so that it will stay floating over the parent, but not block input to it, rather than "normal" here.


Ian MacArthur

unread,
Dec 14, 2021, 11:43:48 AM12/14/21
to fltk.general
On Tuesday, 14 December 2021 at 09:09:38 UTC Mo wrote:
I wasn’t aware of the 3 states of modality, but I’ve added Window::clear_modal_states() in the latest release 1.2.21. 

Cool: As will be clear from this thread, I find the terminology surrounding the "modality" of the windows somewhat confusing...

 

Mark

unread,
Dec 15, 2021, 4:21:17 AM12/15/21
to fltk.general
Here's my full list of modalities:
- Modeless - a modeless window supports interaction when it has the focus but does not grab or block interaction with any other window in its own or other applications.
- Window Modal - has no effect on other windows in other applications or in this application. Used by pop up menus or other interactive popups (e.g., completion). Normally this is handled automatically by the GUI library's pop-up API.
- Modal - has no effect on other windows in other applications or in this application's windows in the same tree: here 'tree' is the top-level window and any child windows the modal dialog has been invoked on. So, for example, if you have an application and use it to open two top-level windows (e.g., two document windows), and use a modal dialog in one of those windows (e.g., a Find dialog), the modal restriction will only apply to the document window (or child) it is invoked on, and  _not_ apply to the other document window (or its children).
- Application Modal - has no effect on other windows in other applications, but applies to _all_ this application's windows, even in the case of multiple top-level windows. Rarely used.
- System Modal - affects _all_ windows in _all_ applications; normally only used for the system login dialog. Dangerous to use this level of modality because a bug could make the entire system unusable.

Modeless windows are often convenient for users, but can be harder to program since state they rely on may be changed outside of them. Conversely modal windows can be inconvenient for users -- they don't allow interaction with other windows (in the same tree) -- but are much easier to program because state can't change from under them.

Bill Spitzak

unread,
Dec 15, 2021, 11:50:25 AM12/15/21
to fltkg...@googlegroups.com
This is pretty accurate. What FLTK is *trying* to get I will try to describe, the problem is all the window systems (originally Windows but X window managers have gotten increasingly bad) do not provide what is wanted, and FLTK has to deal with whatever is closest:

On Wed, Dec 15, 2021 at 1:21 AM 'Mark' via fltk.general <fltkg...@googlegroups.com> wrote:
Here's my full list of modalities:
- Modeless - a modeless window supports interaction when it has the focus but does not grab or block interaction with any other window in its own or other applications.

This is a normal window. Ideally the only thing that will change the stacking order of this window and others is if the user clicks on the titlebar or clicks on a "dead" area (no widget), or the clicked-on widget thinks some currenty-obscured area should be visible, in which case the window is raised to the top of the stack (except for any modal windows, see below). On modern systems this exact behavior is usually impossible, and clicking anywhere always raises the window even when not wanted (which makes it a real pain to do drag & drop!).

- Window Modal - has no effect on other windows in other applications or in this application. Used by pop up menus or other interactive popups (e.g., completion). Normally this is handled automatically by the GUI library's pop-up API.

The primary purpose is "make a window without a window border". It also often stays on top, though most modern api's have a click anywhere outside of this dismiss the window so it does not matter if stay-on-top does not work as the window would go away at the same time it is buried. There is also often some kludge needed to detect if the user clicks on the desktop or another application's window so the window will go away. Getting tihe window to appear exactly where FLTK wants it is also assumed and sometimes a pain to implement.
 
- Modal - has no effect on other windows in other applications or in this application's windows in the same tree: here 'tree' is the top-level window and any child windows the modal dialog has been invoked on. So, for example, if you have an application and use it to open two top-level windows (e.g., two document windows), and use a modal dialog in one of those windows (e.g., a Find dialog), the modal restriction will only apply to the document window (or child) it is invoked on, and  _not_ apply to the other document window (or its children).

This is the primary goal FLTK wants. Any attempt to raise the "parent" window will also raise this one so it is on top. Raising this one should *not* raise the parent, however.
 
- Application Modal - has no effect on other windows in other applications, but applies to _all_ this application's windows, even in the case of multiple top-level windows. Rarely used.

Usually this is what the window system provides and FLTK has to use it for modal windows. The parent of the modal window is ignored and instead all windows of the application act like parent.

- System Modal - affects _all_ windows in _all_ applications; normally only used for the system login dialog. Dangerous to use this level of modality because a bug could make the entire system unusable.

FLTK cannot make these windows.

In addition there is what FLTK is calling "non-modal". Most of these also cause all events to be sent to the window. FLTK however just pretends keyboard events are going to the window it thinks they should go to, even if the operating system sends them to this window. So "non modal" is used when you want the stacking behavior but don't want to block interaction with the other windows.
 
I've complained about this situation forever but they are never going to fix this. All that is really needed is that a window should *never* raise automatically, the application has to be able to say when it is raised. And a "raise" should be able to say "put it just below this other window". All window stacking and modality can be done by the app and everything would be much much simpler and usable. But it looks hopeless.

Reply all
Reply to author
Forward
0 new messages