rustfltk

How do I pin a frame "status bar" to the bottom of a resizable window?


I want to create a layout where window is resizable, the main frame is resizable with the window, a fixed status bar at the bottom of the window which only resizes horizontally and always stays stuck to the bottom.

I am new to fltk. I couldn't figure it out from the documentation.


Solution

  • This question is tagged with fltk and rust and the OP uses the term "frame" which is the fltk-rs equivalent to FLTK's "box" or "box type" (see demo code below).

    There's an entire chapter about resizing in the FLTK docs: "How Does Resizing Work?" and your particular question is almost described in "Resizing can be simple": basically you add one Fl_Group (main) as your main group ("frame") and adjacent to (i.e. below) main the status bar sb. Then you make your main group the resizable() of the window.

    I'm posting a working example in pure FLTK (C++) with the requested functionality that updates the status bar once every second. I'm leaving the port to Rust (fltk-rs) to the reader.

    //
    // Demo: Resizable Window with Status Bar
    //
    // License: Free to use, Public Domain, ...
    //
    
    #include <FL/Fl.H>
    #include <FL/Fl_Window.H>
    #include <FL/Fl_Box.H>
    #include <stdio.h>        // optional: timer demo
    
    const int ww = 600;       // window width
    const int wh = 400;       // window height
    const int sbh = 25;       // status bar height
    
    // Optional: timer demo
    // Write counter of seconds since start to the status bar
    void update_status(void *w) { // 
      static int sec = 3;
      char buf[40];
      sprintf(buf, "Time since start: %d seconds", sec++);
      Fl_Widget *sb = (Fl_Widget *)w;
      sb->copy_label(buf);  // update status (label)
      sb->redraw();         // draw it
      Fl::repeat_timeout(1.0, update_status, w); // repeat in 1 sec
    }
    
    int main(int argc, char **argv) {
      Fl_Window *win = new Fl_Window(ww, wh, "Resizable with Status Bar");
      // create main group (aka "frame")
      Fl_Group *main = new Fl_Group(0, 0, ww, wh - sbh);
      // add widgets inside the main group
      Fl_Box *box = new Fl_Box((ww-300)/2, (wh-sbh-60)/2, 300, 60, "in main group");
      box->box(FL_FLAT_BOX);
      box->color(FL_YELLOW);
      // more widgets here ...
      main->end();
      // create status bar outside main group
      Fl_Box *sb = new Fl_Box(0, wh - sbh, ww, sbh, "This is the status bar");
      sb->box(FL_DOWN_BOX);
      win->end();
      win->resizable(main);
      win->size_range(250, 150); // min. window size
      win->show(argc, argv);
      Fl::add_timeout(3.0, update_status, sb); // optional: timer demo
      return Fl::run();
    }
    

    The above example uses the "classic" resizing approach. Since FLTK 1.4 (current Git master) another simple approach would be to use an Fl_Flex widget with one column and two widgets:

    1. the main group (frame) as the top widget
    2. the status bar as the second widget.

    The status bar can be made "fixed" which makes the top (main) group consume the entire space minus the height of the status bar.

    Last but not least: the most flexible approach would be to use one or more (nested) Fl_Grid widget(s) for the layout (also available since FLTK 1.4.0). AFAICT all these widgets are available in fltk-rs as well.

    With a minimal change this can be extended to a fixed menu bar on top, a main group in the middle, and a fixed status bar at the bottom of the window.

    Update: Layout with fltk-rs and Flex widget:

    // ----- Cargo.toml -----

    [package]
    name    = "statusbar"
    version = "0.1.0"
    edition = "2021"
    
    [dependencies]
    fltk = { version = "^1.4", features = ["use-ninja", "use-wayland"] }
    

    // ----- src/main.rs -----

    //
    // Demo: fltk-rs Resizable Window with Status Bar
    //
    // License: Free to use, Public Domain, ...
    //
    
    use fltk::{
        app,
        group::{Group, Flex},
        prelude::{GroupExt, WidgetBase, WidgetExt, WindowExt},
        window::Window,
        enums::{Color, FrameType},
        frame::Frame,
    };
    
    fn main() {
        let app = app::App::default().with_scheme(app::Scheme::Oxy);
    
        let mut win = Window::default()
            .with_size(500, 450)
            .with_label("fltk-rs Layout Demo")
            .center_screen();
    
        let mut col = Flex::new(0, 0, 500, 450, "Flex").column();
        col.set_color(Color::White);
        col.set_frame(FrameType::FlatBox);
    
        // place holder for the top menubar
        let mut menubar = Frame::default().with_label("Menu Bar");
        menubar.set_frame(FrameType::FlatBox);
        menubar.set_color(Color::Yellow);
    
        // main group
        let group = Group::new(0, 40, 500, 370, "");
    
        let mut frame1 = Frame::new(60, 100, 200, 100, "Frame 1");
        frame1.set_color(Color::Red);
        frame1.set_frame(FrameType::FlatBox);
        frame1.set_label_color(Color::White);
    
        let mut frame2 = Frame::new(200, 260, 200, 100, "Frame 2");
        frame2.set_color(Color::Green);
        frame2.set_frame(FrameType::FlatBox);
    
        group.end();
    
        // place holder for the status bar
        let mut statusbar = Frame::default().with_label("Status Bar");
        statusbar.set_color(Color::Yellow);
        statusbar.set_frame(FrameType::FlatBox);
    
        col.end();
    
        // make menubar and statusbar "fixed"
        col.fixed(&menubar, 40);
        col.fixed(&statusbar, 40);
        // make window resizable with the 'col' group
        win.resizable(&col);
        win.make_resizable(true);
        win.end();
        win.show();
        // run the app ...
        app.run().unwrap();
    }
    

    Disclaimer: although I'm a FLTK (C++) core developer I'm a beginner with fltk-rs and thus the fltk-rs demo will likely be suboptimal. However, it works for me...