# HG changeset patch # User David Demelier # Date 1677663252 -3600 # Node ID ab2816f9551c37c7f80f3d56e9d02ed328fa3bef # Parent 23559c7ccf47673334bd2609178270680af55092 ui: start reworking on styles While here, demonstrates how we can use a custom style on a button. diff -r 23559c7ccf47 -r ab2816f9551c examples/example-ui/CMakeLists.txt --- a/examples/example-ui/CMakeLists.txt Tue Feb 28 15:54:44 2023 +0100 +++ b/examples/example-ui/CMakeLists.txt Wed Mar 01 10:34:12 2023 +0100 @@ -20,6 +20,8 @@ set( SOURCES + ${example-ui_SOURCE_DIR}/button-style-glow.c + ${example-ui_SOURCE_DIR}/button-style-glow.h ${example-ui_SOURCE_DIR}/example-ui.c ) diff -r 23559c7ccf47 -r ab2816f9551c examples/example-ui/button-style-glow.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/example-ui/button-style-glow.c Wed Mar 01 10:34:12 2023 +0100 @@ -0,0 +1,112 @@ +/* + * button-style-glow.c -- example of glowing button + * + * Copyright (c) 2020-2023 David Demelier + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "button-style-glow.h" + +/* + * This button style illuminates the background color by periodically changing + * its red, green, blue components to the target color and then go back to the + * original transition. + * + * Its sole purpose is to demonstrates how style works. Because the style needs + * a state it is implemented through a parent button_style_glow structure which + * holds an underlying style. + * + * This style does not override the glow function because we just change the + * bg_color property that will be reused. + */ + +static inline unsigned int +increment(unsigned long ccmp, unsigned long ctgt) +{ + if (ctgt > ccmp) + return ccmp + 2 > ctgt ? ctgt : ccmp + 2; + if (ctgt < ccmp) + return ccmp - 2 < ctgt ? ctgt : ccmp - 2; + + return ccmp; +} + +static void +init(struct mlk_button_style *style, struct mlk_button *button) +{ + (void)button; + + struct button_style_glow *glow = style->data; + + /* + * We start at colors[0] and reach colors[1], colors[2] will be the + * destination color that will swap over time. + */ + glow->colors[2] = glow->colors[1]; + glow->elapsed = 0; + + style->bg_color = glow->colors[0]; +} + +static void +update(struct mlk_button_style *style, struct mlk_button *button, unsigned int ticks) +{ + (void)button; + + struct button_style_glow *glow = style->data; + unsigned int red, green, blue; + + glow->elapsed += ticks; + + if (glow->elapsed >= glow->delay) { + glow->elapsed = 0; + + /* Color target reached, invert logic. */ + if (style->bg_color == glow->colors[2]) { + if (glow->colors[2] == glow->colors[0]) + glow->colors[2] = glow->colors[1]; + else + glow->colors[2] = glow->colors[0]; + } else { + red = increment( + MLK_COLOR_R(style->bg_color), + MLK_COLOR_R(glow->colors[2]) + ); + green = increment( + MLK_COLOR_G(style->bg_color), + MLK_COLOR_G(glow->colors[2]) + ); + blue = increment( + MLK_COLOR_B(style->bg_color), + MLK_COLOR_B(glow->colors[2]) + ); + + style->bg_color = MLK_COLOR_HEX(red, green, blue, 0xff); + } + } +} + +void +button_style_glow_init(struct button_style_glow *glow) +{ + assert(glow); + + glow->style.data = glow; + glow->style.init = init; + glow->style.update = update; +} diff -r 23559c7ccf47 -r ab2816f9551c examples/example-ui/button-style-glow.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/example-ui/button-style-glow.h Wed Mar 01 10:34:12 2023 +0100 @@ -0,0 +1,38 @@ +/* + * button-style-glow.h -- example of glowing button + * + * Copyright (c) 2020-2023 David Demelier + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MLK_EXAMPLE_BUTTON_STYLE_H +#define MLK_EXAMPLE_BUTTON_STYLE_H + +#include + +#define BUTTON_STYLE_GLOW_COLOR_1 0x235b7cff +#define BUTTON_STYLE_GLOW_COLOR_2 0x2d80a6ff +#define BUTTON_STYLE_GLOW_DELAY 20 + +struct button_style_glow { + unsigned long colors[3]; + unsigned int delay; + unsigned int elapsed; + struct mlk_button_style style; +}; + +void +button_style_glow_init(struct button_style_glow *); + +#endif /* !MLK_EXAMPLE_BUTTON_GLOW_STYLE_H */ diff -r 23559c7ccf47 -r ab2816f9551c examples/example-ui/example-ui.c --- a/examples/example-ui/example-ui.c Tue Feb 28 15:54:44 2023 +0100 +++ b/examples/example-ui/example-ui.c Wed Mar 01 10:34:12 2023 +0100 @@ -16,15 +16,16 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +#include #include #include #include #include +#include #include -#include #include #include +#include #include #include @@ -33,10 +34,14 @@ #include #include #include +#include #include #include #include +#include + +#include "button-style-glow.h" #define FRAME_ORIGIN_X (10) #define FRAME_ORIGIN_Y (10) @@ -57,7 +62,7 @@ * | [x] Auto save | * | | * | | - * | [ Quit ] | + * | [Hello][ Quit ] | * +---------------------------------------------+ */ static struct { @@ -81,8 +86,19 @@ } autosave; struct { - struct mlk_button button; - } quit; + /* [Hello] with default style*/ + struct mlk_button hello; + + /* [Quit] with custom style color. */ + struct mlk_button_style quit_style; + struct mlk_button quit; + + /* + * [Download free RAM] with custom style drawing. + */ + struct button_style_glow download_glow; + struct mlk_button download; + } buttons; } ui = { .panel = { .frame = { @@ -110,10 +126,31 @@ .flags = MLK_LABEL_FLAGS_SHADOW, } }, - .quit = { - .button = { + .buttons = { + .hello = { + .text = "Hello", + .h = ELEMENT_HEIGHT + }, + .quit_style = { + .bg_color = 0x235b7cff + }, + .quit = { .text = "Quit", - .h = ELEMENT_HEIGHT + .h = ELEMENT_HEIGHT, + .style = &ui.buttons.quit_style + }, + .download_glow = { + .colors = { + BUTTON_STYLE_GLOW_COLOR_1, + BUTTON_STYLE_GLOW_COLOR_2 + }, + .delay = BUTTON_STYLE_GLOW_DELAY + }, + .download = { + .w = 180, + .h = 32, + .text = "!! Download free RAM !!", + .style = &ui.buttons.download_glow.style } } }; @@ -121,15 +158,6 @@ static struct mlk_state *states[1]; static void -init(void) -{ - int err; - - if ((err = mlk_example_init("example-ui")) < 0) - mlk_panicf("mlk_example_init: %s", mlk_err_string(err)); -} - -static void resize_header(void) { struct mlk_frame *f = &ui.panel.frame; @@ -163,16 +191,25 @@ { unsigned int padding = mlk_theme_default()->padding; struct mlk_frame *f = &ui.panel.frame; - struct mlk_button *b = &ui.quit.button; + struct mlk_button *quit = &ui.buttons.quit; + struct mlk_button *hello = &ui.buttons.hello; + struct mlk_button *dl = &ui.buttons.download; - /* Button. */ - b->w = f->w / 4; + /* Buttons takes 1/4 of the frame width. */ + quit->w = hello->w = f->w / 4; - mlk_align(MLK_ALIGN_BOTTOM_RIGHT, &b->x, &b->y, b->w, b->h, + mlk_align(MLK_ALIGN_BOTTOM_RIGHT, &quit->x, &quit->y, quit->w, quit->h, f->x, f->y, f->w, f->h); - b->x -= padding; - b->y -= padding; + quit->x -= padding; + quit->y -= padding; + + /* Hello is immediately left. */ + hello->x = quit->x - quit->w - padding; + hello->y = quit->y; + + /* Download free ram is at the bottom center. */ + mlk_align(MLK_ALIGN_BOTTOM, &dl->x, &dl->y, dl->w, dl->h, 0, 0, mlk_window.w, mlk_window.h - 10); } static void @@ -232,8 +269,32 @@ mlk_checkbox_handle(&ui.autosave.cb, ev); - if (mlk_button_handle(&ui.quit.button, ev)) + if (mlk_button_handle(&ui.buttons.quit, ev)) mlk_game_quit(); + if (mlk_button_handle(&ui.buttons.hello, ev)) + mlk_notify( + &mlk_registry_textures[MLK_REGISTRY_TEXTURE_SWORD], + "Hello", + "Hello world!" + ); + if (mlk_button_handle(&ui.buttons.download, ev)) + mlk_notify( + &mlk_registry_textures[MLK_REGISTRY_TEXTURE_SWORD], + "Complete", + "16GB of RAM successfully downloaded!" + ); +} + +static void +update(struct mlk_state *st, unsigned int ticks) +{ + (void)st; + + mlk_button_update(&ui.buttons.quit, ticks); + mlk_button_update(&ui.buttons.hello, ticks); + mlk_button_update(&ui.buttons.download, ticks); + + mlk_notify_update(ticks); } static void @@ -247,15 +308,34 @@ mlk_label_draw(&ui.header.label); mlk_checkbox_draw(&ui.autosave.cb); mlk_label_draw(&ui.autosave.label); - mlk_button_draw(&ui.quit.button); + mlk_button_draw(&ui.buttons.hello); + mlk_button_draw(&ui.buttons.quit); + mlk_button_draw(&ui.buttons.download); + mlk_notify_draw(); mlk_painter_present(); } static void +init(void) +{ + int err; + + if ((err = mlk_example_init("example-ui")) < 0) + mlk_panicf("mlk_example_init: %s", mlk_err_string(err)); + + button_style_glow_init(&ui.buttons.download_glow); + + mlk_button_init(&ui.buttons.hello); + mlk_button_init(&ui.buttons.quit); + mlk_button_init(&ui.buttons.download); +} + +static void run(void) { struct mlk_state state = { .handle = handle, + .update = update, .draw = draw }; diff -r 23559c7ccf47 -r ab2816f9551c libmlk-ui/mlk/ui/button.c --- a/libmlk-ui/mlk/ui/button.c Tue Feb 28 15:54:44 2023 +0100 +++ b/libmlk-ui/mlk/ui/button.c Wed Mar 01 10:34:12 2023 +0100 @@ -29,30 +29,23 @@ #include "label.h" #include "theme.h" -static int -is_boxed(const struct mlk_button *button, const struct mlk_event_click *click) -{ - assert(button); - assert(click); - assert(click->type == MLK_EVENT_CLICKDOWN || click->type == MLK_EVENT_CLICKUP); +#define STYLE_INVOKE(s, f, ...) \ +do { \ + if (s && s->f) \ + s->f(s, __VA_ARGS__); \ + else if (mlk_button_style.f) \ + mlk_button_style.f(s ? s : &mlk_button_style, __VA_ARGS__); \ +} while (0) - return mlk_maths_is_boxed(button->x, button->y, button->w, button->h, - click->x, click->y); -} - -void -mlk_button_draw_default(const struct mlk_theme *t, const struct mlk_button *button) +static void +draw(struct mlk_button_style *style, const struct mlk_button *button) { - assert(t); - assert(button); - - (void)t; - struct mlk_label label = { .text = button->text, }; unsigned int lw, lh; + // TODO: once label has style, copy color. mlk_label_query(&label, &lw, &lh); if (lw > button->w) @@ -63,22 +56,49 @@ mlk_align(MLK_ALIGN_CENTER, &label.x, &label.y, lw, lh, button->x, button->y, button->w, button->h); - mlk_painter_set_color(0x577277ff); + mlk_painter_set_color(style->bg_color); mlk_painter_draw_rectangle(button->x, button->y, button->w, button->h); mlk_label_draw(&label); } +static inline int +is_boxed(const struct mlk_button *button, const struct mlk_event_click *click) +{ + assert(button); + assert(click); + assert(click->type == MLK_EVENT_CLICKDOWN || click->type == MLK_EVENT_CLICKUP); + + return mlk_maths_is_boxed(button->x, button->y, button->w, button->h, + click->x, click->y); +} + +struct mlk_button_style mlk_button_style = { + .bg_color = 0x577277ff, + .text_color = 0xffffffff, + .draw = draw +}; + +void +mlk_button_init(struct mlk_button *button) +{ + assert(button); + + STYLE_INVOKE(button->style, init, button); +} + int mlk_button_handle(struct mlk_button *button, const union mlk_event *ev) { assert(button); assert(ev); + int active = 0; + switch (ev->type) { case MLK_EVENT_CLICKDOWN: if (is_boxed(button, &ev->click)) - button->state = MLK_BUTTON_STATE_PRESSED; + button->pressed = 1; break; case MLK_EVENT_CLICKUP: /* @@ -86,24 +106,24 @@ * finally activated. This let the user to move the cursor * outside the button to "forget" the press. */ - if (!is_boxed(button, &ev->click)) - button->state = MLK_BUTTON_STATE_NONE; - else if (button->state == MLK_BUTTON_STATE_PRESSED) - button->state = MLK_BUTTON_STATE_ACTIVATED; + button->pressed = 0; + + if (is_boxed(button, &ev->click)) + active = 1; break; default: break; } - return button->state == MLK_BUTTON_STATE_ACTIVATED; + return active; } void -mlk_button_reset(struct mlk_button *button) +mlk_button_update(struct mlk_button *button, unsigned int ticks) { assert(button); - button->state = MLK_BUTTON_STATE_NONE; + STYLE_INVOKE(button->style, update, button, ticks); } void @@ -111,5 +131,13 @@ { assert(button); - mlk_theme_draw_button(button->theme, button); + STYLE_INVOKE(button->style, draw, button); } + +void +mlk_button_finish(struct mlk_button *button) +{ + assert(button); + + STYLE_INVOKE(button->style, finish, button); +} diff -r 23559c7ccf47 -r ab2816f9551c libmlk-ui/mlk/ui/button.h --- a/libmlk-ui/mlk/ui/button.h Tue Feb 28 15:54:44 2023 +0100 +++ b/libmlk-ui/mlk/ui/button.h Wed Mar 01 10:34:12 2023 +0100 @@ -24,37 +24,45 @@ union mlk_event; struct mlk_theme; +struct mlk_button; -enum mlk_button_state { - MLK_BUTTON_STATE_NONE, - MLK_BUTTON_STATE_PRESSED, - MLK_BUTTON_STATE_ACTIVATED +struct mlk_button_style { + void *data; + unsigned long bg_color; + unsigned long text_color; + void (*init)(struct mlk_button_style *, struct mlk_button *); + void (*update)(struct mlk_button_style *, struct mlk_button *, unsigned int); + void (*draw)(struct mlk_button_style *, const struct mlk_button *); + void (*finish)(struct mlk_button_style *, struct mlk_button *); }; struct mlk_button { - int x; - int y; - unsigned int w; - unsigned int h; + int x, y; + unsigned int w, h; const char *text; - enum mlk_button_state state; - const struct mlk_theme *theme; + struct mlk_button_style *style; + int pressed; }; +extern struct mlk_button_style mlk_button_style; + MLK_CORE_BEGIN_DECLS +void +mlk_button_init(struct mlk_button *); + int mlk_button_handle(struct mlk_button *, const union mlk_event *); void -mlk_button_reset(struct mlk_button *); - -void -mlk_button_draw_default(const struct mlk_theme *, const struct mlk_button *); +mlk_button_update(struct mlk_button *, unsigned int); void mlk_button_draw(const struct mlk_button *); +void +mlk_button_finish(struct mlk_button *); + MLK_CORE_END_DECLS #endif /* !MLK_UI_BUTTON_H */ diff -r 23559c7ccf47 -r ab2816f9551c libmlk-ui/mlk/ui/theme.c --- a/libmlk-ui/mlk/ui/theme.c Tue Feb 28 15:54:44 2023 +0100 +++ b/libmlk-ui/mlk/ui/theme.c Wed Mar 01 10:34:12 2023 +0100 @@ -55,7 +55,6 @@ .padding = 10, .draw_frame = mlk_frame_draw_default, .draw_label = mlk_label_draw_default, - .draw_button = mlk_button_draw_default, .draw_checkbox = mlk_checkbox_draw_default };