6 ex__gui__slider__step_8c
gullradriel edited this page 2026-10-09 00:55:18 +02:00

examples/ex_gui_slider_step.c

Functions

Name
ALLEGRO_EVENT mouse_event(int type, int x, int y, int dz)
Build a mouse event at (x, y) with a wheel delta.
int main(void )
ALLEGRO_EVENT key_event(int keycode)
Build a key event for keycode.
void expect_true(const char * label, int cond)
Assert a condition, logging which check failed.
void expect_near(const char * label, double got, double want)
Assert two values are equal to within a tolerance.

Attributes

Name
int failures
Count of failed assertions.

Defines

Name
ALLEGRO_UNSTABLE

Functions Documentation

function mouse_event

static ALLEGRO_EVENT mouse_event(
    int type,
    int x,
    int y,
    int dz
)

Build a mouse event at (x, y) with a wheel delta.

Parameters:

  • type The ALLEGRO_EVENT_MOUSE_* type.
  • x Cursor x.
  • y Cursor y.
  • dz Wheel notches.

Return: The event.

function main

int main(
    void 
)

function key_event

static ALLEGRO_EVENT key_event(
    int keycode
)

Build a key event for keycode.

Parameters:

  • keycode An ALLEGRO_KEY_* code.

Return: The event, ready for n_gui_process_event.

function expect_true

static void expect_true(
    const char * label,
    int cond
)

Assert a condition, logging which check failed.

Parameters:

  • label What is being checked.
  • cond Non-zero to pass.

function expect_near

static void expect_near(
    const char * label,
    double got,
    double want
)

Assert two values are equal to within a tolerance.

Parameters:

  • label What is being checked.
  • got The value under test.
  • want The expected value.

Attributes Documentation

variable failures

static int failures = 0;

Count of failed assertions.

Macros Documentation

define ALLEGRO_UNSTABLE

#define ALLEGRO_UNSTABLE 1

Source code

