This repository has been archived on 2025-09-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
alsa-scarlett-gui/src/widget-drop-down.c
2025-02-21 04:08:35 +10:30

190 lines
4.9 KiB
C

// SPDX-FileCopyrightText: 2023-2025 Geoffrey D. Bennett <g@b4.vu>
// SPDX-License-Identifier: GPL-3.0-or-later
#include <ctype.h>
#include "gtkhelper.h"
#include "widget-drop-down.h"
struct drop_down {
struct alsa_elem *elem;
GtkWidget *button;
GtkWidget *popover;
GtkWidget *listview;
GtkSingleSelection *selection;
int fixed_text;
};
static void sanitise_class_name(char *s) {
char *dst = s;
while (*s) {
if (isalnum(*s) || *s == '-')
*dst++ = tolower(*s);
s++;
}
*dst = '\0';
}
static void add_class(GtkWidget *widget, const char *class) {
char *class_name = g_strdup_printf("selected-%s", class);
sanitise_class_name(class_name);
gtk_widget_add_css_class(widget, class_name);
g_free(class_name);
}
static void list_item_activated(
GtkListItem *list_item,
guint index,
struct drop_down *data
) {
alsa_set_elem_value(data->elem, index);
gtk_popover_popdown(GTK_POPOVER(data->popover));
}
static void toggle_button_clicked(GtkWidget *widget, struct drop_down *data) {
gtk_popover_popup(GTK_POPOVER(data->popover));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->button), FALSE);
}
static void setup_factory(
GtkListItemFactory *factory,
GtkListItem *list_item,
gpointer user_data
) {
GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
GtkWidget *label = gtk_label_new(NULL);
gtk_label_set_xalign(GTK_LABEL(label), 0.0);
gtk_box_append(GTK_BOX(box), label);
GtkWidget *icon = gtk_image_new_from_icon_name("object-select-symbolic");
gtk_box_append(GTK_BOX(box), icon);
gtk_list_item_set_child(list_item, box);
}
static void update_list_item(
GtkListItem *list_item,
struct drop_down *data
) {
GtkWidget *box = gtk_list_item_get_child(list_item);
GtkWidget *icon = gtk_widget_get_last_child(box);
int index = gtk_single_selection_get_selected(data->selection);
if (index == gtk_list_item_get_position(list_item))
gtk_widget_set_opacity(icon, 1.0);
else
gtk_widget_set_opacity(icon, 0.0);
}
static void bind_factory(
GtkListItemFactory *factory,
GtkListItem *list_item,
gpointer user_data
) {
struct drop_down *data = user_data;
GtkWidget *box = gtk_list_item_get_child(list_item);
GtkWidget *label = gtk_widget_get_first_child(box);
int index = gtk_list_item_get_position(list_item);
const char *text = alsa_get_item_name(data->elem, index);
gtk_label_set_text(GTK_LABEL(label), text);
update_list_item(list_item, data);
}
static void drop_down_updated(
struct alsa_elem *elem,
void *private
) {
struct drop_down *data = private;
int is_writable = alsa_get_elem_writable(elem);
gtk_widget_set_sensitive(data->button, is_writable);
int value = alsa_get_elem_value(elem);
gtk_single_selection_set_selected(data->selection, value);
gtk_widget_remove_css_classes_by_prefix(data->button, "selected-");
add_class(data->button, alsa_get_item_name(elem, value));
if (data->fixed_text)
return;
gtk_button_set_label(
GTK_BUTTON(data->button),
alsa_get_item_name(elem, value)
);
}
static void drop_down_destroy(GtkWidget *widget, GtkWidget *popover) {
gtk_widget_unparent(popover);
}
GtkWidget *make_drop_down_alsa_elem(
struct alsa_elem *elem,
const char *label_text
) {
struct drop_down *data = g_malloc(sizeof(struct drop_down));
data->elem = elem;
data->button = gtk_toggle_button_new_with_label(label_text);
gtk_widget_add_css_class(data->button, "drop-down");
data->fixed_text = !!label_text;
data->popover = gtk_popover_new();
gtk_popover_set_has_arrow(GTK_POPOVER(data->popover), FALSE);
gtk_widget_set_parent(
data->popover,
gtk_widget_get_first_child(data->button)
);
g_signal_connect(
gtk_widget_get_first_child(data->button),
"destroy", G_CALLBACK(drop_down_destroy), data->popover
);
GListModel *model = G_LIST_MODEL(gtk_string_list_new(NULL));
int count = alsa_get_item_count(elem);
for (int i = 0; i < count; i++) {
const char *text = alsa_get_item_name(elem, i);
gtk_string_list_append(GTK_STRING_LIST(model), text);
}
GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
g_signal_connect(
factory, "setup", G_CALLBACK(setup_factory), data
);
g_signal_connect(
factory, "bind", G_CALLBACK(bind_factory), data
);
data->selection = gtk_single_selection_new(model);
data->listview = gtk_list_view_new(
GTK_SELECTION_MODEL(data->selection),
factory
);
gtk_list_view_set_single_click_activate(GTK_LIST_VIEW(data->listview), TRUE);
gtk_popover_set_child(GTK_POPOVER(data->popover), data->listview);
g_signal_connect(
data->button, "clicked", G_CALLBACK(toggle_button_clicked), data
);
g_signal_connect(
data->listview, "activate", G_CALLBACK(list_item_activated), data
);
drop_down_updated(elem, data);
alsa_elem_add_callback(elem, drop_down_updated, data);
return data->button;
}