c++fltk

Why isn't the circle redrawn in the window? (Fltk/c++)


I wrote this code to make the circle move along the x axis as many pixels as those moved on the dial. All the variables seem fine but the circle never redraws itself.

I would like that by moving the dial the circle on the screen also moves at the same time, as if it were doing a shift.

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Widget.H>
#include <FL/fl_draw.H>
#include <FL/Fl_Dial.H>
#include <iostream>  // Necessario per std::cout

// Classe per il widget che disegnerà il cerchio
class CircleWidget : public Fl_Widget {
public:
    int radius;

    CircleWidget(int X, int Y, int R)
        : Fl_Widget(X, Y, 2 * R, 2 * R), radius(R) {}

    // Funzione di disegno del cerchio
    void draw() override {
        fl_color(FL_RED); // Colore del cerchio
        fl_circle(x() + radius, y() + radius, radius); // Disegna il cerchio
    }

    // Aggiorna la posizione orizzontale del cerchio e ridisegna
    void update_position(int new_x) {
        this->position(new_x, y()); // Cambia la posizione del widget lungo l'asse x
        redraw(); // Ridisegna il cerchio nella nuova posizione
    }
};

// Callback per il controllo tramite il Dial
void dial_callback(Fl_Widget* dial, void* circle_widget) {
    Fl_Dial* d = (Fl_Dial*)dial;
    CircleWidget* circle = (CircleWidget*)circle_widget;

    // Sposta il cerchio in base al valore del dial
    int new_x = (int)d->value();
    
    // Stampa il valore di new_x
    std::cout << "Valore di new_x: " << new_x << std::endl;

    circle->update_position(new_x); // Aggiorna la posizione orizzontale del cerchio
}

int main() {
    // Crea una finestra con sfondo nero
    Fl_Window* window = new Fl_Window(400, 300, "Dial e Cerchio");
    window->color(FL_BLACK);  // Imposta il colore di sfondo a nero

    // Crea un cerchio iniziale
    CircleWidget* circle = new CircleWidget(50, 50, 50); // Cerchio con raggio 50

    // Crea un dial
    Fl_Dial* dial = new Fl_Dial(300, 200, 80, 80, "Dial");
    dial->range(0, 300);  // Imposta il range del dial da 0 a 300
    dial->value(50);      // Imposta un valore iniziale
    dial->callback(dial_callback, circle); // Associa una callback per aggiornare il cerchio

    window->end();
    window->show();

    return Fl::run();
}

Solution

  • For me the circle moves when the dial is changed. However, its background is not redrawn and thus the previously drawn circles are not erased. Tested with FLTK 1.4 (git) and 1.3.9 (git branch-1.3) on Linux and Windows (cross-compiled using wine).

    You can fix this issue by adding parent()->redraw() in update_position(). Just in case you're using a similar approach in a project where the window is resizable you should also add parent()->init_sizes();. This is necessary whenever you reposition widgets inside their (resizable) parent groups. New code:

        void update_position(int new_x) {
            this->position(new_x, y()); // Cambia la posizione del widget lungo l'asse x
            redraw(); // Ridisegna il cerchio nella nuova posizione
            parent()->redraw();
            parent()->init_sizes();
        }
    

    However, this is not very efficient because the entire window is redrawn whenever the dial is changed. You can improve this by adding another group "around" the circle widget so only this group is redrawn, like this code in main():

        Fl_Group *cgroup = new Fl_Group(0, 50, window->w(), 2*50);
        cgroup->box(FL_FLAT_BOX);
        cgroup->color(FL_YELLOW);
    
        // Crea un cerchio iniziale
        CircleWidget* circle = new CircleWidget(50, 50, 50); // Cerchio con raggio 50
        cgroup->end();
    

    Note that I added a yellow background to cgroup to demonstrate where the group is located.

    Note also that it's possible to erase (i.e. redraw) only a specific area of a widget or window by using Fl_Widget::damage(...) but I'm leaving this to the reader.

    @OP: if this doesn't solve your issue, please add more info about your FLTK version and system (OS) platform.

    EDIT: this version works with Fl_Widget::damage() (showing only the modified method):

      void update_position(int new_x) {
        window()->damage(FL_DAMAGE_EXPOSE|FL_DAMAGE_CHILD, x() - 2,   y() - 2, w() + 4, h() + 4);
        window()->damage(FL_DAMAGE_EXPOSE|FL_DAMAGE_CHILD, new_x - 2, y() - 2, w() + 4, h() + 4);
        position(new_x, y()); // Cambia la posizione del widget lungo l'asse x
        parent()->init_sizes();
      }
    
    

    Note that this doesn't work as expected if the window is made resizable and is resized smaller than the original size but I'm not going to (try to) "fix" this since this code is only for demonstration purposes.