Search code examples
c++resizedragfltk

FLTK: how to make widgets resizable by dragging?


In FLTK, is there a way to let users resize widgets during runtime by dragging the borders of the widget's box? I mean, for instance, to resize a Fl_Text_Display or a Fl_Box or a Fl_Pack the same way we usually to that for a window in any OS?

I have gone through all the demos that come with FLTK and I have done quite a bit of searching, but only found examples of resizing via code or for user to resize via button-clicking. I could not find anything that points me to the right direction of how to make the borders of an widget become draggable in order to make widgets resizable by dragging.


Solution

  • You can explore Fl_Group and Fl_Tile resizing capabilities, perhaps using them as wrappers. Unfortunately FLTK doesn't support this out of box for every widget, so you have to make it on your own. If you only need to make a couple of custom widgets, the code below gives you the general idea to start off:

    #include <FL/Fl.H>
    #include <FL/Fl_Double_Window.H>
    #include <FL/Fl_Box.H>
    #include <FL/fl_draw.H>
    #include <cmath>
    
    class Resizeable : public Fl_Box
    {
    public:
        Resizeable(int X, int Y, int W, int H)
            : Fl_Box(X, Y, W, H, "Resize Me") {}
    
    private:
        bool can_resize;
        bool is_on_right_bottom_corner;
    
        void draw()
        {
            Fl_Box::draw();
            fl_rect(x(), y(), w(), h(), FL_RED);
            int bottom_right_x = w() + x();
            int bottom_right_y = h() + y();
            fl_polygon(bottom_right_x - 6, bottom_right_y - 1,
                    bottom_right_x - 1, bottom_right_y - 6,
                    bottom_right_x -1, bottom_right_y - 1);
        }
    
        int handle(int event)
        {
            switch (event) {
            case FL_PUSH: {
                can_resize = is_on_right_bottom_corner;
                return 1;
            }
            case FL_RELEASE:
                can_resize = false;
                return 1;
            case FL_DRAG: {
                if (can_resize) {
                    int X = Fl::event_x();
                    int Y = Fl::event_y();
                    int W = X > x() + 1 ? X - x() : w();
                    int H = Y > y() + 1 ? Y - y() : h();
                    size(W, H);
                    parent()->redraw();
                }
                return 1;
            }
            case FL_MOVE: {
                int dist_right_border  = std::abs(x() + w() - Fl::event_x());
                int dist_bottom_border = std::abs(y() + h() - Fl::event_y());
                is_on_right_bottom_corner = (dist_right_border < 10 && dist_bottom_border < 10);
                window()->cursor(is_on_right_bottom_corner ? FL_CURSOR_SE : FL_CURSOR_DEFAULT);
                return 1;
            }
            case FL_ENTER:
                return 1;
            case FL_LEAVE:
                window()->cursor(FL_CURSOR_DEFAULT);
                return 1;
            }
            return 0;
        }
    };
    
    int main()
    {
        Fl_Double_Window win(300, 300, "Resize Example");
        Resizeable res(50, 50, 100, 40);
        win.show();
        return Fl::run();
    }