Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_gui_slider_step.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
41
42#define ALLEGRO_UNSTABLE 1
43
44#include <math.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48
49#include "nilorea/n_common.h"
50#include "nilorea/n_gui.h"
51#include "nilorea/n_log.h"
52
54static int failures = 0;
55
61static void expect_true(const char* label, int cond) {
62 if (!cond) {
63 n_log(LOG_ERR, "%s: FAILED", label);
64 failures++;
65 } else {
66 n_log(LOG_INFO, "%s: OK", label);
67 }
68}
69
76static void expect_near(const char* label, double got, double want) {
77 if (fabs(got - want) > 1e-9) {
78 n_log(LOG_ERR, "%s: FAILED, got %g wanted %g", label, got, want);
79 failures++;
80 } else {
81 n_log(LOG_INFO, "%s: OK", label);
82 }
83}
84
90static ALLEGRO_EVENT key_event(int keycode) {
91 ALLEGRO_EVENT ev;
92 memset(&ev, 0, sizeof(ev));
93 ev.type = ALLEGRO_EVENT_KEY_CHAR;
94 ev.keyboard.keycode = keycode;
95 return ev;
96}
97
106static ALLEGRO_EVENT mouse_event(int type, int x, int y, int dz) {
107 ALLEGRO_EVENT ev;
108 memset(&ev, 0, sizeof(ev));
109 ev.type = (unsigned int)type;
110 ev.mouse.x = x;
111 ev.mouse.y = y;
112 ev.mouse.dz = dz;
113 ev.mouse.button = 1;
114 return ev;
115}
116
117int main(void) {
118 ALLEGRO_FONT* font;
119 N_GUI_CTX* gui;
120 int win, plain, keyed, snapped;
121
123 if (!al_init() || !al_init_font_addon()) {
124 n_log(LOG_ERR, "allegro init failed");
125 return 1;
126 }
127 font = al_create_builtin_font(); /* no display needed */
128 if (!font) {
129 n_log(LOG_ERR, "al_create_builtin_font failed");
130 return 1;
131 }
133 if (!gui) {
134 n_log(LOG_ERR, "n_gui_new_ctx failed");
135 al_destroy_font(font);
136 return 1;
137 }
138 n_gui_set_display_size(gui, 800.0f, 600.0f);
139 win = n_gui_add_window(gui, "W", 0.0f, 0.0f, 400.0f, 300.0f);
141
142 /* The range a photo editor's exposure control has: ten stops end to end.
143 A twentieth of that is half a stop, which is the behaviour this exists
144 to give a program a way out of. */
145 plain = n_gui_add_slider(gui, win, 0.0f, 0.0f, 200.0f, 20.0f, -5.0, 5.0, 0.0,
146 N_GUI_SLIDER_VALUE, NULL, NULL);
147 keyed = n_gui_add_slider(gui, win, 0.0f, 40.0f, 200.0f, 20.0f, -5.0, 5.0, 0.0,
148 N_GUI_SLIDER_VALUE, NULL, NULL);
149 snapped = n_gui_add_slider(gui, win, 0.0f, 80.0f, 200.0f, 20.0f, -5.0, 5.0, 0.0,
150 N_GUI_SLIDER_VALUE, NULL, NULL);
152
153 /* Nothing set: a twentieth of the range, exactly as before. */
154 expect_near("with neither set, a key moves a twentieth of the range",
155 n_gui_slider_get_key_step(gui, plain), 0.5);
156
157 /* The snapping step is still the fallback, so a caller that only ever set
158 that one sees no change either. */
159 n_gui_slider_set_step(gui, snapped, 0.25);
160 expect_near("the snapping step is the next fallback",
161 n_gui_slider_get_key_step(gui, snapped), 0.25);
162
163 /* And the new number wins over both. */
164 n_gui_slider_set_key_step(gui, keyed, 1.0 / 3.0);
165 expect_near("a key step of its own wins", n_gui_slider_get_key_step(gui, keyed),
166 1.0 / 3.0);
167
168 /* Setting it must not move the value, and must not snap it: a third of a
169 stop is not a divisor of anything here. */
170 n_gui_slider_set_value(gui, keyed, 1.1);
171 n_gui_slider_set_key_step(gui, keyed, 1.0 / 3.0);
172 expect_near("setting it leaves the value alone", n_gui_slider_get_value(gui, keyed), 1.1);
173
174 /* One arrow press moves by the key step and not by a twentieth. */
175 n_gui_slider_set_value(gui, keyed, 0.0);
176 n_gui_set_focus(gui, keyed);
177 n_gui_process_event(gui, key_event(ALLEGRO_KEY_RIGHT));
178 expect_near("one arrow press moves by the key step",
179 n_gui_slider_get_value(gui, keyed), 1.0 / 3.0);
180 n_gui_process_event(gui, key_event(ALLEGRO_KEY_LEFT));
181 expect_near("and back", n_gui_slider_get_value(gui, keyed), 0.0);
182
183 /* A wheel notch does the same. */
184 n_gui_process_event(gui, mouse_event(ALLEGRO_EVENT_MOUSE_AXES, 20, 50, 1));
185 expect_near("one wheel notch moves by the key step",
186 n_gui_slider_get_value(gui, keyed), 1.0 / 3.0);
187
188 /* The point of the whole thing: a drag lands where it was dragged, not on
189 a multiple of the key step. A slider with a snapping step still snaps,
190 which is what that one is for. */
191 n_gui_slider_set_value(gui, keyed, 0.0);
192 n_gui_process_event(gui, mouse_event(ALLEGRO_EVENT_MOUSE_BUTTON_DOWN, 150, 50, 0));
193 n_gui_process_event(gui, mouse_event(ALLEGRO_EVENT_MOUSE_AXES, 150, 50, 0));
194 {
195 double dragged = n_gui_slider_get_value(gui, keyed);
196 double thirds = dragged / (1.0 / 3.0);
197
198 expect_true("a drag moved it", fabs(dragged) > 1e-9);
199 expect_true("and it did not land on a multiple of the key step",
200 fabs(thirds - floor(thirds + 0.5)) > 1e-6);
201 }
202 n_gui_process_event(gui, mouse_event(ALLEGRO_EVENT_MOUSE_BUTTON_UP, 150, 50, 0));
203
204 /* Zero puts it back to the fallback chain rather than meaning "no
205 movement": a slider that could not be moved by a key at all would be a
206 trap, and 0 is what a caller passes to undo the setting. */
207 n_gui_slider_set_key_step(gui, keyed, 0.0);
208 expect_near("zero falls back rather than freezing the slider",
209 n_gui_slider_get_key_step(gui, keyed), 0.5);
210 n_gui_slider_set_key_step(gui, keyed, -1.0);
211 expect_near("and so does a negative", n_gui_slider_get_key_step(gui, keyed), 0.5);
212
213 /* A widget that is not a slider answers 0.0 rather than reading whatever
214 its own data happens to hold at that offset. */
215 {
216 int label = n_gui_add_label(gui, win, "x", 0.0f, 120.0f, 50.0f, 20.0f,
218
219 expect_near("a non-slider answers zero", n_gui_slider_get_key_step(gui, label), 0.0);
220 n_gui_slider_set_key_step(gui, label, 2.0);
221 expect_near("and setting one on it does nothing",
222 n_gui_slider_get_key_step(gui, label), 0.0);
223 }
224 expect_near("an unknown widget answers zero", n_gui_slider_get_key_step(gui, 9999), 0.0);
225
227 al_destroy_font(font);
228 if (failures) {
229 n_log(LOG_ERR, "ex_gui_slider_step: %d failure(s)", failures);
230 return 1;
231 }
232 n_log(LOG_NOTICE, "ex_gui_slider_step: all checks passed");
233 return 0;
234}
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 ALLEGRO_EVENT key_event(int keycode, unsigned int modifiers)
Build a key event for keycode with modifiers.
static N_GUI_CTX * gui
static void expect_near(const char *label, double got, double want)
Assert two values are equal to within a tolerance.
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()
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_ALIGN_LEFT
left aligned text
Definition n_gui.h:212
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
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.
Definition n_gui.c:3639
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
void n_gui_set_focus(N_GUI_CTX *ctx, int widget_id)
Set keyboard focus to a specific widget.
Definition n_gui.c:3908
#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
void n_gui_slider_set_step(N_GUI_CTX *ctx, int widget_id, double step)
set slider step increment.
Definition n_gui.c:4014
double n_gui_slider_get_key_step(N_GUI_CTX *ctx, int widget_id)
get the arrow key and wheel increment a slider actually uses
Definition n_gui.c:4136
void n_gui_slider_set_key_step(N_GUI_CTX *ctx, int widget_id, double step)
set how far one arrow key press or wheel notch moves a slider
Definition n_gui.c:4061
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
#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.