diff options
Diffstat (limited to 'csrc/widgets/control-center/control-details.c')
| -rw-r--r-- | csrc/widgets/control-center/control-details.c | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/csrc/widgets/control-center/control-details.c b/csrc/widgets/control-center/control-details.c new file mode 100644 index 0000000..72a90aa --- /dev/null +++ b/csrc/widgets/control-center/control-details.c @@ -0,0 +1,289 @@ +#include "widgets/control-center/control-details.h" + +// +// FoobarControlDetails: +// +// A revealer for details in the "controls" section of the control center. This widget is usually used to embed a list +// view. It takes care of scrolling by embedding the child in a GtkScrolledWindow. +// + +struct _FoobarControlDetails +{ + GtkWidget parent_instance; + GtkWidget* child; + gboolean is_expanded; + gint inset_top; +}; + +enum +{ + PROP_CHILD = 1, + PROP_IS_EXPANDED, + PROP_INSET_TOP, + N_PROPS, +}; + +static GParamSpec* props[N_PROPS] = { 0 }; + +static void foobar_control_details_class_init ( FoobarControlDetailsClass* klass ); +static void foobar_control_details_init ( FoobarControlDetails* self ); +static void foobar_control_details_get_property( GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec ); +static void foobar_control_details_set_property( GObject* object, + guint prop_id, + GValue const* value, + GParamSpec* pspec ); +static void foobar_control_details_dispose ( GObject* object ); + +G_DEFINE_FINAL_TYPE( FoobarControlDetails, foobar_control_details, GTK_TYPE_WIDGET ) + +// --------------------------------------------------------------------------------------------------------------------- +// Widget Implementation +// --------------------------------------------------------------------------------------------------------------------- + +// +// Static initialization for control details. +// +void foobar_control_details_class_init( FoobarControlDetailsClass* klass ) +{ + GtkWidgetClass* widget_klass = GTK_WIDGET_CLASS( klass ); + gtk_widget_class_set_css_name( widget_klass, "control-details" ); + gtk_widget_class_set_layout_manager_type( widget_klass, GTK_TYPE_BIN_LAYOUT ); + + GObjectClass* object_klass = G_OBJECT_CLASS( klass ); + object_klass->get_property = foobar_control_details_get_property; + object_klass->set_property = foobar_control_details_set_property; + object_klass->dispose = foobar_control_details_dispose; + + props[PROP_CHILD] = g_param_spec_object( + "child", + "Child", + "Child widget for the details revealer.", + GTK_TYPE_WIDGET, + G_PARAM_READWRITE ); + props[PROP_IS_EXPANDED] = g_param_spec_boolean( + "is-expanded", + "Is Expanded", + "Current state of the details revealer.", + FALSE, + G_PARAM_READWRITE ); + props[PROP_INSET_TOP] = g_param_spec_int( + "inset-top", + "Inset Top", + "Inset from the top edge (only visible when expanded).", + 0, + INT_MAX, + 0, + G_PARAM_READWRITE ); + g_object_class_install_properties( object_klass, N_PROPS, props ); +} + +// +// Instance initialization for control details. +// +void foobar_control_details_init( FoobarControlDetails* self ) +{ + GtkWidget* scrolled_window = gtk_scrolled_window_new( ); + gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrolled_window ), GTK_POLICY_NEVER, GTK_POLICY_EXTERNAL ); + gtk_scrolled_window_set_propagate_natural_height( GTK_SCROLLED_WINDOW( scrolled_window ), TRUE ); + g_object_bind_property( + self, + "child", + scrolled_window, + "child", + G_BINDING_SYNC_CREATE ); + g_object_bind_property( + self, + "inset-top", + scrolled_window, + "margin-top", + G_BINDING_SYNC_CREATE ); + + GtkWidget* revealer = gtk_revealer_new( ); + gtk_revealer_set_transition_type( GTK_REVEALER( revealer ), GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN ); + gtk_revealer_set_transition_duration( GTK_REVEALER( revealer ), 100 ); + gtk_revealer_set_child( GTK_REVEALER( revealer ), scrolled_window ); + g_object_bind_property( + self, + "is-expanded", + revealer, + "reveal-child", + G_BINDING_SYNC_CREATE ); + + gtk_widget_set_parent( revealer, GTK_WIDGET( self ) ); +} + +// +// Property getter implementation, mapping a property id to a method. +// +void foobar_control_details_get_property( + GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec ) +{ + FoobarControlDetails* self = (FoobarControlDetails*)object; + + switch ( prop_id ) + { + case PROP_CHILD: + g_value_set_object( value, foobar_control_details_get_child( self ) ); + break; + case PROP_IS_EXPANDED: + g_value_set_boolean( value, foobar_control_details_is_expanded( self ) ); + break; + case PROP_INSET_TOP: + g_value_set_int( value, foobar_control_details_get_inset_top( self ) ); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec ); + break; + } +} + +// +// Property setter implementation, mapping a property id to a method. +// +void foobar_control_details_set_property( + GObject* object, + guint prop_id, + GValue const* value, + GParamSpec* pspec ) +{ + FoobarControlDetails* self = (FoobarControlDetails*)object; + + switch ( prop_id ) + { + case PROP_CHILD: + foobar_control_details_set_child( self, g_value_get_object( value ) ); + break; + case PROP_IS_EXPANDED: + foobar_control_details_set_expanded( self, g_value_get_boolean( value ) ); + break; + case PROP_INSET_TOP: + foobar_control_details_set_inset_top( self, g_value_get_int( value ) ); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec ); + break; + } +} + +// +// Instance de-initialization for control details. +// +void foobar_control_details_dispose( GObject* object ) +{ + FoobarControlDetails* self = (FoobarControlDetails*)object; + + GtkWidget* child; + while ( ( child = gtk_widget_get_first_child( GTK_WIDGET( self ) ) ) ) + { + gtk_widget_unparent( child ); + } + + g_clear_object( &self->child ); + + G_OBJECT_CLASS( foobar_control_details_parent_class )->dispose( object ); +} + +// --------------------------------------------------------------------------------------------------------------------- +// Public API +// --------------------------------------------------------------------------------------------------------------------- + +// +// Create a new control details instance. +// +GtkWidget* foobar_control_details_new( void ) +{ + return g_object_new( FOOBAR_TYPE_CONTROL_DETAILS, NULL ); +} + +// +// Get the child widget for the details revealer. +// +GtkWidget* foobar_control_details_get_child( FoobarControlDetails* self ) +{ + g_return_val_if_fail( FOOBAR_IS_CONTROL_DETAILS( self ), NULL ); + return self->child; +} + +// +// Get the current state of the details revealer. +// +gboolean foobar_control_details_is_expanded( FoobarControlDetails* self ) +{ + g_return_val_if_fail( FOOBAR_IS_CONTROL_DETAILS( self ), FALSE ); + return self->is_expanded; +} + +// +// Get the inset from the top edge (only visible when expanded). +// +gint foobar_control_details_get_inset_top( FoobarControlDetails* self ) +{ + g_return_val_if_fail( FOOBAR_IS_CONTROL_DETAILS( self ), FALSE ); + return self->inset_top; +} + +// +// Update the child widget for the details revealer. +// +void foobar_control_details_set_child( + FoobarControlDetails* self, + GtkWidget* value ) +{ + g_return_if_fail( FOOBAR_IS_CONTROL_DETAILS( self ) ); + + if ( self->child != value ) + { + g_clear_object( &self->child ); + if ( value ) { self->child = g_object_ref( value ); } + g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_CHILD] ); + } +} + +// +// Update the current state of the details revealer. +// +void foobar_control_details_set_expanded( + FoobarControlDetails* self, + gboolean value ) +{ + g_return_if_fail( FOOBAR_IS_CONTROL_DETAILS( self ) ); + + value = !!value; + if ( self->is_expanded != value ) + { + self->is_expanded = value; + g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_IS_EXPANDED] ); + + if ( self->is_expanded ) + { + gtk_widget_add_css_class( GTK_WIDGET( self ), "expanded" ); + } + else + { + gtk_widget_remove_css_class( GTK_WIDGET( self ), "expanded" ); + } + } +} + +// +// Update the inset from the top edge (only visible when expanded). +// +void foobar_control_details_set_inset_top( + FoobarControlDetails* self, + gint value ) +{ + g_return_if_fail( FOOBAR_IS_CONTROL_DETAILS( self ) ); + + value = MAX( value, 0 ); + if ( self->inset_top != value ) + { + self->inset_top = value; + g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_INSET_TOP] ); + } +} |
