Tooltip bubble regression (headless): an in-app window does not narrow the bubble, style.tooltip_max_w caps how wide it runs, and a window opened over the pointer hides it instead of being drawn over.
Tooltip bubble regression (headless): an in-app window does not narrow the bubble, style.tooltip_max_w caps how wide it runs, and a window opened over the pointer hides it instead of being drawn over.
#define ALLEGRO_UNSTABLE 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define TT_W 1200
#define TT_H 400
#define PANEL_X 10.0f
#define PANEL_W 80.0f
#define SPLIT_X 400.0f
#define SPLIT_Y 100.0f
#define SPLIT_W 200.0f
#define SPLIT_H 120.0f
if (!cond) {
}
}
static void usage(
void) {
fprintf(stderr,
" -v version\n"
" -V log level: LOG_NULL, LOG_NOTICE, LOG_INFO, LOG_ERR, LOG_DEBUG\n"
" -h help\n");
}
while ((
getoptret = getopt(argc, argv,
"hvV:")) != EOF) {
case 'v':
fprintf(stderr, "build date: %s %s\n", __DATE__, __TIME__);
exit(0);
case 'V':
if (!strcmp("LOG_NULL", optarg))
else if (!strcmp("LOG_NOTICE", optarg))
else if (!strcmp("LOG_INFO", optarg))
else if (!strcmp("LOG_ERR", optarg))
else if (!strcmp("LOG_DEBUG", optarg))
else {
fprintf(stderr, "%s is not a valid log level\n", optarg);
exit(1);
}
break;
case 'h':
exit(0);
default:
case '?':
if (optopt == 'V') fprintf(stderr, "\n Missing log level\n");
exit(1);
}
}
}
al_set_target_bitmap(target);
al_clear_to_color(al_map_rgb(0, 0, 0));
gui->tooltip_widget_id = armed ? widget_id : -1;
if (armed) {
gui->tooltip_armed_at = al_get_time() - (double)
gui->style.tooltip_delay - 1.0;
}
}
static int bubble_width(ALLEGRO_BITMAP* a, ALLEGRO_BITMAP* b) {
ALLEGRO_LOCKED_REGION* la = al_lock_bitmap(a, ALLEGRO_PIXEL_FORMAT_ABGR_8888_LE, ALLEGRO_LOCK_READONLY);
ALLEGRO_LOCKED_REGION* lb = al_lock_bitmap(b, ALLEGRO_PIXEL_FORMAT_ABGR_8888_LE, ALLEGRO_LOCK_READONLY);
int min_x =
TT_W, max_x = -1;
if (la && lb) {
for (
int y = 0; y <
TT_H; y++) {
const unsigned char* ra = (const unsigned char*)la->data + (ptrdiff_t)y * la->pitch;
const unsigned char* rb = (const unsigned char*)lb->data + (ptrdiff_t)y * lb->pitch;
for (
int x = 0; x <
TT_W; x++) {
if (memcmp(ra + (ptrdiff_t)x * 4, rb + (ptrdiff_t)x * 4, 4) != 0) {
if (x < min_x) min_x = x;
if (x > max_x) max_x = x;
}
}
}
}
if (la) al_unlock_bitmap(a);
if (lb) al_unlock_bitmap(b);
return (max_x < 0) ? 0 : (max_x - min_x + 1);
}
ALLEGRO_EVENT ev;
memset(&ev, 0, sizeof(ev));
ev.type = ALLEGRO_EVENT_MOUSE_AXES;
ev.mouse.x = x;
ev.mouse.y = y;
return gui->tooltip_widget_id;
}
int main(
int argc,
char** argv) {
if (!al_init() || !al_init_font_addon()) {
return 1;
}
al_init_primitives_addon();
al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP);
ALLEGRO_BITMAP* plain = al_create_bitmap(
TT_W,
TT_H);
ALLEGRO_BITMAP* tipped = al_create_bitmap(
TT_W,
TT_H);
ALLEGRO_FONT*
font = al_create_builtin_font();
if (!plain || !tipped || !
font) {
return 1;
}
return 1;
}
"Promote this panel to a real OS window, and back again, "
"which is the sort of sentence a tooltip has to hold when it "
"explains something instead of restating the label beside it.");
const int btn_x = (int)(
PANEL_X + 5.0f + 15.0f);
const int btn_y = (int)(10.0f +
gui->style.titlebar_h + 5.0f + 10.0f);
{
}
{
gui->style.tooltip_max_w = 480.0f;
expect_true(
"bubble honours tooltip_max_w", capped <= 480 + 16 + 2);
expect_true(
"bubble still uses the width it is allowed", capped > 480 / 2);
gui->style.tooltip_max_w = 0.0f;
expect_true(
"tooltip_max_w = 0 restores wrap-at-the-clamp-edge", uncapped > capped);
n_log(
LOG_INFO,
"capped %d px, uncapped %d px", capped, uncapped);
gui->style.tooltip_max_w = 480.0f;
}
{
}
{
int divider_x = (int)(ox +
SPLIT_W * 0.5f);
expect_true(
"pointer on the divider arms the split pane",
expect_true(
"pointer well left of the divider arms nothing",
expect_true(
"pointer well right of the divider arms nothing",
expect_true(
"a widget above the pane still arms its own tooltip",
}
{
int menu = 0;
expect_true(
"nothing is pending while a window covers the pointer",
expect_true(
"a window that does not cover the pointer changes nothing",
}
{
ALLEGRO_EVENT ev;
int button = 0;
for (button = 1; button <= 3; button++) {
memset(&ev, 0, sizeof(ev));
ev.type = ALLEGRO_EVENT_MOUSE_BUTTON_DOWN;
ev.mouse.button = (unsigned)button;
ev.mouse.x = btn_x;
ev.mouse.y = btn_y;
expect_true(
"and a press on any button dismisses it",
gui->tooltip_widget_id == -1);
memset(&ev, 0, sizeof(ev));
ev.type = ALLEGRO_EVENT_MOUSE_BUTTON_UP;
ev.mouse.button = (unsigned)button;
ev.mouse.x = btn_x;
ev.mouse.y = btn_y;
}
}
al_destroy_bitmap(plain);
al_destroy_bitmap(tipped);
return 1;
}
return 0;
}
void process_args(int argc, char **argv)
static void expect_true(const char *label, int cond)
FONT * font
the default 8x8 font, valid after allegro_init()
void n_gui_set_display_size(N_GUI_CTX *ctx, float w, float h)
set the display (viewport) size so global scrollbars can be computed.
void n_gui_set_widget_tooltip(N_GUI_CTX *ctx, int widget_id, const char *text)
Set (or clear) the tooltip text of any widget.
void n_gui_window_set_rect(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h)
Move and resize a window at runtime, reflowing its widgets.
int n_gui_process_event(N_GUI_CTX *ctx, ALLEGRO_EVENT event)
Process an allegro event through the GUI system.
void n_gui_close_window(N_GUI_CTX *ctx, int window_id)
Close (hide) a window.
int n_gui_tooltip_pending(N_GUI_CTX *ctx)
Report the tooltip phase.
void n_gui_window_set_zorder(N_GUI_CTX *ctx, int window_id, int z_mode, int z_value)
Set window z-order mode and value.
int n_gui_add_splitpane(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, int orientation, float ratio, void(*on_change)(int, float, void *), void *user_data)
Add a split pane widget: a draggable divider between two regions.
#define N_GUI_WIN_FRAMELESS
frameless window: no title bar drawn, drag via window body unless N_GUI_WIN_FIXED_POSITION is also se...
void n_gui_draw(N_GUI_CTX *ctx)
draw all visible GUI windows and widgets
int n_gui_add_window(N_GUI_CTX *ctx, const char *title, float x, float y, float w, float h)
Add a new pseudo-window to the context.
N_GUI_CTX * n_gui_new_ctx(ALLEGRO_FONT *default_font)
Create a new GUI context.
#define N_GUI_SPLIT_VERTICAL
split pane: vertical divider, regions are left and right
void n_gui_destroy_ctx(N_GUI_CTX **ctx)
Destroy a GUI context and all its windows/widgets.
#define N_GUI_ZORDER_POPUP
popup/modal dialogs: drawn above EVERYTHING including ALWAYS_ON_TOP windows.
void n_gui_window_set_flags(N_GUI_CTX *ctx, int window_id, int flags)
Set feature flags on a window.
#define N_GUI_SHAPE_RECT
rectangle shape (default)
int n_gui_add_button(N_GUI_CTX *ctx, int window_id, const char *label, float x, float y, float w, float h, int shape, void(*on_click)(int, void *), void *user_data)
Add a button widget to a window.
The top-level GUI context that holds all windows.
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_DEBUG
debug-level messages
#define LOG_ERR
error conditions
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL ).
#define LOG_NOTICE
normal but significant condition
#define LOG_NULL
no log output
#define LOG_INFO
informational
Common headers and low-level functions & define.
GUI system: buttons, sliders, text areas, checkboxes, scrollbars, dropdown menus, windows.