diff options
| author | Hannes Schulze <haschu0103@outlook.de> | 2025-06-19 23:17:58 +0200 |
|---|---|---|
| committer | Hannes Schulze <haschu0103@outlook.de> | 2025-06-19 23:17:58 +0200 |
| commit | 1af3d14f3f995645a0de08a54b244c19840709e9 (patch) | |
| tree | 3c4098863d8a8fd004a2acc8912a23b6036ab4dd /src/control-center.c | |
| parent | 40efcf60a5f39674b5137f11de7bcfe37fb121b5 (diff) | |
init hs project
Diffstat (limited to 'src/control-center.c')
| -rw-r--r-- | src/control-center.c | 821 |
1 files changed, 0 insertions, 821 deletions
diff --git a/src/control-center.c b/src/control-center.c deleted file mode 100644 index bb22830..0000000 --- a/src/control-center.c +++ /dev/null @@ -1,821 +0,0 @@ -#include "control-center.h" -#include "widgets/control-center/control-button.h" -#include "widgets/control-center/control-slider.h" -#include "widgets/control-center/control-details.h" -#include "widgets/control-center/control-details-item.h" -#include "widgets/inset-container.h" -#include "widgets/notification-widget.h" -#include <gtk4-layer-shell.h> - -#define STACK_ITEM_LIST "list" -#define STACK_ITEM_PLACEHOLDER "placeholder" - -// -// FoobarControlCenter: -// -// The control center is made up of two sections: -// - The "controls" section for configuring wi-fi, volume, brightness, etc. -// - The "notifications" section showing all notifications (including dismissed ones) and allowing the user to close -// them. -// - -struct _FoobarControlCenter -{ - GtkWindow parent_instance; - GtkWidget* control_container; - GtkWidget* notification_list; - GtkWidget* notification_container; - GtkWidget* notification_placeholder; - GtkWidget* notification_stack; - GtkWidget* layout; - FoobarBrightnessService* brightness_service; - FoobarAudioService* audio_service; - FoobarNetworkService* network_service; - FoobarBluetoothService* bluetooth_service; - FoobarNotificationService* notification_service; - FoobarConfigurationService* configuration_service; - gchar* notification_time_format; - gint notification_min_height; - gint notification_close_button_inset; - gint padding; - gint spacing; - gulong config_handler_id; -}; - -static void foobar_control_center_class_init ( FoobarControlCenterClass* klass ); -static void foobar_control_center_init ( FoobarControlCenter* self ); -static void foobar_control_center_finalize ( GObject* object ); -static void foobar_control_center_handle_notification_setup ( GtkListItemFactory* factory, - GtkListItem* list_item, - gpointer userdata ); -static void foobar_control_center_handle_network_setup ( GtkListItemFactory* factory, - GtkListItem* list_item, - gpointer userdata ); -static void foobar_control_center_handle_bluetooth_device_setup ( GtkListItemFactory* factory, - GtkListItem* list_item, - gpointer userdata ); -static void foobar_control_center_handle_bluetooth_device_activate ( GtkListView* view, - guint position, - gpointer userdata ); -static void foobar_control_center_handle_audio_device_setup ( GtkListItemFactory* factory, - GtkListItem* list_item, - gpointer userdata ); -static void foobar_control_center_handle_audio_device_activate ( GtkListView* view, - guint position, - gpointer userdata ); -static void foobar_control_center_handle_config_change ( GObject* object, - GParamSpec* pspec, - gpointer userdata ); -static FoobarControlDetailsAccessory foobar_control_center_compute_bluetooth_device_accessory ( GtkExpression* expression, - FoobarBluetoothDeviceState state, - gpointer userdata ); -static gchar* foobar_control_center_compute_visible_notification_child_name( GtkExpression* expression, - guint count, - gpointer userdata ); - -G_DEFINE_FINAL_TYPE( FoobarControlCenter, foobar_control_center, GTK_TYPE_WINDOW ) - -// --------------------------------------------------------------------------------------------------------------------- -// Window Implementation -// --------------------------------------------------------------------------------------------------------------------- - -// -// Static initialization for the control center. -// -void foobar_control_center_class_init( FoobarControlCenterClass* klass ) -{ - GObjectClass* object_klass = G_OBJECT_CLASS( klass ); - object_klass->finalize = foobar_control_center_finalize; -} - -// -// Instance initialization for the control center. -// -void foobar_control_center_init( FoobarControlCenter* self ) -{ - self->control_container = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 ); - gtk_widget_add_css_class( self->control_container, "controls" ); - gtk_widget_set_hexpand( self->control_container, TRUE ); - - GtkListItemFactory* notification_factory = gtk_signal_list_item_factory_new( ); - g_signal_connect( - notification_factory, - "setup", - G_CALLBACK( foobar_control_center_handle_notification_setup ), - self ); - self->notification_list = gtk_list_view_new( NULL, notification_factory ); - gtk_widget_add_css_class( self->notification_list, "notifications" ); - - // XXX: Get rid of this container to enable virtualization; currently needed because I haven't figured out how to - // set padding without using css (setting margin on list view causes glitches when scrolling). - self->notification_container = foobar_inset_container_new( ); - foobar_inset_container_set_child( FOOBAR_INSET_CONTAINER( self->notification_container ), self->notification_list ); - - GtkWidget* scrolled_window = gtk_scrolled_window_new( ); - gtk_scrolled_window_set_child( GTK_SCROLLED_WINDOW( scrolled_window ), self->notification_container ); - gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrolled_window ), GTK_POLICY_NEVER, GTK_POLICY_EXTERNAL ); - - self->notification_placeholder = gtk_label_new( "No Notifications" ); - gtk_widget_add_css_class( self->notification_placeholder, "placeholder" ); - gtk_label_set_ellipsize( GTK_LABEL( self->notification_placeholder ), PANGO_ELLIPSIZE_END ); - gtk_label_set_wrap( GTK_LABEL( self->notification_placeholder ), FALSE ); - - self->notification_stack = gtk_stack_new( ); - gtk_stack_add_named( GTK_STACK( self->notification_stack ), scrolled_window, STACK_ITEM_LIST ); - gtk_stack_add_named( GTK_STACK( self->notification_stack ), self->notification_placeholder, STACK_ITEM_PLACEHOLDER ); - gtk_widget_set_vexpand( self->notification_stack, TRUE ); - gtk_widget_set_hexpand( self->notification_stack, TRUE ); - - self->layout = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 ); - gtk_box_append( GTK_BOX( self->layout ), self->control_container ); - gtk_box_append( GTK_BOX( self->layout ), gtk_separator_new( GTK_ORIENTATION_HORIZONTAL ) ); - gtk_box_append( GTK_BOX( self->layout ), self->notification_stack ); - - g_autoptr( GtkSizeGroup ) size_group = gtk_size_group_new( GTK_SIZE_GROUP_HORIZONTAL ); - gtk_size_group_add_widget( size_group, self->control_container ); - gtk_size_group_add_widget( size_group, self->notification_stack ); - - gtk_window_set_child( GTK_WINDOW( self ), self->layout ); - gtk_window_set_title( GTK_WINDOW( self ), "Foobar Control Center" ); - gtk_widget_add_css_class( GTK_WIDGET( self ), "control-center" ); - gtk_layer_init_for_window( GTK_WINDOW( self ) ); - gtk_layer_set_namespace( GTK_WINDOW( self ), "foobar-control-center" ); -} - -// -// Instance cleanup for the control center. -// -void foobar_control_center_finalize( GObject* object ) -{ - FoobarControlCenter* self = (FoobarControlCenter*)object; - - g_clear_signal_handler( &self->config_handler_id, self->configuration_service ); - g_clear_object( &self->brightness_service ); - g_clear_object( &self->audio_service ); - g_clear_object( &self->network_service ); - g_clear_object( &self->bluetooth_service ); - g_clear_object( &self->configuration_service ); - g_clear_object( &self->notification_service ); - g_clear_pointer( &self->notification_time_format, g_free ); - - G_OBJECT_CLASS( foobar_control_center_parent_class )->finalize( object ); -} - -// --------------------------------------------------------------------------------------------------------------------- -// Public API -// --------------------------------------------------------------------------------------------------------------------- - -// -// Create a new control center instance. -// -FoobarControlCenter* foobar_control_center_new( - FoobarBrightnessService* brightness_service, - FoobarAudioService* audio_service, - FoobarNetworkService* network_service, - FoobarBluetoothService* bluetooth_service, - FoobarNotificationService* notification_service, - FoobarConfigurationService* configuration_service ) -{ - g_return_val_if_fail( FOOBAR_IS_BRIGHTNESS_SERVICE( brightness_service ), NULL ); - g_return_val_if_fail( FOOBAR_IS_AUDIO_SERVICE( audio_service ), NULL ); - g_return_val_if_fail( FOOBAR_IS_NETWORK_SERVICE( network_service ), NULL ); - g_return_val_if_fail( FOOBAR_IS_BLUETOOTH_SERVICE( bluetooth_service ), NULL ); - g_return_val_if_fail( FOOBAR_IS_NOTIFICATION_SERVICE( notification_service ), NULL ); - g_return_val_if_fail( FOOBAR_IS_CONFIGURATION_SERVICE( configuration_service ), NULL ); - - FoobarControlCenter* self = g_object_new( FOOBAR_TYPE_CONTROL_CENTER, NULL ); - self->brightness_service = g_object_ref( brightness_service ); - self->audio_service = g_object_ref( audio_service ); - self->network_service = g_object_ref( network_service ); - self->bluetooth_service = g_object_ref( bluetooth_service ); - self->notification_service = g_object_ref( notification_service ); - self->configuration_service = g_object_ref( configuration_service ); - - // Apply the configuration and subscribe to changes. - - FoobarConfiguration const* config = foobar_configuration_service_get_current( self->configuration_service ); - foobar_control_center_apply_configuration( - self, - foobar_configuration_get_control_center( config ), - foobar_configuration_get_notifications( config ) ); - self->config_handler_id = g_signal_connect( - self->configuration_service, - "notify::current", - G_CALLBACK( foobar_control_center_handle_config_change ), - self ); - - // Set up the notifications list view. - - GListModel* source_model = foobar_notification_service_get_notifications( self->notification_service ); - GtkNoSelection* selection_model = gtk_no_selection_new( g_object_ref( source_model ) ); - gtk_list_view_set_model( GTK_LIST_VIEW( self->notification_list ), GTK_SELECTION_MODEL( selection_model ) ); - - // Set up bindings. - - { - GtkExpression* count_expr = gtk_property_expression_new( GTK_TYPE_SORT_LIST_MODEL, NULL, "n-items" ); - GtkExpression* child_params[] = { count_expr }; - GtkExpression* child_expr = gtk_cclosure_expression_new( - G_TYPE_STRING, - NULL, - G_N_ELEMENTS( child_params ), - child_params, - G_CALLBACK( foobar_control_center_compute_visible_notification_child_name ), - NULL, - NULL ); - gtk_expression_bind( child_expr, self->notification_stack, "visible-child-name", source_model ); - } - - return self; -} - -// -// Apply the control center configuration provided by the configuration service, additionally using the provided -// notification configuration for some settings. -// -void foobar_control_center_apply_configuration( - FoobarControlCenter* self, - FoobarControlCenterConfiguration const* config, - FoobarNotificationConfiguration const* notification_config ) -{ - g_return_if_fail( FOOBAR_IS_CONTROL_CENTER( self ) ); - g_return_if_fail( config != NULL ); - g_return_if_fail( notification_config != NULL ); - - // Copy configuration into member variables. - - g_clear_pointer( &self->notification_time_format, g_free ); - self->notification_time_format = g_strdup( foobar_notification_configuration_get_time_format( notification_config ) ); - self->notification_min_height = foobar_notification_configuration_get_min_height( notification_config ); - self->notification_close_button_inset = foobar_notification_configuration_get_close_button_inset( notification_config ); - self->padding = foobar_control_center_configuration_get_padding( config ); - self->spacing = foobar_control_center_configuration_get_spacing( config ); - - // Configure the window. - - gtk_window_set_default_size( - GTK_WINDOW( self ), - foobar_control_center_configuration_get_width( config ), - foobar_control_center_configuration_get_height( config ) ); - - FoobarScreenEdge position = foobar_control_center_configuration_get_position( config ); - FoobarControlCenterAlignment alignment = foobar_control_center_configuration_get_alignment( config ); - gboolean attach_left = ( position == FOOBAR_SCREEN_EDGE_LEFT ); - gboolean attach_right = ( position == FOOBAR_SCREEN_EDGE_RIGHT ); - gboolean attach_top = ( position == FOOBAR_SCREEN_EDGE_TOP ); - gboolean attach_bottom = ( position == FOOBAR_SCREEN_EDGE_BOTTOM ); - switch ( position ) - { - case FOOBAR_SCREEN_EDGE_LEFT: - case FOOBAR_SCREEN_EDGE_RIGHT: - attach_top = - ( alignment == FOOBAR_CONTROL_CENTER_ALIGNMENT_START ) || - ( alignment == FOOBAR_CONTROL_CENTER_ALIGNMENT_FILL ); - attach_bottom = - ( alignment == FOOBAR_CONTROL_CENTER_ALIGNMENT_END ) || - ( alignment == FOOBAR_CONTROL_CENTER_ALIGNMENT_FILL ); - break; - case FOOBAR_SCREEN_EDGE_TOP: - case FOOBAR_SCREEN_EDGE_BOTTOM: - attach_left = - ( alignment == FOOBAR_CONTROL_CENTER_ALIGNMENT_START ) || - ( alignment == FOOBAR_CONTROL_CENTER_ALIGNMENT_FILL ); - attach_right = - ( alignment == FOOBAR_CONTROL_CENTER_ALIGNMENT_END ) || - ( alignment == FOOBAR_CONTROL_CENTER_ALIGNMENT_FILL ); - break; - default: - g_warn_if_reached( ); - break; - } - - gtk_layer_set_anchor( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_LEFT, attach_left ); - gtk_layer_set_anchor( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_RIGHT, attach_right ); - gtk_layer_set_anchor( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_TOP, attach_top ); - gtk_layer_set_anchor( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_BOTTOM, attach_bottom ); - - gint offset = foobar_control_center_configuration_get_offset( config ); - gtk_layer_set_margin( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_LEFT, attach_left ? offset : 0 ); - gtk_layer_set_margin( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_RIGHT, attach_right ? offset : 0 ); - gtk_layer_set_margin( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_TOP, attach_top ? offset : 0 ); - gtk_layer_set_margin( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_BOTTOM, attach_bottom ? offset : 0 ); - - // Configure the orientation. - - FoobarOrientation orientation = foobar_control_center_configuration_get_orientation( config ); - switch ( orientation ) - { - case FOOBAR_ORIENTATION_HORIZONTAL: - gtk_orientable_set_orientation( GTK_ORIENTABLE( self->layout ), GTK_ORIENTATION_HORIZONTAL ); - break; - case FOOBAR_ORIENTATION_VERTICAL: - gtk_orientable_set_orientation( GTK_ORIENTABLE( self->layout ), GTK_ORIENTATION_VERTICAL ); - break; - default: - g_warn_if_reached( ); - break; - } - - // Adjust spacing for the containers. - - foobar_inset_container_set_inset_vertical( FOOBAR_INSET_CONTAINER( self->notification_container ), -self->spacing / 2 ); - gtk_widget_set_margin_top( self->notification_container, self->padding ); - gtk_widget_set_margin_bottom( self->notification_container, self->padding ); - - gtk_widget_set_margin_start( self->notification_placeholder, self->padding ); - gtk_widget_set_margin_end( self->notification_placeholder, self->padding ); - gtk_widget_set_margin_top( self->notification_placeholder, self->padding ); - gtk_widget_set_margin_bottom( self->notification_placeholder, self->padding ); - - gtk_box_set_spacing( GTK_BOX( self->control_container ), self->spacing ); - gtk_widget_set_margin_top( self->control_container, self->padding ); - gtk_widget_set_margin_bottom( self->control_container, self->padding ); - - // Re-create list items by resetting the factory. - - GtkListItemFactory* factory = gtk_list_view_get_factory( GTK_LIST_VIEW( self->notification_list ) ); - g_object_ref( factory ); - gtk_list_view_set_factory( GTK_LIST_VIEW( self->notification_list ), NULL ); - gtk_list_view_set_factory( GTK_LIST_VIEW( self->notification_list ), factory ); - g_object_unref( factory ); - - // Re-create control rows. - - GtkWidget* row_widget; - while ( ( row_widget = gtk_widget_get_first_child( self->control_container ) ) ) - { - gtk_widget_unparent( row_widget ); - } - - gsize rows_count; - FoobarControlCenterRow const* rows = foobar_control_center_configuration_get_rows( config, &rows_count ); - for ( gsize i = 0; i < rows_count; ++i ) - { - // Add rows. If there are details for a row, add them into another box that is then added to the - // control_container to circumvent the spacing of the box. - - switch ( rows[i] ) - { - case FOOBAR_CONTROL_CENTER_ROW_CONNECTIVITY: - { - // Create the buttons for network and bluetooth. - - GtkWidget* network_button = foobar_control_button_new( ); - - GtkWidget* bluetooth_button = foobar_control_button_new( ); - foobar_control_button_set_icon_name( FOOBAR_CONTROL_BUTTON( bluetooth_button ), "fluent-bluetooth-symbolic" ); - foobar_control_button_set_label( FOOBAR_CONTROL_BUTTON( bluetooth_button ), "Bluetooth" ); - - GtkWidget* button_container = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, self->spacing ); - gtk_box_set_homogeneous( GTK_BOX( button_container ), TRUE ); - gtk_widget_set_margin_start( button_container, self->padding ); - gtk_widget_set_margin_end( button_container, self->padding ); - gtk_box_append( GTK_BOX( button_container ), network_button ); - gtk_box_append( GTK_BOX( button_container ), bluetooth_button ); - - GtkWidget* row = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 ); - gtk_box_append( GTK_BOX( row ), button_container ); - - // Show either a button for managing Wi-Fi or a static element for ethernet. - - FoobarNetworkAdapterWifi* wifi_adapter = foobar_network_service_get_wifi( self->network_service ); - if ( wifi_adapter ) - { - foobar_control_button_set_icon_name( FOOBAR_CONTROL_BUTTON( network_button ), "fluent-wifi-1-symbolic" ); - foobar_control_button_set_label( FOOBAR_CONTROL_BUTTON( network_button ), "Wi-Fi" ); - foobar_control_button_set_can_expand( FOOBAR_CONTROL_BUTTON( network_button ), TRUE ); - foobar_control_button_set_can_toggle( FOOBAR_CONTROL_BUTTON( network_button ), TRUE ); - g_object_bind_property( - wifi_adapter, - "is-enabled", - network_button, - "is-toggled", - G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL ); - g_object_bind_property( - network_button, - "is-toggled", - network_button, - "can-expand", - G_BINDING_SYNC_CREATE ); - g_object_bind_property( - network_button, - "is-expanded", - wifi_adapter, - "is-scanning", - G_BINDING_SYNC_CREATE ); - - GtkListItemFactory* wifi_item_factory = gtk_signal_list_item_factory_new( ); - g_signal_connect( - wifi_item_factory, - "setup", - G_CALLBACK( foobar_control_center_handle_network_setup ), - self ); - - GListModel* wifi_source_model = g_object_ref( foobar_network_adapter_wifi_get_networks( wifi_adapter ) ); - GtkNoSelection* wifi_selection_model = gtk_no_selection_new( wifi_source_model ); - - GtkWidget* wifi_list_view = gtk_list_view_new( GTK_SELECTION_MODEL( wifi_selection_model ), wifi_item_factory ); - - GtkWidget* wifi_details = foobar_control_details_new( ); - foobar_control_details_set_inset_top( FOOBAR_CONTROL_DETAILS( wifi_details ), self->spacing ); - foobar_control_details_set_child( FOOBAR_CONTROL_DETAILS( wifi_details ), wifi_list_view ); - g_object_bind_property( - network_button, - "is-expanded", - wifi_details, - "is-expanded", - G_BINDING_SYNC_CREATE ); - - gtk_box_append( GTK_BOX( row ), wifi_details ); - } - else - { - foobar_control_button_set_icon_name( FOOBAR_CONTROL_BUTTON( network_button ), "fluent-virtual-network-symbolic" ); - foobar_control_button_set_label( FOOBAR_CONTROL_BUTTON( network_button ), "Ethernet" ); - foobar_control_button_set_can_expand( FOOBAR_CONTROL_BUTTON( network_button ), FALSE ); - foobar_control_button_set_can_toggle( FOOBAR_CONTROL_BUTTON( network_button ), FALSE ); - } - - // Enable interactions for the bluetooth button if available. - - g_object_bind_property( - self->bluetooth_service, - "is-available", - bluetooth_button, - "can-toggle", - G_BINDING_SYNC_CREATE ); - g_object_bind_property( - self->bluetooth_service, - "is-enabled", - bluetooth_button, - "is-toggled", - G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL ); - g_object_bind_property( - bluetooth_button, - "is-toggled", - bluetooth_button, - "can-expand", - G_BINDING_SYNC_CREATE ); - g_object_bind_property( - bluetooth_button, - "is-expanded", - self->bluetooth_service, - "is-scanning", - G_BINDING_SYNC_CREATE ); - - GtkListItemFactory* bluetooth_item_factory = gtk_signal_list_item_factory_new( ); - g_signal_connect( - bluetooth_item_factory, - "setup", - G_CALLBACK( foobar_control_center_handle_bluetooth_device_setup ), - self ); - - GListModel* bluetooth_source_model = g_object_ref( foobar_bluetooth_service_get_devices( self->bluetooth_service ) ); - GtkNoSelection* bluetooth_selection_model = gtk_no_selection_new( bluetooth_source_model ); - - GtkWidget* bluetooth_list_view = gtk_list_view_new( - GTK_SELECTION_MODEL( bluetooth_selection_model ), - bluetooth_item_factory ); - gtk_list_view_set_single_click_activate( GTK_LIST_VIEW( bluetooth_list_view ), TRUE ); - g_signal_connect( - bluetooth_list_view, - "activate", - G_CALLBACK( foobar_control_center_handle_bluetooth_device_activate ), - NULL ); - - GtkWidget* bluetooth_details = foobar_control_details_new( ); - foobar_control_details_set_inset_top( FOOBAR_CONTROL_DETAILS( bluetooth_details ), self->spacing ); - foobar_control_details_set_child( FOOBAR_CONTROL_DETAILS( bluetooth_details ), bluetooth_list_view ); - g_object_bind_property( - bluetooth_button, - "is-expanded", - bluetooth_details, - "is-expanded", - G_BINDING_SYNC_CREATE ); - - gtk_box_append( GTK_BOX( row ), bluetooth_details ); - - gtk_box_append( GTK_BOX( self->control_container ), row ); - break; - } - case FOOBAR_CONTROL_CENTER_ROW_AUDIO_OUTPUT: - case FOOBAR_CONTROL_CENTER_ROW_AUDIO_INPUT: - { - FoobarAudioDevice* default_device; - GListModel* device_list; - gchar const* icon_name; - gchar const* label; - if ( rows[i] == FOOBAR_CONTROL_CENTER_ROW_AUDIO_OUTPUT ) - { - default_device = foobar_audio_service_get_default_output( self->audio_service ); - device_list = foobar_audio_service_get_outputs( self->audio_service ); - icon_name = "fluent-speaker-2-symbolic"; - label = "Audio Output"; - } - else - { - default_device = foobar_audio_service_get_default_input( self->audio_service ); - device_list = foobar_audio_service_get_inputs( self->audio_service ); - icon_name = "fluent-microphone-symbolic"; - label = "Audio Input"; - } - - // Create the slider for controlling the volume. - - GtkWidget* slider = foobar_control_slider_new( ); - gtk_widget_set_margin_start( slider, self->padding ); - gtk_widget_set_margin_end( slider, self->padding ); - foobar_control_slider_set_icon_name( FOOBAR_CONTROL_SLIDER( slider ), icon_name ); - foobar_control_slider_set_label( FOOBAR_CONTROL_SLIDER( slider ), label ); - foobar_control_slider_set_can_expand( FOOBAR_CONTROL_SLIDER( slider ), TRUE ); - g_object_bind_property( - default_device, - "volume", - slider, - "percentage", - G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL ); - - // Create the details view for selecting the audio device. - - GtkListItemFactory* item_factory = gtk_signal_list_item_factory_new( ); - g_signal_connect( item_factory, "setup", G_CALLBACK( foobar_control_center_handle_audio_device_setup ), self ); - - GListModel* source_model = g_object_ref( device_list ); - GtkNoSelection* selection_model = gtk_no_selection_new( source_model ); - - GtkWidget* list_view = gtk_list_view_new( GTK_SELECTION_MODEL( selection_model ), item_factory ); - gtk_list_view_set_single_click_activate( GTK_LIST_VIEW( list_view ), TRUE ); - g_signal_connect( - list_view, - "activate", - G_CALLBACK( foobar_control_center_handle_audio_device_activate ), - NULL ); - - GtkWidget* details = foobar_control_details_new( ); - foobar_control_details_set_inset_top( FOOBAR_CONTROL_DETAILS( details ), self->spacing ); - foobar_control_details_set_child( FOOBAR_CONTROL_DETAILS( details ), list_view ); - g_object_bind_property( - slider, - "is-expanded", - details, - "is-expanded", - G_BINDING_SYNC_CREATE ); - - GtkWidget* row = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 ); - gtk_box_append( GTK_BOX( row ), slider ); - gtk_box_append( GTK_BOX( row ), details ); - - gtk_box_append( GTK_BOX( self->control_container ), row ); - break; - } - case FOOBAR_CONTROL_CENTER_ROW_BRIGHTNESS: - { - // Create the slider for adjusting the brightness. - - GtkWidget* slider = foobar_control_slider_new( ); - gtk_widget_set_margin_start( slider, self->padding ); - gtk_widget_set_margin_end( slider, self->padding ); - foobar_control_slider_set_icon_name( FOOBAR_CONTROL_SLIDER( slider ), "fluent-brightness-high-symbolic" ); - foobar_control_slider_set_label( FOOBAR_CONTROL_SLIDER( slider ), "Brightness" ); - g_object_bind_property( - self->brightness_service, - "percentage", - slider, - "percentage", - G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL ); - - gtk_box_append( GTK_BOX( self->control_container ), slider ); - break; - } - default: - { - g_warn_if_reached( ); - break; - } - } - } -} - -// --------------------------------------------------------------------------------------------------------------------- -// Signal Handlers -// --------------------------------------------------------------------------------------------------------------------- - -// -// Called by the notification list view to create a widget for displaying a notification. -// -void foobar_control_center_handle_notification_setup( - GtkListItemFactory* factory, - GtkListItem* list_item, - gpointer userdata ) -{ - (void)factory; - FoobarControlCenter* self = (FoobarControlCenter*)userdata; - - GtkWidget* widget = foobar_notification_widget_new( ); - foobar_notification_widget_set_close_action( FOOBAR_NOTIFICATION_WIDGET( widget ), FOOBAR_TYPE_NOTIFICATION_CLOSE_ACTION_REMOVE ); - foobar_notification_widget_set_time_format( FOOBAR_NOTIFICATION_WIDGET( widget ), self->notification_time_format ); - foobar_notification_widget_set_min_height( FOOBAR_NOTIFICATION_WIDGET( widget ), self->notification_min_height ); - foobar_notification_widget_set_close_button_inset( FOOBAR_NOTIFICATION_WIDGET( widget ), self->notification_close_button_inset ); - foobar_notification_widget_set_inset_start( FOOBAR_NOTIFICATION_WIDGET( widget ), self->padding ); - foobar_notification_widget_set_inset_end( FOOBAR_NOTIFICATION_WIDGET( widget ), self->padding ); - foobar_notification_widget_set_inset_top( FOOBAR_NOTIFICATION_WIDGET( widget ), self->spacing / 2 ); - foobar_notification_widget_set_inset_bottom( FOOBAR_NOTIFICATION_WIDGET( widget ), self->spacing / 2 ); - gtk_list_item_set_child( list_item, widget ); - - { - GtkExpression* item_expr = gtk_property_expression_new( GTK_TYPE_LIST_ITEM, NULL, "item" ); - gtk_expression_bind( item_expr, widget, "notification", list_item ); - } -} - -// -// Called by the wi-fi details list view to create a widget for displaying a network. -// -void foobar_control_center_handle_network_setup( - GtkListItemFactory* factory, - GtkListItem* list_item, - gpointer userdata ) -{ - (void)factory; - FoobarControlCenter* self = (FoobarControlCenter*)userdata; - - GtkWidget* item = foobar_control_details_item_new( ); - gtk_widget_set_margin_start( item, self->padding ); - gtk_widget_set_margin_end( item, self->padding ); - gtk_list_item_set_child( list_item, item ); - - { - GtkExpression* item_expr = gtk_property_expression_new( GTK_TYPE_LIST_ITEM, NULL, "item" ); - GtkExpression* label_expr = gtk_property_expression_new( FOOBAR_TYPE_NETWORK, item_expr, "name" ); - gtk_expression_bind( label_expr, item, "label", list_item ); - } - - { - GtkExpression* item_expr = gtk_property_expression_new( GTK_TYPE_LIST_ITEM, NULL, "item" ); - GtkExpression* checked_expr = gtk_property_expression_new( FOOBAR_TYPE_NETWORK, item_expr, "is-active" ); - gtk_expression_bind( checked_expr, item, "is-checked", list_item ); - } -} - -// -// Called by the bluetooth details list view to create a widget for displaying a device. -// -void foobar_control_center_handle_bluetooth_device_setup( - GtkListItemFactory* factory, - GtkListItem* list_item, - gpointer userdata ) -{ - (void)factory; - FoobarControlCenter* self = (FoobarControlCenter*)userdata; - - GtkWidget* item = foobar_control_details_item_new( ); - gtk_widget_set_margin_start( item, self->padding ); - gtk_widget_set_margin_end( item, self->padding ); - gtk_list_item_set_child( list_item, item ); - - { - GtkExpression* item_expr = gtk_property_expression_new( GTK_TYPE_LIST_ITEM, NULL, "item" ); - GtkExpression* label_expr = gtk_property_expression_new( FOOBAR_TYPE_BLUETOOTH_DEVICE, item_expr, "name" ); - gtk_expression_bind( label_expr, item, "label", list_item ); - } - - { - GtkExpression* item_expr = gtk_property_expression_new( GTK_TYPE_LIST_ITEM, NULL, "item" ); - GtkExpression* state_expr = gtk_property_expression_new( FOOBAR_TYPE_BLUETOOTH_DEVICE, item_expr, "state" ); - GtkExpression* accessory_params[] = { state_expr }; - GtkExpression* accessory_expr = gtk_cclosure_expression_new( - FOOBAR_TYPE_CONTROL_DETAILS_ACCESSORY, - NULL, - G_N_ELEMENTS( accessory_params ), - accessory_params, - G_CALLBACK( foobar_control_center_compute_bluetooth_device_accessory ), - NULL, - NULL ); - gtk_expression_bind( accessory_expr, item, "accessory", list_item ); - } -} - -// -// Called by the bluetooth details list view when a device was selected. -// -void foobar_control_center_handle_bluetooth_device_activate( - GtkListView* view, - guint position, - gpointer userdata ) -{ - (void)view; - (void)userdata; - - GListModel* device_list = G_LIST_MODEL( gtk_list_view_get_model( view ) ); - FoobarBluetoothDevice* device = g_list_model_get_item( device_list, position ); - foobar_bluetooth_device_toggle_connection( device ); -} - -// -// Called by the audio details list view to create a widget for displaying a device. -// -void foobar_control_center_handle_audio_device_setup( - GtkListItemFactory* factory, - GtkListItem* list_item, - gpointer userdata ) -{ - (void)factory; - FoobarControlCenter* self = (FoobarControlCenter*)userdata; - - GtkWidget* item = foobar_control_details_item_new( ); - gtk_widget_set_margin_start( item, self->padding ); - gtk_widget_set_margin_end( item, self->padding ); - gtk_list_item_set_child( list_item, item ); - - { - GtkExpression* item_expr = gtk_property_expression_new( GTK_TYPE_LIST_ITEM, NULL, "item" ); - GtkExpression* label_expr = gtk_property_expression_new( FOOBAR_TYPE_AUDIO_DEVICE, item_expr, "description" ); - gtk_expression_bind( label_expr, item, "label", list_item ); - } - - { - GtkExpression* item_expr = gtk_property_expression_new( GTK_TYPE_LIST_ITEM, NULL, "item" ); - GtkExpression* checked_expr = gtk_property_expression_new( FOOBAR_TYPE_AUDIO_DEVICE, item_expr, "is-default" ); - gtk_expression_bind( checked_expr, item, "is-checked", list_item ); - } -} - -// -// Called by the audio details list view when a device was selected. -// -void foobar_control_center_handle_audio_device_activate( - GtkListView* view, - guint position, - gpointer userdata ) -{ - (void)view; - (void)userdata; - - GListModel* device_list = G_LIST_MODEL( gtk_list_view_get_model( view ) ); - FoobarAudioDevice* device = g_list_model_get_item( device_list, position ); - foobar_audio_device_make_default( device ); -} - -// -// Signal handler called when the global configuration file has changed. -// -void foobar_control_center_handle_config_change( - GObject* object, - GParamSpec* pspec, - gpointer userdata ) -{ - (void)object; - (void)pspec; - FoobarControlCenter* self = (FoobarControlCenter*)userdata; - - FoobarConfiguration const* config = foobar_configuration_service_get_current( self->configuration_service ); - foobar_control_center_apply_configuration( - self, - foobar_configuration_get_control_center( config ), - foobar_configuration_get_notifications( config ) ); -} - -// --------------------------------------------------------------------------------------------------------------------- -// Value Converters -// --------------------------------------------------------------------------------------------------------------------- - -// -// Derive the item accessory from a bluetooth device's connection state. -// -FoobarControlDetailsAccessory foobar_control_center_compute_bluetooth_device_accessory( - GtkExpression* expression, - FoobarBluetoothDeviceState state, - gpointer userdata ) -{ - (void)expression; - (void)userdata; - - switch ( state ) - { - case FOOBAR_BLUETOOTH_DEVICE_STATE_DISCONNECTED: - return FOOBAR_CONTROL_DETAILS_ACCESSORY_NONE; - case FOOBAR_BLUETOOTH_DEVICE_STATE_CONNECTING: - return FOOBAR_CONTROL_DETAILS_ACCESSORY_PROGRESS; - case FOOBAR_BLUETOOTH_DEVICE_STATE_CONNECTED: - return FOOBAR_CONTROL_DETAILS_ACCESSORY_CHECKED; - default: - g_warn_if_reached( ); - return FOOBAR_CONTROL_DETAILS_ACCESSORY_NONE; - } -} - -// -// Derive the visible child in the "notifications" stack from the number of notifications. -// -// If there are no notifications, a placeholder is shown. -// -gchar* foobar_control_center_compute_visible_notification_child_name( - GtkExpression* expression, - guint count, - gpointer userdata ) -{ - (void)expression; - (void)userdata; - - return count > 0 ? g_strdup( STACK_ITEM_LIST ) : g_strdup( STACK_ITEM_PLACEHOLDER ); -} |
