Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_gui_frameless_drag.c

A press on a widget in a movable frameless window is not a window drag (headless).

A press on a widget in a movable frameless window is not a window drag (headless). A frameless window without N_GUI_WIN_FIXED_POSITION is dragged by its body. The press handler used to start that drag after handing the press to the widget under the pointer, so a press on a slider started both: the window then followed the pointer exactly, the slider's position inside its own window never changed, and the slider did not move at all. The same held for a scrollbar and for anything else that is dragged.

The rule now is that a press a widget took is not a window drag. A press on the empty body still is, and so is a press on a widget that does nothing with a press - here an image - so a window made mostly of pictures can still be moved.

Author
Castagnier Mickael
Version
1.0
Date
22/09/2026
/*
* Nilorea Library
* Copyright (C) 2005-2026 Castagnier Mickael
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define ALLEGRO_UNSTABLE 1
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "nilorea/n_gui.h"
#include "nilorea/n_log.h"
static int failures = 0;
static int slider_changes = 0;
static void expect_true(const char* label, int cond) {
if (!cond) {
n_log(LOG_ERR, "%s: FAILED", label);
} else {
n_log(LOG_INFO, "%s: OK", label);
}
}
static void on_slider(int id, double value, void* data) {
(void)id;
(void)value;
(void)data;
}
static ALLEGRO_EVENT mouse_event(int type, int x, int y) {
ALLEGRO_EVENT ev;
memset(&ev, 0, sizeof(ev));
ev.type = (unsigned int)type;
ev.mouse.x = x;
ev.mouse.y = y;
ev.mouse.button = 1;
return ev;
}
static void drag_right(N_GUI_CTX* gui, int x, int y, int step) {
int i = 0;
n_gui_process_event(gui, mouse_event(ALLEGRO_EVENT_MOUSE_BUTTON_DOWN, x, y));
for (i = 1; i <= 3; i++) {
n_gui_process_event(gui, mouse_event(ALLEGRO_EVENT_MOUSE_AXES, x + i * step, y));
}
n_gui_process_event(gui, mouse_event(ALLEGRO_EVENT_MOUSE_BUTTON_UP, x + 3 * step, y));
}
int main(void) {
ALLEGRO_FONT* font;
ALLEGRO_BITMAP* pic;
int win, slider;
double before;
if (!al_init() || !al_init_font_addon()) {
n_log(LOG_ERR, "allegro init failed");
return 1;
}
font = al_create_builtin_font(); /* no display needed */
if (!font) {
n_log(LOG_ERR, "al_create_builtin_font failed");
return 1;
}
/* a memory bitmap, since there is no display to make a video one on */
al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP);
pic = al_create_bitmap(16, 16);
if (!pic) {
n_log(LOG_ERR, "al_create_bitmap failed");
al_destroy_font(font);
return 1;
}
if (!gui) {
n_log(LOG_ERR, "n_gui_new_ctx failed");
al_destroy_bitmap(pic);
al_destroy_font(font);
return 1;
}
n_gui_set_display_size(gui, 800.0f, 600.0f);
win = n_gui_add_window(gui, "W", 0.0f, 0.0f, 400.0f, 300.0f);
/* frameless and NOT fixed: the body is a drag handle for the window */
slider = n_gui_add_slider(gui, win, 0.0f, 0.0f, 200.0f, 24.0f, 0.0, 1.0, 0.0,
n_gui_add_image(gui, win, 0.0f, 100.0f, 50.0f, 50.0f, pic, N_GUI_IMAGE_FIT);
w = n_gui_get_window(gui, win);
if (!w) {
n_log(LOG_ERR, "n_gui_get_window failed");
al_destroy_bitmap(pic);
al_destroy_font(font);
return 1;
}
/* A drag on the slider moves the slider and leaves the window where it
is. Before the fix the window moved by exactly the pointer's travel and
the slider stayed at its first value. */
n_gui_slider_set_value(gui, slider, 0.0);
before = n_gui_slider_get_value(gui, slider);
drag_right(gui, 40, 12, 20);
expect_true("a drag on a slider leaves the window where it was",
w->x == 0.0f && w->y == 0.0f);
expect_true("and moves the slider", n_gui_slider_get_value(gui, slider) > before);
/* one for the press and one per motion event */
expect_true("and reports every step of it", slider_changes >= 4);
/* The empty body is still a handle for the window. */
drag_right(gui, 300, 250, 10);
expect_true("a drag on the empty body moves the window", w->x == 30.0f && w->y == 0.0f);
/* So is an image, which does nothing with a press. The window is now 30
to the right, so the image is too. */
drag_right(gui, 30 + 20, 120, 10);
expect_true("a drag on an image moves the window", w->x == 60.0f);
al_destroy_bitmap(pic);
al_destroy_font(font);
if (failures) {
n_log(LOG_ERR, "ex_gui_frameless_drag: %d failure(s)", failures);
return 1;
}
n_log(LOG_NOTICE, "ex_gui_frameless_drag: all checks passed");
return 0;
}
static int failures
Definition ex_atlas.c:51
int main(void)
Definition ex_atlas.c:117
static void expect_true(const char *label, int cond)
Definition ex_atlas.c:53
static ALLEGRO_EVENT mouse_event(int type, int x, int y, int dz)
Build a left-button mouse event at (x, y).
static N_GUI_CTX * gui
static int slider_changes
How many times the slider's callback fired.
static void drag_right(N_GUI_CTX *gui, int x, int y, int step)
Press at (x, y), move right in three steps of step with the button held, and release where the pointe...
static void on_slider(int id, double value, void *data)
Slider callback: counts the changes it is told about.
static void type(N_GUI_CTX *gui, const char *s)
Type a whole string into whatever has focus.
FONT * font
the default 8x8 font, valid after allegro_init()
float x
position x on screen
Definition n_gui.h:1312
float y
position y on screen
Definition n_gui.h:1314
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.
Definition n_gui.c:12348
#define N_GUI_IMAGE_FIT
scale to fit within bounds, keep aspect ratio
Definition n_gui.h:204
int n_gui_add_slider(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, double min_val, double max_val, double initial, int mode, void(*on_change)(int, double, void *), void *user_data)
Add a slider widget.
Definition n_gui.c:3106
#define N_GUI_SLIDER_VALUE
slider uses raw start/end values
Definition n_gui.h:178
int n_gui_process_event(N_GUI_CTX *ctx, ALLEGRO_EVENT event)
Process an allegro event through the GUI system.
Definition n_gui.c:13611
double n_gui_slider_get_value(N_GUI_CTX *ctx, int widget_id)
get the current value of a slider widget
Definition n_gui.c:3962
#define N_GUI_WIN_FRAMELESS
frameless window: no title bar drawn, drag via window body unless N_GUI_WIN_FIXED_POSITION is also se...
Definition n_gui.h:276
int n_gui_add_image(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, ALLEGRO_BITMAP *bitmap, int scale_mode)
Add an image display widget.
Definition n_gui.c:3610
N_GUI_WINDOW * n_gui_get_window(N_GUI_CTX *ctx, int window_id)
Get a window pointer by id.
Definition n_gui.c:1397
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.
Definition n_gui.c:1277
N_GUI_CTX * n_gui_new_ctx(ALLEGRO_FONT *default_font)
Create a new GUI context.
Definition n_gui.c:1103
void n_gui_destroy_ctx(N_GUI_CTX **ctx)
Destroy a GUI context and all its windows/widgets.
Definition n_gui.c:1209
void n_gui_open_window(N_GUI_CTX *ctx, int window_id)
Open (show) a window.
Definition n_gui.c:1436
void n_gui_slider_set_value(N_GUI_CTX *ctx, int widget_id, double value)
set the value of a slider widget
Definition n_gui.c:3976
void n_gui_window_set_flags(N_GUI_CTX *ctx, int window_id, int flags)
Set feature flags on a window.
Definition n_gui.c:2515
The top-level GUI context that holds all windows.
Definition n_gui.h:1669
A pseudo window that contains widgets.
Definition n_gui.h:1298
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:113
#define LOG_ERR
error conditions
Definition n_log.h:100
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL ).
Definition n_log.c:123
#define LOG_NOTICE
normal but significant condition
Definition n_log.h:104
#define LOG_INFO
informational
Definition n_log.h:106
Common headers and low-level functions & define.
GUI system: buttons, sliders, text areas, checkboxes, scrollbars, dropdown menus, windows.
Generic log system.