Was looking for some minimalistic C++ GUI framework for linux, so here I am trying to get familiar with FLTK 1.3 / Ubuntu. Wrote some WORKING code that animates some random signal-wave-like with random colors at 30 FPS. And it works with some little problem: absolutely no idea where that wave will be drawn. In the window or on the button or in the dialog box (when it appears).
Seems like I do not understand how to define some kind of "context" for fl_line()-like functions. Each time fl_like() is called, the line is drawn in some unpredicted place.
When you put focus on button, the lines start drawing on that button. When you invoke the dialog box, you got lines there, moreover the buttons on that dialog gets overwritten by lines too! When you close the dialog box, you got these lines writing only over the button "Close".
So, my question is how to predict a "visual context" (is there a term like this in FLTK) the lines will be painted on.
My code:
#include <FL/Fl.H>
#include <FL/Fl_Button.H>
#include <FL/fl_message.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Double_Window.H>
#include <FL/fl_draw.H>
#include <cstdlib>
namespace simulator {
class Graph : public Fl_Widget {
unsigned char color_[3] = {0, 0, 0};
public:
Graph(int X, int Y, int W, int H)
: Fl_Widget(X, Y, W, H) {
}
void draw(void) {
this->plot();
}
void plot() {
// Pick some trash color in a trash way.
color_[0] = rand() % 256;
color_[1] = rand() % 256;
color_[2] = rand() % 256;
fl_color(color_[0], color_[1], color_[2]);
// Draw some random "audio wave"-like thing
// Dont care about cleaning the scene before drawing.
int old[2] = {0, 0};
for (int i = 0; i < 400; ++i) {
auto x = i * 2;
auto y = rand() % 400;
fl_line(old[0], old[1], x, y);
old[0] = x;
old[1] = y;
}
}
};
class Main_Window : public Fl_Window {
//public Fl_Double_Window {
Fl_Button button_ {10, 10, 75, 25, "Close"};
Graph graph_;
public:
Main_Window()
//: Fl_Double_Window(200, 100, 1200, 800, "Simulator")
: Fl_Window(200, 100, 1200, 800, "Simulator")
, graph_(10, 100, 500, 500) {
begin();
Fl::visual(FL_RGB);
button_.align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_CLIP | FL_ALIGN_WRAP);
// Button will invoke dialog "Want to exit?" with "yes/no" buttons.
button_.callback([](Fl_Widget* sender, void* window) {
printf("callback called, sender %p, window %p\n", sender, window);
reinterpret_cast<Main_Window*>(window)->hide();
}, this);
// Maybe this is some illegal/unneeded/not-perfect widget adding code, and maybe widget adding is not needed at all; Lets forget about this for now.
add(&button_);
add(&graph_);
end();
show();
graph_.show();
}
void hide() override {
fl_message_hotspot(false);
fl_message_title("Close window");
if (fl_choice("Are you sure you want exit?", "No", "Yes", nullptr) == 1) {
Fl_Window::hide();
}
}
void plot() {
graph_.plot();
}
};
} // namespace
// 30 FPS wave animation.
void winUpdate(void *_data) {
auto *window = reinterpret_cast<simulator::Main_Window*>(_data);
Fl::add_timeout(1.0 / 30.0, winUpdate, window);
window->plot();
}
int main(int argc, char *argv[]) {
simulator::Main_Window window;
window.show(argc, argv);
Fl::add_timeout(1.0 / 30.0, winUpdate, &window);
return Fl::run();
}
Build command:
/usr/bin/x86_64-linux-gnu-g++-12 test.cpp -o test -I/usr/include/c++/12 -std=c++20 -Wuninitialized -Wall -Wextra -Werror -Wno-deprecated-declarations -Wno-sign-compare -Wno-type-limits -Wunused-function -ggdb -fno-strict-aliasing -fwrapv -fno-stack-protector -fno-strict-overflow -Wfatal-errors `fltk-config --ldflags` -lfltk -lfltk_images -ljpeg -lstdc++ -lXfixes -lXext
Found the right direction. If you want redraw a widget, never call
draw()or some drawing functions likefl_line()directly. You rather callwidget->redraw()and FLTK will schedule repainting that widget properly.So, in my code,
Main_Window::plot()must dograph_.redraw();instead ofgraph_.plot();.