ltk

GUI toolkit for X11 (WIP)
git clone git://lumidify.org/ltk.git (fast, but not encrypted)
git clone https://lumidify.org/ltk.git (encrypted, but very slow)
git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ltk.git (over tor)
Log | Files | Refs | README | LICENSE

commit d0a001aaf611434b7abb0ca173e9bcf1fae73983
parent fcbc1ce6f1df72e293da709c8f5a29e30a1ac6ba
Author: lumidify <nobody@lumidify.org>
Date:   Fri,  3 May 2024 23:16:12 +0200

Add basic radiobutton

Diffstat:
MMakefile | 2++
Mexamples/ltk/test.c | 6++++++
Msrc/ltk/checkbutton.c | 1+
Msrc/ltk/config.c | 1+
Msrc/ltk/graphics.h | 2++
Msrc/ltk/graphics_xlib.c | 15+++++++++++++++
Asrc/ltk/radiobutton.c | 308+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/ltk/radiobutton.h | 39+++++++++++++++++++++++++++++++++++++++
Msrc/ltk/widget.h | 2++
Msrc/ltk/widget_internal.h | 1+
10 files changed, 377 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile @@ -58,6 +58,7 @@ OBJ_LTK = \ src/ltk/ltk.o \ src/ltk/button.o \ src/ltk/checkbutton.o \ + src/ltk/radiobutton.o \ src/ltk/graphics_xlib.o \ src/ltk/surface_cache.o \ src/ltk/event_xlib.o \ @@ -96,6 +97,7 @@ OBJ_TEST = examples/ltk/test.o HDR_LTK = \ src/ltk/button.h \ src/ltk/checkbutton.h \ + src/ltk/radiobutton.h \ src/ltk/color.h \ src/ltk/label.h \ src/ltk/rect.h \ diff --git a/examples/ltk/test.c b/examples/ltk/test.c @@ -10,6 +10,7 @@ #include <ltk/menu.h> #include <ltk/box.h> #include <ltk/checkbutton.h> +#include <ltk/radiobutton.h> int quit(ltk_widget *self, ltk_callback_arglist args, ltk_callback_arg data) { @@ -87,6 +88,11 @@ main(int argc, char *argv[]) { ltk_box_add(box, LTK_CAST_WIDGET(cbtn1), LTK_STICKY_LEFT); ltk_box_add(box, LTK_CAST_WIDGET(cbtn2), LTK_STICKY_LEFT); + ltk_radiobutton *rbtn1 = ltk_radiobutton_create(window, "Radiobutton1", 0, NULL); + ltk_radiobutton *rbtn2 = ltk_radiobutton_create(window, "Radiobutton2", 1, rbtn1); + ltk_box_add(box, LTK_CAST_WIDGET(rbtn1), LTK_STICKY_LEFT); + ltk_box_add(box, LTK_CAST_WIDGET(rbtn2), LTK_STICKY_LEFT); + ltk_grid_add(grid, LTK_CAST_WIDGET(menu), 0, 0, 1, 2, LTK_STICKY_LEFT|LTK_STICKY_RIGHT); ltk_grid_add(grid, LTK_CAST_WIDGET(button), 1, 0, 1, 1, LTK_STICKY_LEFT); ltk_grid_add(grid, LTK_CAST_WIDGET(button1), 1, 1, 1, 1, LTK_STICKY_RIGHT); diff --git a/src/ltk/checkbutton.c b/src/ltk/checkbutton.c @@ -220,6 +220,7 @@ ltk_checkbutton_set_checked(ltk_checkbutton *button, int checked) { button->checked = checked; ltk_widget *self = LTK_CAST_WIDGET(button); ltk_window_invalidate_widget_rect(self->window, self); + ltk_widget_emit_signal(self, LTK_CHECKBUTTON_SIGNAL_CHANGED, LTK_EMPTY_ARGLIST); } #define MAX(a, b) ((a) > (b) ? (a) : (b)) diff --git a/src/ltk/config.c b/src/ltk/config.c @@ -85,6 +85,7 @@ static struct theme_handlerinfo { {"theme:submenu", &ltk_submenu_get_theme_parseinfo, "theme:window", 0}, {"theme:submenuentry", &ltk_submenuentry_get_theme_parseinfo, "theme:window", 0}, {"theme:checkbutton", &ltk_checkbutton_get_theme_parseinfo, "theme:window", 0}, + {"theme:radiobutton", &ltk_radiobutton_get_theme_parseinfo, "theme:window", 0}, }; GEN_SORT_SEARCH_HELPERS(themehandler, struct theme_handlerinfo, name) diff --git a/src/ltk/graphics.h b/src/ltk/graphics.h @@ -62,6 +62,8 @@ void ltk_surface_draw_border(ltk_surface *s, ltk_color *c, ltk_rect rect, int li void ltk_surface_draw_border_clipped(ltk_surface *s, ltk_color *c, ltk_rect rect, ltk_rect clip_rect, int line_width, ltk_border_sides border_sides); void ltk_surface_fill_polygon(ltk_surface *s, ltk_color *c, ltk_point *points, size_t npoints); void ltk_surface_fill_polygon_clipped(ltk_surface *s, ltk_color *c, ltk_point *points, size_t npoints, ltk_rect clip); +void ltk_surface_fill_ellipse(ltk_surface *s, ltk_color *c, ltk_rect rect); +void ltk_surface_fill_ellipse_clipped(ltk_surface *s, ltk_color *c, ltk_rect rect, ltk_rect clip); /* dpi is actually 5 * the real dpi! */ /* sz.val is scaled by 100! */ diff --git a/src/ltk/graphics_xlib.c b/src/ltk/graphics_xlib.c @@ -329,6 +329,21 @@ ltk_surface_fill_polygon_clipped(ltk_surface *s, ltk_color *c, ltk_point *points } void +ltk_surface_fill_ellipse(ltk_surface *s, ltk_color *c, ltk_rect rect) { + XSetForeground(s->window->renderdata->dpy, s->window->gc, c->xcolor.pixel); + XFillArc(s->window->renderdata->dpy, s->d, s->window->gc, rect.x, rect.y, rect.w, rect.h, 0, 360 * 64); +} + +void +ltk_surface_fill_ellipse_clipped(ltk_surface *s, ltk_color *c, ltk_rect rect, ltk_rect clip) { + /* NOTE: XRectangle only uses short, so this could cause issues */ + XRectangle xclip = {clip.x, clip.y, clip.w, clip.h}; + XSetClipRectangles(s->window->renderdata->dpy, s->window->gc, 0, 0, &xclip, 1, Unsorted); + ltk_surface_fill_ellipse(s, c, rect); + XSetClipMask(s->window->renderdata->dpy, s->window->gc, None); +} + +void ltk_surface_copy(ltk_surface *src, ltk_surface *dst, ltk_rect src_rect, int dst_x, int dst_y) { XCopyArea( src->window->renderdata->dpy, src->d, dst->d, src->window->gc, diff --git a/src/ltk/radiobutton.c b/src/ltk/radiobutton.c @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2024 lumidify <nobody@lumidify.org> + * + * 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 <stdio.h> + +#include "config.h" +#include "radiobutton.h" +#include "color.h" +#include "graphics.h" +#include "ltk.h" +#include "memory.h" +#include "rect.h" +#include "text.h" +#include "util.h" +#include "widget.h" + +/* FIXME: a lot of duplicated code from checkbutton */ + +#define MAX_RADIOBUTTON_BORDER_WIDTH 10000 +#define MAX_RADIOBUTTON_PADDING 50000 +#define MAX_RADIOBUTTON_CIRCLE_SIZE 50000 + +static void ltk_radiobutton_draw(ltk_widget *self, ltk_surface *draw_surf, int x, int y, ltk_rect clip); +static int ltk_radiobutton_release(ltk_widget *self); +static void ltk_radiobutton_destroy(ltk_widget *self, int shallow); +static void ltk_radiobutton_recalc_ideal_size(ltk_widget *self); + +static struct ltk_widget_vtable vtable = { + .key_press = NULL, + .key_release = NULL, + .mouse_press = NULL, + .mouse_release = NULL, + .release = &ltk_radiobutton_release, + .motion_notify = NULL, + .mouse_leave = NULL, + .mouse_enter = NULL, + .change_state = NULL, + .get_child_at_pos = NULL, + .resize = NULL, + .hide = NULL, + .draw = &ltk_radiobutton_draw, + .destroy = &ltk_radiobutton_destroy, + .child_size_change = NULL, + .remove_child = NULL, + .recalc_ideal_size = &ltk_radiobutton_recalc_ideal_size, + .type = LTK_WIDGET_RADIOBUTTON, + .flags = LTK_NEEDS_REDRAW | LTK_ACTIVATABLE_ALWAYS, + .invalid_signal = LTK_RADIOBUTTON_SIGNAL_INVALID, +}; + +static struct { + ltk_color *text_color; + + ltk_color *fill; + ltk_color *fill_pressed; + ltk_color *fill_hover; + ltk_color *fill_active; + ltk_color *fill_disabled; + + ltk_color *circle_fill; + ltk_color *circle_border; + + ltk_color *circle_fill_pressed; + ltk_color *circle_border_pressed; + + ltk_color *circle_fill_hover; + ltk_color *circle_border_hover; + + ltk_color *circle_fill_active; + ltk_color *circle_border_active; + + ltk_color *circle_fill_disabled; + ltk_color *circle_border_disabled; + + ltk_color *circle_fill_checked; + ltk_color *circle_border_checked; + + ltk_color *circle_fill_pressed_checked; + ltk_color *circle_border_pressed_checked; + + ltk_color *circle_fill_hover_checked; + ltk_color *circle_border_hover_checked; + + ltk_color *circle_fill_active_checked; + ltk_color *circle_border_active_checked; + + ltk_color *circle_fill_disabled_checked; + ltk_color *circle_border_disabled_checked; + + char *font; + ltk_size circle_size; + ltk_size circle_border_width; + ltk_size pad; + ltk_size font_size; +} theme; + +static ltk_theme_parseinfo parseinfo[] = { + {"fill", THEME_COLOR, {.color = &theme.fill}, {.color = "#000000"}, 0, 0, 0}, + {"fill-hover", THEME_COLOR, {.color = &theme.fill_hover}, {.color = "#222222"}, 0, 0, 0}, + {"fill-active", THEME_COLOR, {.color = &theme.fill_active}, {.color = "#222222"}, 0, 0, 0}, + {"fill-disabled", THEME_COLOR, {.color = &theme.fill_disabled}, {.color = "#292929"}, 0, 0, 0}, + {"fill-pressed", THEME_COLOR, {.color = &theme.fill_pressed}, {.color = "#222222"}, 0, 0, 0}, + + {"circle-fill", THEME_COLOR, {.color = &theme.circle_fill}, {.color = "#000000"}, 0, 0, 0}, + {"circle-fill-hover", THEME_COLOR, {.color = &theme.circle_fill_hover}, {.color = "#222222"}, 0, 0, 0}, + {"circle-fill-active", THEME_COLOR, {.color = &theme.circle_fill_active}, {.color = "#222222"}, 0, 0, 0}, + {"circle-fill-disabled", THEME_COLOR, {.color = &theme.circle_fill_disabled}, {.color = "#292929"}, 0, 0, 0}, + {"circle-fill-pressed", THEME_COLOR, {.color = &theme.circle_fill_pressed}, {.color = "#222222"}, 0, 0, 0}, + {"circle-border", THEME_COLOR, {.color = &theme.circle_border}, {.color = "#FFFFFF"}, 0, 0, 0}, + {"circle-border-hover", THEME_COLOR, {.color = &theme.circle_border_hover}, {.color = "#FFFFFF"}, 0, 0, 0}, + {"circle-border-active", THEME_COLOR, {.color = &theme.circle_border_active}, {.color = "#FFFFFF"}, 0, 0, 0}, + {"circle-border-disabled", THEME_COLOR, {.color = &theme.circle_border_disabled}, {.color = "#FFFFFF"}, 0, 0, 0}, + {"circle-border-pressed", THEME_COLOR, {.color = &theme.circle_border_pressed}, {.color = "#FFFFFF"}, 0, 0, 0}, + + {"circle-fill-checked", THEME_COLOR, {.color = &theme.circle_fill_checked}, {.color = "#113355"}, 0, 0, 0}, + {"circle-fill-hover-checked", THEME_COLOR, {.color = &theme.circle_fill_hover_checked}, {.color = "#738194"}, 0, 0, 0}, + {"circle-fill-active-checked", THEME_COLOR, {.color = &theme.circle_fill_active_checked}, {.color = "#113355"}, 0, 0, 0}, + {"circle-fill-disabled-checked", THEME_COLOR, {.color = &theme.circle_fill_disabled_checked}, {.color = "#292929"}, 0, 0, 0}, + {"circle-fill-pressed-checked", THEME_COLOR, {.color = &theme.circle_fill_pressed_checked}, {.color = "#113355"}, 0, 0, 0}, + {"circle-border-checked", THEME_COLOR, {.color = &theme.circle_border_checked}, {.color = "#FFFFFF"}, 0, 0, 0}, + {"circle-border-hover-checked", THEME_COLOR, {.color = &theme.circle_border_hover_checked}, {.color = "#FFFFFF"}, 0, 0, 0}, + {"circle-border-active-checked", THEME_COLOR, {.color = &theme.circle_border_active_checked}, {.color = "#FFFFFF"}, 0, 0, 0}, + {"circle-border-disabled-checked", THEME_COLOR, {.color = &theme.circle_border_disabled_checked}, {.color = "#FFFFFF"}, 0, 0, 0}, + {"circle-border-pressed-checked", THEME_COLOR, {.color = &theme.circle_border_pressed_checked}, {.color = "#FFFFFF"}, 0, 0, 0}, + + {"circle-size", THEME_SIZE, {.size = &theme.circle_size}, {.size = {.val = 500, .unit = LTK_UNIT_MM}}, 0, MAX_RADIOBUTTON_CIRCLE_SIZE, 0}, + {"circle-border-width", THEME_SIZE, {.size = &theme.circle_border_width}, {.size = {.val = 50, .unit = LTK_UNIT_MM}}, 0, MAX_RADIOBUTTON_BORDER_WIDTH, 0}, + {"pad", THEME_SIZE, {.size = &theme.pad}, {.size = {.val = 100, .unit = LTK_UNIT_MM}}, 0, MAX_RADIOBUTTON_PADDING, 0}, + {"text-color", THEME_COLOR, {.color = &theme.text_color}, {.color = "#FFFFFF"}, 0, 0, 0}, + {"font", THEME_STRING, {.str = &theme.font}, {.str = "Monospace"}, 0, 0, 0}, + {"font-size", THEME_SIZE, {.size = &theme.font_size}, {.size = {.val = 1200, .unit = LTK_UNIT_PT}}, 0, 20000, 0}, +}; + +void +ltk_radiobutton_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len) { + *p = parseinfo; + *len = LENGTH(parseinfo); +} + +/* FIXME: a lot more theme settings */ +static void +ltk_radiobutton_draw(ltk_widget *self, ltk_surface *draw_surf, int x, int y, ltk_rect clip) { + ltk_radiobutton *button = LTK_CAST_RADIOBUTTON(self); + ltk_rect lrect = self->lrect; + ltk_rect clip_final = ltk_rect_intersect(clip, (ltk_rect){0, 0, lrect.w, lrect.h}); + if (clip_final.w <= 0 || clip_final.h <= 0) + return; + + int circle_size = ltk_size_to_pixel(theme.circle_size, self->last_dpi); + int circle_bw = ltk_size_to_pixel(theme.circle_border_width, self->last_dpi); + int pad = ltk_size_to_pixel(theme.pad, self->last_dpi); + ltk_color *fill = NULL, *circle_border = NULL, *circle_fill = NULL; + if (self->state & LTK_DISABLED) { + fill = theme.fill_disabled; + circle_border = button->checked ? theme.circle_border_disabled_checked : theme.circle_border_disabled; + circle_fill = button->checked ? theme.circle_fill_disabled_checked : theme.circle_fill_disabled; + } else if (self->state & LTK_PRESSED) { + fill = theme.fill_pressed; + circle_border = button->checked ? theme.circle_border_pressed_checked : theme.circle_border_pressed; + circle_fill = button->checked ? theme.circle_fill_pressed_checked : theme.circle_fill_pressed; + } else if (self->state & LTK_HOVER) { + fill = theme.fill_hover; + circle_border = button->checked ? theme.circle_border_hover_checked : theme.circle_border_hover; + circle_fill = button->checked ? theme.circle_fill_hover_checked : theme.circle_fill_hover; + } else if (self->state & LTK_ACTIVE) { + fill = theme.fill_active; + circle_border = button->checked ? theme.circle_border_active_checked : theme.circle_border_active; + circle_fill = button->checked ? theme.circle_fill_active_checked : theme.circle_fill_active; + } else { + fill = theme.fill; + circle_border = button->checked ? theme.circle_border_checked : theme.circle_border; + circle_fill = button->checked ? theme.circle_fill_checked : theme.circle_fill; + } + ltk_rect circle_rect = {x + pad, y + pad, circle_size, circle_size}; + ltk_rect draw_clip = {x + clip_final.x, y + clip_final.y, clip_final.w, clip_final.h}; + ltk_surface_fill_rect(draw_surf, fill, draw_clip); + /* Yeah, this draws the inner part of the circle twice... */ + if (circle_bw > 0) { + ltk_surface_fill_ellipse_clipped(draw_surf, circle_border, circle_rect, draw_clip); + } + if (circle_size > circle_bw * 2) { + circle_rect.x += circle_bw; + circle_rect.y += circle_bw; + circle_rect.w -= circle_bw * 2; + circle_rect.h -= circle_bw * 2; + ltk_surface_fill_ellipse_clipped(draw_surf, circle_fill, circle_rect, draw_clip); + } + int text_w, text_h; + ltk_text_line_get_size(button->tl, &text_w, &text_h); + int text_x = x + 2 * pad + circle_size; + int text_y = y + (lrect.h - text_h) / 2; + ltk_text_line_draw_clipped(button->tl, draw_surf, theme.text_color, text_x, text_y, draw_clip); + /* FIXME: only redraw if dirty (needs to be handled higher-up to only + call draw when dirty or window rect invalidated */ + self->dirty = 0; +} + +static void +uncheck_other_buttons(ltk_radiobutton *button) { + ltk_radiobutton *prev = button->prev, *next = button->next; + while (prev) { + ltk_radiobutton_set_checked(prev, 0); + prev = prev->prev; + } + while (next) { + ltk_radiobutton_set_checked(next, 0); + next = next->next; + } +} + +static int +ltk_radiobutton_release(ltk_widget *self) { + ltk_radiobutton *button = LTK_CAST_RADIOBUTTON(self); + button->checked = !button->checked; + if (button->checked) + uncheck_other_buttons(button); + ltk_widget_emit_signal(self, LTK_RADIOBUTTON_SIGNAL_CHANGED, LTK_EMPTY_ARGLIST); + return 1; +} + +int +ltk_radiobutton_get_checked(ltk_radiobutton *button) { + return button->checked; +} + +void +ltk_radiobutton_set_checked(ltk_radiobutton *button, int checked) { + button->checked = checked; + if (checked) + uncheck_other_buttons(button); + ltk_widget *self = LTK_CAST_WIDGET(button); + ltk_window_invalidate_widget_rect(self->window, self); +} + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +static void +recalc_ideal_size(ltk_radiobutton *button) { + int text_w, text_h; + ltk_text_line_get_size(button->tl, &text_w, &text_h); + int circle_size = ltk_size_to_pixel(theme.circle_size, LTK_CAST_WIDGET(button)->last_dpi); + int pad = ltk_size_to_pixel(theme.pad, LTK_CAST_WIDGET(button)->last_dpi); + button->widget.ideal_w = text_w + pad * 3 + circle_size; + button->widget.ideal_h = MAX(text_h, circle_size) + pad * 2; +} + +static void +ltk_radiobutton_recalc_ideal_size(ltk_widget *self) { + ltk_radiobutton *button = LTK_CAST_RADIOBUTTON(self); + int font_size = ltk_size_to_pixel(theme.font_size, self->last_dpi); + ltk_text_line_set_font_size(button->tl, font_size); + recalc_ideal_size(button); +} + +ltk_radiobutton * +ltk_radiobutton_create(ltk_window *window, const char *text, int checked, ltk_radiobutton *group_member) { + ltk_radiobutton *button = ltk_malloc(sizeof(ltk_radiobutton)); + ltk_fill_widget_defaults(LTK_CAST_WIDGET(button), window, &vtable, 0, 0); + button->checked = checked; + button->prev = button->next = NULL; + if (group_member) { + button->prev = group_member; + button->next = group_member->next; + group_member->next = button; + /* I guess it's technically possible for a button that is only created and + never added to the widget hierarchy to cause the other buttons to be + unchecked, but I guess that's just the way it is. */ + if (button->checked) + uncheck_other_buttons(button); + } + + button->tl = ltk_text_line_create_const_text_default( + theme.font, ltk_size_to_pixel(theme.font_size, button->widget.last_dpi), text, -1 + ); + recalc_ideal_size(button); + button->widget.dirty = 1; + + return button; +} + +static void +ltk_radiobutton_destroy(ltk_widget *self, int shallow) { + (void)shallow; + ltk_radiobutton *button = LTK_CAST_RADIOBUTTON(self); + if (!button) { + ltk_warn("Tried to destroy NULL radiobutton.\n"); + return; + } + ltk_text_line_destroy(button->tl); + if (button->prev) + button->prev->next = button->next; + if (button->next) + button->next->prev = button->prev; + ltk_free(button); +} diff --git a/src/ltk/radiobutton.h b/src/ltk/radiobutton.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024 lumidify <nobody@lumidify.org> + * + * 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 LTK_RADIOBUTTON_H +#define LTK_RADIOBUTTON_H + +#include "text.h" +#include "widget.h" +#include "window.h" + +#define LTK_RADIOBUTTON_SIGNAL_CHANGED -1 +#define LTK_RADIOBUTTON_SIGNAL_INVALID -2 + +typedef struct ltk_radiobutton { + ltk_widget widget; + ltk_text_line *tl; + struct ltk_radiobutton *prev; + struct ltk_radiobutton *next; + int checked; +} ltk_radiobutton; + +ltk_radiobutton *ltk_radiobutton_create(ltk_window *window, const char *text, int checked, ltk_radiobutton *group_member); +int ltk_radiobutton_get_checked(ltk_radiobutton *button); +void ltk_radiobutton_set_checked(ltk_radiobutton *button, int checked); + +#endif /* LTK_RADIOBUTTON_H */ diff --git a/src/ltk/widget.h b/src/ltk/widget.h @@ -46,6 +46,7 @@ typedef enum { LTK_WIDGET_WINDOW, LTK_WIDGET_SCROLLBAR, LTK_WIDGET_CHECKBUTTON, + LTK_WIDGET_RADIOBUTTON, LTK_NUM_WIDGETS, } ltk_widget_type; @@ -186,6 +187,7 @@ typedef struct { #define LTK_CAST_SCROLLBAR(w) (ltk_assert(w->vtable->type == LTK_WIDGET_SCROLLBAR), (ltk_scrollbar *)(w)) #define LTK_CAST_BOX(w) (ltk_assert(w->vtable->type == LTK_WIDGET_BOX), (ltk_box *)(w)) #define LTK_CAST_CHECKBUTTON(w) (ltk_assert(w->vtable->type == LTK_WIDGET_CHECKBUTTON), (ltk_checkbutton *)(w)) +#define LTK_CAST_RADIOBUTTON(w) (ltk_assert(w->vtable->type == LTK_WIDGET_RADIOBUTTON), (ltk_radiobutton *)(w)) /* FIXME: a bit weird because window never gets some of these signals */ #define LTK_WIDGET_SIGNAL_KEY_PRESS 1 diff --git a/src/ltk/widget_internal.h b/src/ltk/widget_internal.h @@ -27,6 +27,7 @@ void ltk_window_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len); void ltk_button_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len); void ltk_checkbutton_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len); +void ltk_radiobutton_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len); void ltk_label_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len); void ltk_menu_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len); void ltk_menuentry_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len);