34#include <allegro5/allegro_primitives.h>
42#define N_GUI_COLOR_W 300.0f
44#define N_GUI_COLOR_ROW 24.0f
46#define N_GUI_COLOR_LABEL 18.0f
48#define N_GUI_COLOR_SWATCH 48.0f
50#define N_GUI_COLOR_PAD 10.0f
52#define N_GUI_COLOR_BTN_W 90.0f
55#define N_GUI_COLOR_CHECK 6.0f
89 return v < 0.0f ? 0.0f : (v > 1.0f ? 1.0f : v);
93 ALLEGRO_MOUSE_CURSOR* cursor = NULL;
94 ALLEGRO_BITMAP* bitmap = NULL;
95 ALLEGRO_BITMAP* previous = al_get_target_bitmap();
97 int size = (int)(32.0f * scale + 0.5f);
98 float s = 0.0f, tip_x = 0.0f, tip_y = 0.0f, neck_x = 0.0f, neck_y = 0.0f, bulb_x = 0.0f, bulb_y = 0.0f;
100 if (size < 16) size = 16;
101 s = (float)size / 32.0f;
102 al_store_state(&state, ALLEGRO_STATE_NEW_BITMAP_PARAMETERS | ALLEGRO_STATE_BLENDER);
105 al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP);
106 bitmap = al_create_bitmap(size, size);
108 al_restore_state(&state);
109 n_log(
LOG_ERR,
"eyedropper cursor: could not create a %dx%d bitmap", size, size);
112 al_set_target_bitmap(bitmap);
113 al_clear_to_color(al_map_rgba(0, 0, 0, 0));
114 al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_INVERSE_ALPHA);
118 tip_y = (float)size - 1.5f * s;
126 al_draw_line(tip_x, tip_y, neck_x, neck_y, al_map_rgb(0, 0, 0), 6.0f * s);
127 al_draw_line(tip_x + 1.0f * s, tip_y - 1.0f * s, neck_x, neck_y, al_map_rgb(235, 240, 245), 3.0f * s);
129 al_draw_line(neck_x - 3.5f * s, neck_y - 3.5f * s, neck_x + 3.5f * s, neck_y + 3.5f * s, al_map_rgb(0, 0, 0), 4.0f * s);
130 al_draw_line(neck_x - 2.5f * s, neck_y - 2.5f * s, neck_x + 2.5f * s, neck_y + 2.5f * s, al_map_rgb(235, 240, 245), 1.5f * s);
132 al_draw_filled_circle(bulb_x, bulb_y, 6.0f * s, al_map_rgb(255, 255, 255));
133 al_draw_filled_circle(bulb_x, bulb_y, 5.0f * s, al_map_rgb(0, 0, 0));
134 al_draw_filled_circle(bulb_x, bulb_y, 3.5f * s, al_map_rgb(70, 70, 80));
136 al_draw_filled_rectangle(0.0f, tip_y - 1.5f * s, 2.0f * s, (
float)size, al_map_rgb(0, 0, 0));
138 al_set_target_bitmap(previous);
139 al_restore_state(&state);
140 cursor = al_create_mouse_cursor(bitmap, 0, size - 1);
141 al_destroy_bitmap(bitmap);
142 if (!cursor)
n_log(
LOG_ERR,
"eyedropper cursor: Allegro could not make a cursor");
147 int rows = 0, cols = 0,
row = 0;
149 if (!rgba || w <= 0.0f || h <= 0.0f)
return;
159 for (
int col = 0; col < cols; col++) {
162 float grey = ((
row + col) & 1) ? 0.8f : 0.55f;
164 al_draw_filled_rectangle(cx, cy, x2, y2, al_map_rgb_f(
grey,
grey,
grey));
169 al_draw_rectangle(x + 0.5f, y + 0.5f, x + w - 0.5f, y + h - 0.5f, al_map_rgba(0, 0, 0, 160), 1.0f);
173 unsigned byte[4] = {0, 0, 0, 0};
176 if (!out || len == 0)
return;
181 for (c = 0; c < 4; c++)
byte[c] = (
unsigned)lroundf(
_n_gui_color_clamp(rgba[c]) * 255.0f);
182 snprintf(out, len,
"#%02X%02X%02X%02X",
byte[0],
byte[1],
byte[2],
byte[3]);
192 if (!widget || !picker)
return;
209 if (!picker || picker->
syncing)
return;
210 for (c = 0; c < 4; c++) {
211 if (picker->
sl[c] != widget_id)
continue;
213 picker->
rgba[c] = (float)lround(value) / 255.0f;
237 float pad = 0.0f,
row = 0.0f, label = 0.0f, swatch_h = 0.0f, client_w = 0.0f, client_h = 0.0f, inner = 0.0f, y = 0.0f;
241 if (scale <= 0.0f) scale = 1.0f;
251 inner = client_w - 2.0f * pad;
253 client_h = pad + label + swatch_h + pad + 4.0f * (label +
row) + pad +
row + pad;
272 for (c = 0; c < 4; c++) {
275 picker->
sl[c] =
n_gui_add_slider(ctx, picker->
window, pad, y, inner,
row, 0.0, 255.0, 255.0,
N_GUI_SLIDER_VALUE,
_n_gui_color_on_slider, picker);
277 if (picker->
lbl[c] < 0 || picker->
sl[c] < 0)
break;
280 n_gui_set_widget_tooltip(ctx, picker->
sl[c], c < 3 ?
"How much of this colour, 0 to 255" :
"How solid, 0 see-through to 255 opaque");
285 if (c < 4 || picker->lbl_what < 0 || picker->swatch < 0 || picker->btn_close < 0) {
286 n_log(
LOG_ERR,
"color picker: a widget could not be created");
294 for (c = 0; c < 4; c++) picker->
rgba[c] = 1.0f;
300 float dw = 0.0f, dh = 0.0f;
311 for (c = 0; c < 4; c++) {
321 if ((dw <= 0.0f || dh <= 0.0f) && al_get_current_display()) {
322 dw = (float)al_get_display_width(al_get_current_display());
323 dh = (float)al_get_display_height(al_get_current_display());
326 if (win && dw > 0.0f && dh > 0.0f) {
327 float x = (dw - win->
w) * 0.5f;
328 float y = (dh - win->
h) * 0.5f;
347 if (!picker)
return 0;
356 for (c = 0; c < 4; c++) rgba[c] = picker->
rgba[c];
361 return picker ? picker->
window : -1;
365 if (!picker || channel < 0 || channel > 3)
return -1;
366 return picker->
sl[channel];
370 if (!picker || !*picker)
return;
static void on_change(const float rgba[4], void *user_data)
the picker's callback
static ALLEGRO_COLOR grey(float u, float v, void *user_data)
a caller's colour: grey everywhere
static const char * row(N_GUI_TREE *tree, int index)
the text of a visible row, "" past the end
FONT * font
the default 8x8 font, valid after allegro_init()
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
#define __n_assert(__ptr, __ret)
macro to assert things
#define Free(__ptr)
Free Handler to get errors.
float titlebar_h
title bar height
float display_w
display/viewport width (set via n_gui_set_display_size)
float display_h
display/viewport height
#define N_GUI_ALIGN_LEFT
left aligned text
void n_gui_raise_window(N_GUI_CTX *ctx, int window_id)
Bring a window to the front (top of draw order).
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.
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_label_set_text(N_GUI_CTX *ctx, int widget_id, const char *text)
set label text content
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.
#define N_GUI_SLIDER_VALUE
slider uses raw start/end values
int n_gui_add_label(N_GUI_CTX *ctx, int window_id, const char *text, float x, float y, float w, float h, int align)
Add a static text label.
void n_gui_close_window(N_GUI_CTX *ctx, int window_id)
Close (hide) a window.
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.
void n_gui_slider_set_step(N_GUI_CTX *ctx, int widget_id, double step)
set slider step increment.
int n_gui_window_is_open(N_GUI_CTX *ctx, int window_id)
Check if a window is currently visible.
N_GUI_WINDOW * n_gui_get_window(N_GUI_CTX *ctx, int window_id)
Get a window pointer by id.
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.
void n_gui_slider_set_value_visible(N_GUI_CTX *ctx, int widget_id, int visible)
Toggle the built-in value label.
void n_gui_window_set_close_callback(N_GUI_CTX *ctx, int window_id, void(*on_close)(int, void *), void *user_data)
Set the close button callback for a window.
void n_gui_open_window(N_GUI_CTX *ctx, int window_id)
Open (show) a window.
#define N_GUI_WIN_BTN_CLOSE
enable close button on the title bar
#define N_GUI_ZORDER_POPUP
popup/modal dialogs: drawn above EVERYTHING including ALWAYS_ON_TOP windows.
void n_gui_slider_set_value(N_GUI_CTX *ctx, int widget_id, double value)
set the value of a slider widget
#define N_GUI_SHAPE_ROUNDED
rounded rectangle shape
void n_gui_window_set_flags(N_GUI_CTX *ctx, int window_id, int flags)
Set feature flags on a window.
void n_gui_mark_dirty(N_GUI_CTX *ctx)
Mark the GUI as needing a redraw.
void n_gui_window_set_rect_axes(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, int scale_x, int scale_y)
n_gui_window_set_rect with per-axis control over the widget reflow.
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.
int n_gui_add_custom(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, N_GUI_CUSTOM_DRAW draw, void *user_data)
add an owner-draw custom widget; returns the widget id or -1
The top-level GUI context that holds all windows.
A pseudo window that contains widgets.
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_ERR
error conditions
int n_gui_color_picker_get(const N_GUI_COLOR_PICKER *picker, float rgba[4])
The colour the picker holds now.
void(* N_GUI_COLOR_FN)(const float rgba[4], void *user_data)
called each time the picked colour changes.
void n_gui_color_picker_open(N_GUI_COLOR_PICKER *picker, const char *what, const float rgba[4], N_GUI_COLOR_FN on_change, void *user_data)
Open the picker on a colour, centred on the display.
void n_gui_color_to_hex(const float rgba[4], char *out, size_t len)
Write a colour as #RRGGBBAA, each channel clamped to 0..1 and rounded to the nearest of 255 steps.
int n_gui_color_picker_window(const N_GUI_COLOR_PICKER *picker)
The picker's window id, for a caller that walks its windows or sets their flags.
int n_gui_color_picker_slider(const N_GUI_COLOR_PICKER *picker, int channel)
The slider for one channel, for a caller or a test that drives it.
N_GUI_COLOR_PICKER * n_gui_color_picker_new(N_GUI_CTX *ctx, const char *title, float scale)
Build a colour picker window in ctx, closed.
void n_gui_color_picker_close(N_GUI_COLOR_PICKER *picker)
Close the picker.
void n_gui_draw_color_swatch(float x, float y, float w, float h, const float rgba[4])
Draw a colour as a swatch, over a checkerboard so its alpha shows, with a thin dark border.
void n_gui_color_picker_free(N_GUI_COLOR_PICKER **picker)
Close the picker's window and free the handle.
int n_gui_color_picker_is_open(const N_GUI_COLOR_PICKER *picker)
Whether the picker is open.
ALLEGRO_MOUSE_CURSOR * n_gui_eyedropper_cursor(float scale)
Make a pipette mouse cursor, for a tool that picks a colour from an image.
Common headers and low-level functions & define.
static float _n_gui_color_clamp(float v)
helper: clamp a channel to 0..1
N_GUI_COLOR_FN on_change
whom to tell
float rgba[4]
the colour being edited
#define N_GUI_COLOR_SWATCH
height of the large swatch at scale 1
static void _n_gui_color_on_slider(int widget_id, double value, void *user_data)
slider callback: one channel moved
int lbl_what
what the colour is for
#define N_GUI_COLOR_CHECK
side of one checkerboard square behind a swatch, in pixels.
#define N_GUI_COLOR_W
window width at scale 1: wide enough that a slider moved by one step in 255 moves by at least a pixel
int btn_close
the Close button
static void _n_gui_color_on_close_button(int widget_id, void *user_data)
button callback: Close
void * user_data
handed back to on_change
static void _n_gui_color_draw_swatch(N_GUI_WIDGET *widget, float x, float y, ALLEGRO_FONT *font, void *user_data)
custom draw callback of the large swatch
static void _n_gui_color_on_close_window(int window_id, void *user_data)
title bar close: the same as the Close button, so the callback is forgotten either way
static const char *const _n_gui_color_channels[4]
the four channels, in the order of the sliders
int sl[4]
one slider per channel, 0 to 255
int syncing
non-zero while the sliders are set from code, so their callbacks are not taken for the user's
#define N_GUI_COLOR_PAD
padding at scale 1
#define N_GUI_COLOR_LABEL
height of a label at scale 1
int swatch
the large swatch
#define N_GUI_COLOR_ROW
height of a slider or button row at scale 1
N_GUI_CTX * ctx
the context the widgets live in
static void _n_gui_color_set_label(N_GUI_COLOR_PICKER *picker, int channel)
helper: write one channel's label, its name and its value out of 255
#define N_GUI_COLOR_BTN_W
width of the Close button at scale 1
int lbl[4]
name and value over each slider
a colour picker: the ids of its widgets and the colour being edited
A colour picker dialog for N_GUI, and a swatch to show a colour.