panel: New volume slider menu widget

This commit is contained in:
K. Lange 2021-10-24 21:20:25 +09:00
parent f0091f8f87
commit 729b0c231f
3 changed files with 156 additions and 45 deletions

View File

@ -287,10 +287,7 @@ static void update_volume_level(void) {
ioctl(mixer, SND_MIXER_READ_KNOB, &value);
volume_level = value.val;
}
static void volume_raise(void) {
volume_level += 0x10000000;
if (volume_level > 0xF0000000) volume_level = 0xFC000000;
static void set_volume(void) {
snd_knob_value_t value = {0};
value.device = VOLUME_DEVICE_ID; /* TODO configure this somewhere */
value.id = VOLUME_KNOB_ID; /* TODO this too */
@ -299,19 +296,96 @@ static void volume_raise(void) {
ioctl(mixer, SND_MIXER_WRITE_KNOB, &value);
redraw();
}
static void volume_raise(void) {
volume_level += 0x10000000;
if (volume_level > 0xF0000000) volume_level = 0xFC000000;
set_volume();
}
static void volume_lower(void) {
volume_level -= 0x10000000;
if (volume_level < 0x0) volume_level = 0x0;
snd_knob_value_t value = {0};
value.device = VOLUME_DEVICE_ID; /* TODO configure this somewhere */
value.id = VOLUME_KNOB_ID; /* TODO this too */
value.val = volume_level;
ioctl(mixer, SND_MIXER_WRITE_KNOB, &value);
redraw();
set_volume();
}
#define VOLUME_SLIDER_LEFT_PAD 36
#define VOLUME_SLIDER_RIGHT_PAD 12
#define VOLUME_SLIDER_PAD (VOLUME_SLIDER_LEFT_PAD + VOLUME_SLIDER_RIGHT_PAD)
#define VOLUME_SLIDER_VERT_PAD 8
#define VOLUME_SLIDER_BALL_RADIUS 8
struct SliderStuff {
int level;
uint32_t on;
uint32_t off;
};
uint32_t volume_pattern(int32_t x, int32_t y, double alpha, void * extra) {
struct SliderStuff * stuff = extra;
if (alpha > 1.0) alpha = 1.0;
if (alpha < 0.0) alpha = 0.0;
uint32_t color = stuff->off;
if (x < stuff->level + VOLUME_SLIDER_LEFT_PAD) {
color = stuff->on;
}
color |= rgba(0,0,0,alpha*255);
return premultiply(color);
}
void _menu_draw_MenuEntry_Slider(gfx_context_t * ctx, struct MenuEntry * self, int offset) {
self->offset = offset;
draw_sprite_alpha_paint(ctx, sprite_volume_high, 4, offset, 1.0, rgb(0,0,0));
struct SliderStuff stuff;
stuff.level = (ctx->width - VOLUME_SLIDER_PAD) * (float)volume_level / (float)0xFC000000;
stuff.on = rgba(0,120,220,0);
stuff.off = rgba(140,140,140,0);
draw_rounded_rectangle_pattern(ctx,
VOLUME_SLIDER_LEFT_PAD - 1, offset + VOLUME_SLIDER_VERT_PAD - 1,
ctx->width - VOLUME_SLIDER_PAD + 2, self->height - 2 * VOLUME_SLIDER_VERT_PAD + 2, 6, volume_pattern, &stuff);
stuff.on = rgba(40,160,255,0);
stuff.off = rgba(200,200,200,0);
draw_rounded_rectangle_pattern(ctx,
VOLUME_SLIDER_LEFT_PAD, offset + VOLUME_SLIDER_VERT_PAD,
ctx->width - VOLUME_SLIDER_PAD, self->height - 2 * VOLUME_SLIDER_VERT_PAD, 5, volume_pattern, &stuff);
draw_rounded_rectangle(ctx, stuff.level - VOLUME_SLIDER_BALL_RADIUS + VOLUME_SLIDER_LEFT_PAD, offset + 12 - VOLUME_SLIDER_BALL_RADIUS,
VOLUME_SLIDER_BALL_RADIUS * 2, VOLUME_SLIDER_BALL_RADIUS * 2, VOLUME_SLIDER_BALL_RADIUS, rgb(140,140,140));
draw_rounded_rectangle(ctx, stuff.level - VOLUME_SLIDER_BALL_RADIUS + 1 + VOLUME_SLIDER_LEFT_PAD, offset + 12 - VOLUME_SLIDER_BALL_RADIUS + 1,
VOLUME_SLIDER_BALL_RADIUS * 2 - 2, VOLUME_SLIDER_BALL_RADIUS * 2 - 2, VOLUME_SLIDER_BALL_RADIUS - 1, rgb(220,220,220));
}
int _menu_mouse_MenuEntry_Slider(struct MenuEntry * self, struct yutani_msg_window_mouse_event * event) {
if (event->buttons & YUTANI_MOUSE_BUTTON_LEFT) {
/* Figure out where it is */
float level = (float)(event->new_x - VOLUME_SLIDER_LEFT_PAD) / (float)(self->width - VOLUME_SLIDER_PAD);
if (level >= 1.0) level = 1.0;
if (level <= 0.0) level = 0.0;
if (volume_level != level * 0xFC000000) {
volume_level = level * 0xFC000000;
set_volume();
return 1;
}
}
return 0;
}
static struct MenuEntryVTable slider_vtable = {
.methods = 4,
.renderer = _menu_draw_MenuEntry_Slider,
.mouse_event = _menu_mouse_MenuEntry_Slider,
};
struct MenuEntry * menu_create_slider(void) {
struct MenuEntry * out = menu_create_separator(); /* Steal some defaults */
out->_type = -1; /* Special */
out->height = 24;
out->rwidth = 200;
out->vtable = &slider_vtable;
return out;
}
static int volume_left = 0;
static void show_volume_status(void) {
if (!volume_menu) {
@ -326,14 +400,7 @@ static void show_volume_status(void) {
free(node);
}
/* Add the current volume status */
char volume_level_label[100];
if (volume_level < 10) {
snprintf(volume_level_label, 99, "<b>Volume:</b> <i>Muted</i>");
} else {
snprintf(volume_level_label, 99, "<b>Volume:</b> %d%%", (int)(100 * ((float)volume_level / (float)0xFc000000)));
}
menu_insert(volume_menu, menu_create_normal(NULL, NULL, volume_level_label, NULL));
menu_insert(volume_menu, menu_create_slider());
/* TODO Our mixer supports multiple knobs and we could show all of them. */
/* TODO We could also show a nice slider... if we had one... */
@ -1392,6 +1459,11 @@ void _menu_draw_MenuEntry_Clock(gfx_context_t * ctx, struct MenuEntry * self, in
}
static struct MenuEntryVTable clock_vtable = {
.methods = 3,
.renderer = _menu_draw_MenuEntry_Clock,
};
struct MenuEntry * menu_create_clock(void) {
struct MenuEntry * out = menu_create_separator(); /* Steal some defaults */
@ -1403,7 +1475,7 @@ struct MenuEntry * menu_create_clock(void) {
out->_type = -1; /* Special */
out->height = 140;
out->rwidth = 148;
out->renderer = _menu_draw_MenuEntry_Clock;
out->vtable = &clock_vtable;
return out;
}
@ -1503,6 +1575,11 @@ void _menu_draw_MenuEntry_Calendar(gfx_context_t * ctx, struct MenuEntry * self,
}
}
static struct MenuEntryVTable calendar_vtable = {
.methods = 3,
.renderer = _menu_draw_MenuEntry_Calendar,
};
/*
* Special menu entry to display a calendar
*/
@ -1517,7 +1594,7 @@ struct MenuEntry * menu_create_calendar(void) {
tt_set_size(font_mono, 13);
out->rwidth = 200; //tt_string_width(font_mono, "XX XX XX XX XX XX XX") + 20;
out->renderer = _menu_draw_MenuEntry_Calendar;
out->vtable = &calendar_vtable;
return out;
}

View File

@ -4,6 +4,7 @@
#include <toaru/graphics.h>
#include <toaru/hashmap.h>
#include <toaru/list.h>
#include <toaru/yutani.h>
_Begin_C_Header
@ -15,6 +16,15 @@ enum MenuEntry_Type {
};
struct MenuList;
struct MenuEntry;
struct MenuEntryVTable {
size_t methods;
void (*renderer)(gfx_context_t *, struct MenuEntry *, int);
void (*focus_change)(struct MenuEntry *, int);
void (*activate)(struct MenuEntry *, int);
int (*mouse_event)(struct MenuEntry *, struct yutani_msg_window_mouse_event *);
};
struct MenuEntry {
enum MenuEntry_Type _type;
@ -27,10 +37,7 @@ struct MenuEntry {
int hilight; /* Is currently hilighted */
int offset; /* Our offset when we were rendered */
void (*renderer)(gfx_context_t *, struct MenuEntry *, int);
void (*focus_change)(struct MenuEntry *, int);
void (*activate)(struct MenuEntry *, int);
struct MenuEntryVTable * vtable;
void (*callback)(struct MenuEntry *);
};

View File

@ -127,15 +127,20 @@ void _menu_activate_MenuEntry_Normal(struct MenuEntry * self, int flags) {
}
}
static struct MenuEntryVTable _menu_vtable_MenuEntry_Normal = {
.methods = 3,
.renderer = _menu_draw_MenuEntry_Normal,
.focus_change = _menu_focus_MenuEntry_Normal,
.activate = _menu_activate_MenuEntry_Normal,
};
struct MenuEntry * menu_create_normal(const char * icon, const char * action, const char * title, void (*callback)(struct MenuEntry *)) {
struct MenuEntry_Normal * out = malloc(sizeof(struct MenuEntry_Normal));
out->_type = MenuEntry_Normal;
out->height = MENU_ENTRY_HEIGHT;
out->hilight = 0;
out->renderer = _menu_draw_MenuEntry_Normal;
out->focus_change = _menu_focus_MenuEntry_Normal;
out->activate = _menu_activate_MenuEntry_Normal;
out->vtable = &_menu_vtable_MenuEntry_Normal;
out->icon = icon ? strdup(icon) : NULL;
out->title = strdup(title);
out->action = action ? strdup(action) : NULL;
@ -164,7 +169,7 @@ void _menu_draw_MenuEntry_Submenu(gfx_context_t * ctx, struct MenuEntry * self,
void _menu_focus_MenuEntry_Submenu(struct MenuEntry * self, int focused) {
if (focused) {
self->activate(self, focused);
self->vtable->activate(self, focused);
}
}
@ -195,15 +200,20 @@ void _menu_activate_MenuEntry_Submenu(struct MenuEntry * self, int focused) {
}
static struct MenuEntryVTable _menu_vtable_MenuEntry_Submenu = {
.methods = 3,
.renderer = _menu_draw_MenuEntry_Submenu,
.focus_change = _menu_focus_MenuEntry_Submenu,
.activate = _menu_activate_MenuEntry_Submenu,
};
struct MenuEntry * menu_create_submenu(const char * icon, const char * action, const char * title) {
struct MenuEntry_Submenu * out = malloc(sizeof(struct MenuEntry_Submenu));
out->_type = MenuEntry_Submenu;
out->height = MENU_ENTRY_HEIGHT;
out->hilight = 0;
out->renderer = _menu_draw_MenuEntry_Submenu;
out->focus_change = _menu_focus_MenuEntry_Submenu;
out->activate = _menu_activate_MenuEntry_Submenu;
out->vtable = &_menu_vtable_MenuEntry_Submenu;
out->icon = icon ? strdup(icon) : NULL;
out->title = strdup(title);
out->action = action ? strdup(action) : NULL;
@ -232,16 +242,21 @@ void _menu_activate_MenuEntry_Separator(struct MenuEntry * self, int focused) {
}
static struct MenuEntryVTable _menu_vtable_MenuEntry_Separator = {
.methods = 3,
.renderer = _menu_draw_MenuEntry_Separator,
.focus_change = _menu_focus_MenuEntry_Separator,
.activate = _menu_activate_MenuEntry_Separator,
};
struct MenuEntry * menu_create_separator(void) {
struct MenuEntry_Separator * out = malloc(sizeof(struct MenuEntry_Separator));
out->_type = MenuEntry_Separator;
out->height = 6;
out->hilight = 0;
out->renderer = _menu_draw_MenuEntry_Separator;
out->focus_change = _menu_focus_MenuEntry_Separator;
out->rwidth = 10; /* at least a bit please */
out->activate = _menu_activate_MenuEntry_Separator;
out->vtable = &_menu_vtable_MenuEntry_Separator;
return (struct MenuEntry *)out;
}
@ -545,8 +560,8 @@ static void _menu_redraw(yutani_window_t * menu_window, yutani_t * yctx, struct
int offset = (menu->flags & MENU_FLAG_BUBBLE) ? 12 : 4;
foreach(node, entries) {
struct MenuEntry * entry = node->value;
if (entry->renderer) {
entry->renderer(ctx, entry, offset);
if (entry->vtable->methods >= 1 && entry->vtable->renderer) {
entry->vtable->renderer(ctx, entry, offset);
}
offset += entry->height;
@ -734,7 +749,9 @@ void menu_key_action(struct MenuList * menu, struct yutani_msg_key_event * me) {
if (hilighted) {
hilighted->hilight = 1;
if (hilighted->_type == MenuEntry_Submenu) {
hilighted->activate(hilighted, 0);
if (hilighted->vtable->methods >= 3 && hilighted->vtable->activate) {
hilighted->vtable->activate(hilighted, 0);
}
_menu_redraw(window,yctx,menu,1);
} else {
struct menu_bar * bar = NULL;
@ -764,7 +781,9 @@ void menu_key_action(struct MenuList * menu, struct yutani_msg_key_event * me) {
}
if (hilighted) {
hilighted->hilight = 1;
hilighted->activate(hilighted, 0);
if (hilighted->vtable->methods >= 3 && hilighted->vtable->activate) {
hilighted->vtable->activate(hilighted, 0);
}
}
} else if (me->event.keycode == KEY_ARROW_LEFT) {
if (menu->parent) {
@ -800,18 +819,26 @@ void menu_mouse_action(struct MenuList * menu, struct yutani_msg_window_mouse_ev
if (!entry->hilight) {
changed = 1;
entry->hilight = 1;
entry->focus_change(entry, 1);
if (entry->vtable->methods >= 2 && entry->vtable->focus_change) {
entry->vtable->focus_change(entry, 1);
}
}
if (me->command == YUTANI_MOUSE_EVENT_CLICK || _close_enough(me)) {
if (entry->activate) {
entry->activate(entry, 0);
if (entry->vtable->methods >= 4 && entry->vtable->mouse_event) {
if (entry->vtable->mouse_event(entry, me)) {
_menu_redraw(window,yctx,menu,1);
}
} else if (me->command == YUTANI_MOUSE_EVENT_CLICK || _close_enough(me)) {
if (entry->vtable->methods >= 3 && entry->vtable->activate) {
entry->vtable->activate(entry, 0);
}
}
} else {
if (entry->hilight) {
changed = 1;
entry->hilight = 0;
entry->focus_change(entry, 0);
if (entry->vtable->methods >= 2 && entry->vtable->focus_change) {
entry->vtable->focus_change(entry, 0);
}
}
}
offset += entry->height;