Much as I appreciate FLTK, it's ancient C heritage can result in some butt-ugly code for things like creating menus. In an attempt to ameliorate some of the pain in writing GUI code for our robowaifus that can run on the smallest potato, I'm starting a little side-project to wrap calls with simpler C++ ones.
>main.cpp
#include <array>
#include "Window.hpp"
using muh_window::Menu;
using muh_window::Menu_item;
using muh_window::Window;
auto setup_items() {
std::array<Menu_item, 4> items{"File", "Edit", "Tools", "Help"};
// DESIGN: configure all menu items (callbacks, etc) here...
return items;
}
int main() {
const auto win_title{"/robowaifu/ fltk base window/menu test"};
const int win_width{500};
const int win_height{180};
const int menu_height{25};
Window window{win_width, win_height, win_title};
Menu menu{win_width, menu_height};
menu.menu(setup_items().data());
// window.end();
// DESIGN: any add'l non-FLTK setup code would go here...
window.show();
return Fl::run();
}
>Window.hpp
#pragma once
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Menu_Bar.H>
namespace muh_window {
struct Point {
Point(const int x, const int y) : x{x}, y{y} {}
int x{0}, y{0};
};
struct Rect {
Rect(const Point ul_origin, const int w, const int h)
: ul_origin{ul_origin}, w{w}, h{h} {}
Point ul_origin;
int w{100}, h{100};
};
class Window : public Fl_Double_Window {
public:
Window() : Fl_Double_Window{0, 0, 100, 100, ""} {}
Window(const Rect& rect, const char* title)
: Fl_Double_Window{rect.ul_origin.x, rect.ul_origin.y, rect.w, rect.h,
title} {}
Window(const int w, const int h, const char* title)
: Fl_Double_Window{w, h, title} {}
};
// DESIGN: Use Fluid and see what it defines a menu as, then inherit/setup that
class Menu : public Fl_Menu_Bar {
public:
Menu(const Rect& rect)
: Fl_Menu_Bar{rect.ul_origin.x, rect.ul_origin.y, rect.w, rect.h} {}
Menu(const int x, const int y, const int w, const int h)
: Fl_Menu_Bar{x, y, w, h} {}
Menu(const int w, const int h) : Menu{0, 0, w, h} {}
};
class Menu_item : public Fl_Menu_Item {
public:
Menu_item(const char* label, const uchar fnt_sz)
// struct Fl_Menu_Item {
// const char* text; // label()
// ulong shortcut_;
// Fl_Callback* callback_;
// void* user_data_;
// int flags;
// uchar labeltype_;
// uchar labelfont_;
// uchar labelsize_;
// uchar labelcolor_;
// };
: Fl_Menu_Item{label, 0, 0, 0, 0, (uchar)FL_NORMAL_LABEL, 0, fnt_sz, 0} {}
Menu_item(const char* label) : Menu_item{label, 18} {}
};
}; // namespace muh_window
>meson.build
project('fltk_simple', 'cpp')
add_project_arguments('-std=c++20', '-Wall', '-Wextra', language: 'cpp')
cxx = meson.get_compiler('cpp')
fltk_dep = cxx.find_library('fltk')
executable('fltk_simple', 'main.cpp', dependencies : fltk_dep)
I personally consider functions like
auto setup_items() {
std::array<Menu_item, 4> items{"File", "Edit", "Tools", "Help"};
// DESIGN: configure all menu items (callbacks, etc) here...
return items;
}
and statements at the caller:
menu.menu(setup_items().data());
much cleaner and easier to reason about than typical FLTK examples.