/*
 * 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_common.h"
#include "nilorea/n_gui.h"
#include "nilorea/n_log.h"

static int failures = 0;

static void expect_true(const char* label, int cond) {
    if (!cond) {
        n_log(LOG_ERR, "%s: FAILED", label);
        failures++;
    } else {
        n_log(LOG_INFO, "%s: OK", label);
    }
}

static void expect_near(const char* label, double got, double want) {
    if (fabs(got - want) > 1e-9) {
        n_log(LOG_ERR, "%s: FAILED, got %g wanted %g", label, got, want);
        failures++;
    } else {
        n_log(LOG_INFO, "%s: OK", label);
    }
}

static ALLEGRO_EVENT key_event(int keycode) {
    ALLEGRO_EVENT ev;
    memset(&ev, 0, sizeof(ev));
    ev.type = ALLEGRO_EVENT_KEY_CHAR;
    ev.keyboard.keycode = keycode;
    return ev;
}

static ALLEGRO_EVENT mouse_event(int type, int x, int y, int dz) {
    ALLEGRO_EVENT ev;
    memset(&ev, 0, sizeof(ev));
    ev.type = (unsigned int)type;
    ev.mouse.x = x;
    ev.mouse.y = y;
    ev.mouse.dz = dz;
    ev.mouse.button = 1;
    return ev;
}

int main(void) {
    ALLEGRO_FONT* font;
    N_GUI_CTX* gui;
    int win, plain, keyed, snapped;

    set_log_level(LOG_ERR);
    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;
    }
    gui = n_gui_new_ctx(font);
    if (!gui) {
        n_log(LOG_ERR, "n_gui_new_ctx failed");
        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);
    n_gui_window_set_flags(gui, win, N_GUI_WIN_FRAMELESS);

    /* The range a photo editor's exposure control has: ten stops end to end.
       A twentieth of that is half a stop, which is the behaviour this exists
       to give a program a way out of. */
    plain = n_gui_add_slider(gui, win, 0.0f, 0.0f, 200.0f, 20.0f, -5.0, 5.0, 0.0,
                             N_GUI_SLIDER_VALUE, NULL, NULL);
    keyed = n_gui_add_slider(gui, win, 0.0f, 40.0f, 200.0f, 20.0f, -5.0, 5.0, 0.0,
                             N_GUI_SLIDER_VALUE, NULL, NULL);
    snapped = n_gui_add_slider(gui, win, 0.0f, 80.0f, 200.0f, 20.0f, -5.0, 5.0, 0.0,
                               N_GUI_SLIDER_VALUE, NULL, NULL);
    n_gui_open_window(gui, win);

    /* Nothing set: a twentieth of the range, exactly as before. */
    expect_near("with neither set, a key moves a twentieth of the range",
                n_gui_slider_get_key_step(gui, plain), 0.5);

    /* The snapping step is still the fallback, so a caller that only ever set
       that one sees no change either. */
    n_gui_slider_set_step(gui, snapped, 0.25);
    expect_near("the snapping step is the next fallback",
                n_gui_slider_get_key_step(gui, snapped), 0.25);

    /* And the new number wins over both. */
    n_gui_slider_set_key_step(gui, keyed, 1.0 / 3.0);
    expect_near("a key step of its own wins", n_gui_slider_get_key_step(gui, keyed),
                1.0 / 3.0);

    /* Setting it must not move the value, and must not snap it: a third of a
       stop is not a divisor of anything here. */
    n_gui_slider_set_value(gui, keyed, 1.1);
    n_gui_slider_set_key_step(gui, keyed, 1.0 / 3.0);
    expect_near("setting it leaves the value alone", n_gui_slider_get_value(gui, keyed), 1.1);

    /* One arrow press moves by the key step and not by a twentieth. */
    n_gui_slider_set_value(gui, keyed, 0.0);
    n_gui_set_focus(gui, keyed);
    n_gui_process_event(gui, key_event(ALLEGRO_KEY_RIGHT));
    expect_near("one arrow press moves by the key step",
                n_gui_slider_get_value(gui, keyed), 1.0 / 3.0);
    n_gui_process_event(gui, key_event(ALLEGRO_KEY_LEFT));
    expect_near("and back", n_gui_slider_get_value(gui, keyed), 0.0);

    /* A wheel notch does the same. */
    n_gui_process_event(gui, mouse_event(ALLEGRO_EVENT_MOUSE_AXES, 20, 50, 1));
    expect_near("one wheel notch moves by the key step",
                n_gui_slider_get_value(gui, keyed), 1.0 / 3.0);

    /* The point of the whole thing: a drag lands where it was dragged, not on
       a multiple of the key step. A slider with a snapping step still snaps,
       which is what that one is for. */
    n_gui_slider_set_value(gui, keyed, 0.0);
    n_gui_process_event(gui, mouse_event(ALLEGRO_EVENT_MOUSE_BUTTON_DOWN, 150, 50, 0));
    n_gui_process_event(gui, mouse_event(ALLEGRO_EVENT_MOUSE_AXES, 150, 50, 0));
    {
        double dragged = n_gui_slider_get_value(gui, keyed);
        double thirds = dragged / (1.0 / 3.0);

        expect_true("a drag moved it", fabs(dragged) > 1e-9);
        expect_true("and it did not land on a multiple of the key step",
                    fabs(thirds - floor(thirds + 0.5)) > 1e-6);
    }
    n_gui_process_event(gui, mouse_event(ALLEGRO_EVENT_MOUSE_BUTTON_UP, 150, 50, 0));

    /* Zero puts it back to the fallback chain rather than meaning "no
       movement": a slider that could not be moved by a key at all would be a
       trap, and 0 is what a caller passes to undo the setting. */
    n_gui_slider_set_key_step(gui, keyed, 0.0);
    expect_near("zero falls back rather than freezing the slider",
                n_gui_slider_get_key_step(gui, keyed), 0.5);
    n_gui_slider_set_key_step(gui, keyed, -1.0);
    expect_near("and so does a negative", n_gui_slider_get_key_step(gui, keyed), 0.5);

    /* A widget that is not a slider answers 0.0 rather than reading whatever
       its own data happens to hold at that offset. */
    {
        int label = n_gui_add_label(gui, win, "x", 0.0f, 120.0f, 50.0f, 20.0f,
                                    N_GUI_ALIGN_LEFT);

        expect_near("a non-slider answers zero", n_gui_slider_get_key_step(gui, label), 0.0);
        n_gui_slider_set_key_step(gui, label, 2.0);
        expect_near("and setting one on it does nothing",
                    n_gui_slider_get_key_step(gui, label), 0.0);
    }
    expect_near("an unknown widget answers zero", n_gui_slider_get_key_step(gui, 9999), 0.0);

    n_gui_destroy_ctx(&gui);
    al_destroy_font(font);
    if (failures) {
        n_log(LOG_ERR, "ex_gui_slider_step: %d failure(s)", failures);
        return 1;
    }
    n_log(LOG_NOTICE, "ex_gui_slider_step: all checks passed");
    return 0;
}