/* koto-expander.c * * Copyright 2021 Joshua Strobl * * 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. */ #include #include #include "koto-config.h" #include "koto-button.h" #include "koto-expander.h" enum { PROP_EXP_0, PROP_HEADER_ICON_NAME, PROP_HEADER_LABEL, PROP_HEADER_SECONDARY_BUTTON, PROP_CONTENT, N_EXP_PROPERTIES }; static GParamSpec *expander_props[N_EXP_PROPERTIES] = { NULL, }; struct _KotoExpander { GtkBox parent_instance; gboolean constructed; GtkWidget *header; KotoButton *header_button; gchar *icon_name; gchar *label; KotoButton *header_secondary_button; KotoButton *header_expand_button; GtkWidget *revealer; GtkWidget *content; }; struct _KotoExpanderClass { GtkBoxClass parent_class; }; G_DEFINE_TYPE(KotoExpander, koto_expander, GTK_TYPE_BOX); static void koto_expander_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec); static void koto_expander_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec); static void koto_expander_class_init(KotoExpanderClass *c) { GObjectClass *gobject_class = G_OBJECT_CLASS(c); gobject_class->set_property = koto_expander_set_property; gobject_class->get_property = koto_expander_get_property; expander_props[PROP_HEADER_ICON_NAME] = g_param_spec_string( "icon-name", "Icon Name", "Name of the icon to use in the Expander", "emblem-favorite-symbolic", G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE ); expander_props[PROP_HEADER_LABEL] = g_param_spec_string( "label", "Label", "Label for the Expander", NULL, G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE ); expander_props[PROP_HEADER_SECONDARY_BUTTON] = g_param_spec_object( "secondary-button", "Secondary Button", "Secondary Button to be placed next to Expander button", KOTO_TYPE_BUTTON, G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE ); expander_props[PROP_CONTENT] = g_param_spec_object( "content", "Content", "Content inside the Expander", GTK_TYPE_WIDGET, G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE ); g_object_class_install_properties(gobject_class, N_EXP_PROPERTIES, expander_props); } static void koto_expander_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) { KotoExpander *self = KOTO_EXPANDER(obj); switch (prop_id) { case PROP_HEADER_ICON_NAME: g_value_set_string(val, self->icon_name); break; case PROP_HEADER_LABEL: g_value_set_string(val, self->label); break; case PROP_HEADER_SECONDARY_BUTTON: g_value_set_object(val, (GObject *) self->header_secondary_button); break; case PROP_CONTENT: g_value_set_object(val, (GObject *) self->content); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec); break; } } static void koto_expander_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec) { KotoExpander *self = KOTO_EXPANDER(obj); if (!GTK_IS_WIDGET(self->header_button)) { // Header Button is not a widget KotoButton *new_button = koto_button_new_with_icon("Temporary Text", "emblem-favorite-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_SMALL); if (GTK_IS_WIDGET(new_button)) { // Created our widget successfully self->header_button = new_button; gtk_widget_set_hexpand(GTK_WIDGET(self->header_button), TRUE); gtk_box_prepend(GTK_BOX(self->header), GTK_WIDGET(self->header_button)); } } switch (prop_id) { case PROP_HEADER_ICON_NAME: g_return_if_fail(GTK_IS_WIDGET(self->header_button)); koto_button_set_icon_name(self->header_button, g_strdup(g_value_get_string(val)), FALSE); break; case PROP_HEADER_LABEL: g_return_if_fail(GTK_IS_WIDGET(self->header_button)); koto_button_set_text(self->header_button, g_strdup(g_value_get_string(val))); break; case PROP_HEADER_SECONDARY_BUTTON: koto_expander_set_secondary_button(self, (KotoButton*) g_value_get_object(val)); break; case PROP_CONTENT: koto_expander_set_content(self, (GtkWidget*) g_value_get_object(val)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec); break; } } static void koto_expander_init(KotoExpander *self) { GtkStyleContext *style = gtk_widget_get_style_context(GTK_WIDGET(self)); gtk_style_context_add_class(style, "expander"); gtk_widget_set_hexpand((GTK_WIDGET(self)), TRUE); self->header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10); GtkStyleContext *header_style = gtk_widget_get_style_context(self->header); gtk_style_context_add_class(header_style, "expander-header"); self->revealer = gtk_revealer_new(); gtk_revealer_set_reveal_child(GTK_REVEALER(self->revealer), TRUE); // Set to be revealed by default gtk_revealer_set_transition_type(GTK_REVEALER(self->revealer), GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN); self->header_expand_button = koto_button_new_with_icon("", "pan-down-symbolic", "pan-up-symbolic", KOTO_BUTTON_PIXBUF_SIZE_SMALL); gtk_box_append(GTK_BOX(self->header), GTK_WIDGET(self->header_expand_button)); gtk_box_prepend(GTK_BOX(self), self->header); gtk_box_append(GTK_BOX(self), self->revealer); self->constructed = TRUE; GtkGesture *controller = gtk_gesture_click_new(); // Create a new GtkGestureClick g_signal_connect(controller, "pressed", G_CALLBACK(koto_expander_toggle_content), self); gtk_widget_add_controller(GTK_WIDGET(self->header_expand_button), GTK_EVENT_CONTROLLER(controller)); } void koto_expander_set_secondary_button(KotoExpander *self, KotoButton *new_button) { if (!self->constructed) { return; } if (!GTK_IS_WIDGET(new_button)) { return; } if (GTK_IS_WIDGET(self->header_secondary_button)) { // Already have a button gtk_box_remove(GTK_BOX(self->header), GTK_WIDGET(self->header_secondary_button)); } self->header_secondary_button = new_button; gtk_box_append(GTK_BOX(self->header), GTK_WIDGET(self->header_secondary_button)); g_object_notify_by_pspec(G_OBJECT(self), expander_props[PROP_HEADER_SECONDARY_BUTTON]); } void koto_expander_set_content(KotoExpander *self, GtkWidget *new_content) { if (!self->constructed) { return; } if (GTK_IS_WIDGET(self->content)) { // Already have content gtk_revealer_set_child(GTK_REVEALER(self->revealer), NULL); g_object_unref(self->content); } self->content = new_content; gtk_revealer_set_child(GTK_REVEALER(self->revealer), self->content); g_object_notify_by_pspec(G_OBJECT(self), expander_props[PROP_CONTENT]); } void koto_expander_toggle_content(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data) { (void) gesture; (void) n_press; (void) x; (void) y; KotoExpander* self = data; koto_button_flip(KOTO_BUTTON(self->header_expand_button)); GtkRevealer* rev = GTK_REVEALER(self->revealer); gtk_revealer_set_reveal_child(rev, !gtk_revealer_get_reveal_child(rev)); // Invert our values } KotoExpander* koto_expander_new(gchar *primary_icon_name, gchar *primary_label_text) { return g_object_new(KOTO_TYPE_EXPANDER, "orientation", GTK_ORIENTATION_VERTICAL, "icon-name", primary_icon_name, "label", primary_label_text, NULL ); } KotoExpander* koto_expander_new_with_button(gchar *primary_icon_name, gchar *primary_label_text, KotoButton *secondary_button) { return g_object_new(KOTO_TYPE_EXPANDER, "orientation", GTK_ORIENTATION_VERTICAL, "icon-name", primary_icon_name, "label", primary_label_text, "secondary-button", secondary_button, NULL ); }