Compare commits

...

8 Commits

Author SHA1 Message Date
Matthias Clasen
35b0974000 Add GtkFileDialog
Add an async API for picking a file, together with
a builder object for it. This is meant to replace direct
use of GtkFileChooserDialog.
2022-10-26 07:59:39 -04:00
Matthias Clasen
4c18b1f345 Add GtkChoice
This is an auxiliary object for use with
file chooser choices.
2022-10-26 07:54:23 -04:00
Matthias Clasen
48aa551a72 Add GtkInfoDialog
This is replacing GtkMessageDialog with an
async API for showing informational messages.
2022-10-26 07:54:23 -04:00
Matthias Clasen
c20749e2d8 wip: builder: Accept string lists for strvs
Accept
  <property name="my-strv>
    <object class="GtkStringList">
      <items>
        <item translatable="yes">One</item>
        <item translatable="yes">Two</item>
        ...
      </items>
    </object>
  </property>

Ideally, we would want to parse this without the
<object> tag, but that is much more annoying to
implement.
2022-10-26 07:54:23 -04:00
Matthias Clasen
3e8a5bbb92 Add GtkFontDialogButton
This is like GtkColorDialogButton, but for fonts.
2022-10-26 07:54:23 -04:00
Matthias Clasen
056022a54b Add GtkFontDialog
This is similar to GtkColorDialog, but for fonts.
2022-10-26 07:54:23 -04:00
Matthias Clasen
1765bdc590 Add GtkColorDialogButton
This is a new color button implementation
built around GtkColorDialog.
2022-10-26 07:54:23 -04:00
Matthias Clasen
bcd9130548 Add GtkColorDialog
Add an async API for picking a color, together with
a builder object for it. This is meant to replace direct
use of GtkColorChooserDialog.
2022-10-26 07:54:22 -04:00
18 changed files with 4757 additions and 4 deletions

View File

@@ -78,16 +78,20 @@
#include <gtk/gtkcenterbox.h>
#include <gtk/gtkcenterlayout.h>
#include <gtk/gtkcheckbutton.h>
#include <gtk/gtkchoice.h>
#include <gtk/gtkcolorbutton.h>
#include <gtk/gtkcolorchooser.h>
#include <gtk/gtkcolorchooserdialog.h>
#include <gtk/gtkcolorchooserwidget.h>
#include <gtk/gtkcolordialog.h>
#include <gtk/gtkcolordialogbutton.h>
#include <gtk/gtkcolorutils.h>
#include <gtk/gtkcolumnview.h>
#include <gtk/gtkcolumnviewcolumn.h>
#include <gtk/gtkcolumnviewsorter.h>
#include <gtk/deprecated/gtkcombobox.h>
#include <gtk/deprecated/gtkcomboboxtext.h>
#include <gtk/gtkchoice.h>
#include <gtk/gtkconstraintlayout.h>
#include <gtk/gtkconstraint.h>
#include <gtk/gtkcssprovider.h>
@@ -124,6 +128,7 @@
#include <gtk/gtkfilechooserdialog.h>
#include <gtk/gtkfilechoosernative.h>
#include <gtk/gtkfilechooserwidget.h>
#include <gtk/gtkfiledialog.h>
#include <gtk/gtkfilefilter.h>
#include <gtk/gtkfilter.h>
#include <gtk/gtkfilterlistmodel.h>
@@ -134,6 +139,8 @@
#include <gtk/gtkfontchooser.h>
#include <gtk/gtkfontchooserdialog.h>
#include <gtk/gtkfontchooserwidget.h>
#include <gtk/gtkfontdialog.h>
#include <gtk/gtkfontdialogbutton.h>
#include <gtk/gtkframe.h>
#include <gtk/gtkgesture.h>
#include <gtk/gtkgestureclick.h>
@@ -157,6 +164,7 @@
#include <gtk/gtkimcontextsimple.h>
#include <gtk/gtkimmulticontext.h>
#include <gtk/gtkinfobar.h>
#include <gtk/gtkinfodialog.h>
#include <gtk/gtkinscription.h>
#include <gtk/gtklabel.h>
#include <gtk/gtklayoutmanager.h>

View File

@@ -225,6 +225,7 @@
#include "gtkicontheme.h"
#include "gtkiconthemeprivate.h"
#include "gtkdebug.h"
#include "gtkstringlist.h"
static void gtk_builder_finalize (GObject *object);
@@ -551,6 +552,19 @@ gtk_builder_get_parameters (GtkBuilder *builder,
if (G_PARAM_SPEC_VALUE_TYPE (prop->pspec) == GTK_TYPE_EXPRESSION)
gtk_value_set_expression (&property_value, prop->value);
else if (G_PARAM_SPEC_VALUE_TYPE (prop->pspec) == G_TYPE_STRV)
{
GStrvBuilder *strv = g_strv_builder_new ();
for (guint j = 0; j < g_list_model_get_n_items (G_LIST_MODEL (prop->value)); j++)
{
GtkStringObject *s = g_list_model_get_item (G_LIST_MODEL (prop->value), j);
g_strv_builder_add (strv, gtk_string_object_get_string (s));
g_object_unref (s);
}
g_value_set_boxed (&property_value, g_strv_builder_end (strv));
}
else
g_assert_not_reached ();
}

View File

@@ -1039,6 +1039,10 @@ free_property_info (PropertyInfo *info)
{
if (G_PARAM_SPEC_VALUE_TYPE (info->pspec) == GTK_TYPE_EXPRESSION)
gtk_expression_unref (info->value);
else if (G_PARAM_SPEC_VALUE_TYPE (info->pspec) == G_TYPE_STRV)
{
/* info->value is in the hash table of objects */
}
else
g_assert_not_reached();
}
@@ -1723,8 +1727,11 @@ parse_custom (GtkBuildableParseContext *context,
object = ((ObjectInfo*)child_info->parent)->object;
child = child_info->object;
}
else
return FALSE;
else if (parent_info->tag_type == TAG_PROPERTY)
{
g_print ("custom markup in <property>\n");
return FALSE;
}
if (!gtk_buildable_custom_tag_start (GTK_BUILDABLE (object),
data->builder,
@@ -1947,7 +1954,10 @@ end_element (GtkBuildableParseContext *context,
if (child_info)
child_info->object = object_info->object;
if (prop_info)
g_string_assign (prop_info->text, object_info->id);
{
g_string_assign (prop_info->text, object_info->id);
prop_info->value = object_info->object;
}
if (GTK_IS_BUILDABLE (object_info->object) &&
GTK_BUILDABLE_GET_IFACE (object_info->object)->parser_finished)
@@ -2074,7 +2084,7 @@ end_element (GtkBuildableParseContext *context,
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_UNHANDLED_TAG,
"Unhandled tag: <%s>", element_name);
"Unhandled end tag: </%s>", element_name);
_gtk_builder_prefix_error (data->builder, context, error);
}
}

93
gtk/gtkchoice.c Normal file
View File

@@ -0,0 +1,93 @@
/*
* GTK - The GIMP Toolkit
* Copyright (C) 2022 Red Hat, Inc.
* All rights reserved.
*
* This Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtkchoice.h"
struct _GtkChoice
{
GObject parent_instance;
char *label;
char **options;
};
G_DEFINE_TYPE (GtkChoice, gtk_choice, G_TYPE_OBJECT)
static void
gtk_choice_init (GtkChoice *self)
{
}
static void
gtk_choice_finalize (GObject *object)
{
GtkChoice *self = GTK_CHOICE (object);
g_free (self->label);
g_strfreev (self->options);
G_OBJECT_CLASS (gtk_choice_parent_class)->finalize (object);
}
static void
gtk_choice_class_init (GtkChoiceClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = gtk_choice_finalize;
}
/* }}} */
/* {{{ Public API */
GtkChoice *
gtk_choice_new (const char *label,
const char * const *options)
{
GtkChoice *self;
self = g_object_new (GTK_TYPE_CHOICE, NULL);
self->label = g_strdup (label);
self->options = g_strdupv ((char **)options);
return self;
}
const char *
gtk_choice_get_label (GtkChoice *choice)
{
g_return_val_if_fail (GTK_IS_CHOICE (choice), NULL);
return choice->label;
}
const char * const *
gtk_choice_get_options (GtkChoice *choice)
{
g_return_val_if_fail (GTK_IS_CHOICE (choice), NULL);
return (const char * const *)choice->options;
}
/* }}} */
/* vim:set foldmethod=marker expandtab: */

45
gtk/gtkchoice.h Normal file
View File

@@ -0,0 +1,45 @@
/* GTK - The GIMP Toolkit
*
* Copyright (C) 2022 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gdk/gdk.h>
G_BEGIN_DECLS
#define GTK_TYPE_CHOICE (gtk_choice_get_type ())
GDK_AVAILABLE_IN_4_10
G_DECLARE_FINAL_TYPE (GtkChoice, gtk_choice, GTK, CHOICE, GObject)
GDK_AVAILABLE_IN_4_10
GtkChoice * gtk_choice_new (const char *label,
const char * const *options);
GDK_AVAILABLE_IN_4_10
const char * gtk_choice_get_label (GtkChoice *choice);
GDK_AVAILABLE_IN_4_10
const char * const *
gtk_choice_get_options (GtkChoice *choice);
G_END_DECLS

475
gtk/gtkcolordialog.c Normal file
View File

@@ -0,0 +1,475 @@
/*
* GTK - The GIMP Toolkit
* Copyright (C) 2022 Red Hat, Inc.
* All rights reserved.
*
* This Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtkcolordialog.h"
#include "gtkcolorchooserdialog.h"
#include "gtkcolorchooser.h"
#include "gtkbutton.h"
#include <glib/gi18n-lib.h>
/**
* GtkColorDialog:
*
* A `GtkColorDialog` object collects the arguments that
* are needed to present a color chooser dialog to the
* user, such as a title for the dialog and whether it
* should be modal.
*
* The dialog is shown with the [function@Gtk.ColorDialog.choose_rgba]
* function. This API follows the GIO async pattern, and the
* result can be obtained by calling
* [function@Gtk.ColorDialog.choose_rgba_finish].
*
* See [class@Gtk.ColorDialogButton] for a convenient control
* that uses `GtkColorDialog` and presents the results.
*
* `GtkColorDialog was added in GTK 4.10.
*/
/* {{{ GObject implementation */
struct _GtkColorDialog
{
GObject parent_instance;
char *title;
unsigned int modal : 1;
unsigned int with_alpha : 1;
};
enum
{
PROP_TITLE = 1,
PROP_MODAL,
PROP_WITH_ALPHA,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES];
G_DEFINE_TYPE (GtkColorDialog, gtk_color_dialog, G_TYPE_OBJECT)
static void
gtk_color_dialog_init (GtkColorDialog *self)
{
self->title = g_strdup (_("Pick a Color"));
self->modal = TRUE;
self->with_alpha = TRUE;
}
static void
gtk_color_dialog_finalize (GObject *object)
{
GtkColorDialog *self = GTK_COLOR_DIALOG (object);
g_free (self->title);
G_OBJECT_CLASS (gtk_color_dialog_parent_class)->finalize (object);
}
static void
gtk_color_dialog_get_property (GObject *object,
unsigned int property_id,
GValue *value,
GParamSpec *pspec)
{
GtkColorDialog *self = GTK_COLOR_DIALOG (object);
switch (property_id)
{
case PROP_TITLE:
g_value_set_string (value, self->title);
break;
case PROP_MODAL:
g_value_set_boolean (value, self->modal);
break;
case PROP_WITH_ALPHA:
g_value_set_boolean (value, self->with_alpha);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_color_dialog_set_property (GObject *object,
unsigned int prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkColorDialog *self = GTK_COLOR_DIALOG (object);
switch (prop_id)
{
case PROP_TITLE:
gtk_color_dialog_set_title (self, g_value_get_string (value));
break;
case PROP_MODAL:
gtk_color_dialog_set_modal (self, g_value_get_boolean (value));
break;
case PROP_WITH_ALPHA:
gtk_color_dialog_set_with_alpha (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_color_dialog_class_init (GtkColorDialogClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = gtk_color_dialog_finalize;
object_class->get_property = gtk_color_dialog_get_property;
object_class->set_property = gtk_color_dialog_set_property;
/**
* GtkColorDialog:title: (attributes org.gtk.Property.get=gtk_color_dialog_get_title org.gtk.Property.set=gtk_color_dialog_set_title)
*
* A title that may be shown on the color chooser
* dialog that is presented by [function@Gtk.ColorDialog.choose_rgba].
*
* Since: 4.10
*/
properties[PROP_TITLE] =
g_param_spec_string ("title", NULL, NULL,
NULL,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkColorDialog:modal: (attributes org.gtk.Property.get=gtk_color_dialog_get_modal org.gtk.Property.set=gtk_color_dialog_set_modal)
*
* Whether the color chooser dialog is modal.
*
* Since: 4.10
*/
properties[PROP_MODAL] =
g_param_spec_boolean ("modal", NULL, NULL,
TRUE,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkColorDialog:with-alpha: (attributes org.gtk.Property.get=gtk_color_dialog_get_with_alpha org.gtk.Property.set=gtk_color_dialog_set_with_alpha)
*
* Whether colors may have alpha (translucency).
*
* When with-alpha is %FALSE, the color that is selected
* will be forced to have alpha == 1.
*
* Since: 4.10
*/
properties[PROP_WITH_ALPHA] =
g_param_spec_boolean ("with-alpha", NULL, NULL,
TRUE,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
}
/* }}} */
/* {{{ Constructor */
/**
* gtk_color_dialog_new:
*
* Creates a new `GtkColorDialog` object.
*
* Returns: the new `GtkColorDialog`
*
* Since: 4.10
*/
GtkColorDialog *
gtk_color_dialog_new (void)
{
return g_object_new (GTK_TYPE_COLOR_DIALOG, NULL);
}
/* }}} */
/* {{{ Getters and setters */
/**
* gtk_color_dialog_get_title:
* @self: a `GtkColorDialog`
*
* Returns the title that will be shown on the
* color chooser dialog.
*
* Returns: the title
*
* Since: 4.10
*/
const char *
gtk_color_dialog_get_title (GtkColorDialog *self)
{
g_return_val_if_fail (GTK_IS_COLOR_DIALOG (self), NULL);
return self->title;
}
/**
* gtk_color_dialog_set_title:
* @self: a `GtkColorDialog`
* @title: the new title
*
* Sets the title that will be shown on the
* color chooser dialog.
*
* Since: 4.10
*/
void
gtk_color_dialog_set_title (GtkColorDialog *self,
const char *title)
{
char *new_title;
g_return_if_fail (GTK_IS_COLOR_DIALOG (self));
g_return_if_fail (title != NULL);
if (g_str_equal (self->title, title))
return;
new_title = g_strdup (title);
g_free (self->title);
self->title = new_title;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TITLE]);
}
/**
* gtk_color_dialog_get_modal:
* @self: a `GtkColorDialog`
*
* Returns whether the color chooser dialog
* blocks interaction with the parent window
* while it is presented.
*
* Returns: `TRUE` if the color chooser dialog is modal
*
* Since: 4.10
*/
gboolean
gtk_color_dialog_get_modal (GtkColorDialog *self)
{
g_return_val_if_fail (GTK_IS_COLOR_DIALOG (self), TRUE);
return self->modal;
}
/**
* gtk_color_dialog_set_modal:
* @self: a `GtkColorDialog`
* @modal: the new value
*
* Sets whether the color chooser dialog
* blocks interaction with the parent window
* while it is presented.
*
* Since: 4.10
*/
void
gtk_color_dialog_set_modal (GtkColorDialog *self,
gboolean modal)
{
g_return_if_fail (GTK_IS_COLOR_DIALOG (self));
if (self->modal == modal)
return;
self->modal = modal;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODAL]);
}
/**
* gtk_color_dialog_get_with_alpha:
* @self: a `GtkColorDialog`
*
* Returns whether colors may have alpha.
*
* Returns: `TRUE` if colors may have alpha
*
* Since: 4.10
*/
gboolean
gtk_color_dialog_get_with_alpha (GtkColorDialog *self)
{
g_return_val_if_fail (GTK_IS_COLOR_DIALOG (self), TRUE);
return self->with_alpha;
}
/**
* gtk_color_dialog_set_with_alpha:
* @self: a `GtkColorDialog`
* @with_alpha: the new value
*
* Sets whether colors may have alpha.
*
* Since: 4.10
*/
void
gtk_color_dialog_set_with_alpha (GtkColorDialog *self,
gboolean with_alpha)
{
g_return_if_fail (GTK_IS_COLOR_DIALOG (self));
if (self->with_alpha == with_alpha)
return;
self->with_alpha = with_alpha;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_WITH_ALPHA]);
}
/* }}} */
/* {{{ Async API */
static void response_cb (GTask *task,
int response);
static void
cancelled_cb (GCancellable *cancellable,
GTask *task)
{
response_cb (task, GTK_RESPONSE_CANCEL);
}
static void
response_cb (GTask *task,
int response)
{
GCancellable *cancellable;
cancellable = g_task_get_cancellable (task);
if (cancellable)
g_signal_handlers_disconnect_by_func (cancellable, cancelled_cb, task);
if (response == GTK_RESPONSE_OK)
{
GtkColorChooserDialog *window;
GdkRGBA color;
window = GTK_COLOR_CHOOSER_DIALOG (g_task_get_task_data (task));
gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (window), &color);
g_task_return_pointer (task, &color, NULL);
}
else
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled");
g_object_unref (task);
}
/**
* gtk_color_dialog_choose_rgba:
* @self: a `GtkColorDialog`
* @parent: (nullable): the parent `GtkWindow`
* @initial_color: (nullable): the color to select initially
* @cancellable: (nullable): a `GCancellable` to cancel the operation
* @callback: (scope async): a callback to call when the operation is complete
* @user_data: (closure callback): data to pass to @callback
*
* This function initiates a color choice operation by
* presenting a color chooser dialog to the user.
*
* The @callback will be called when the dialog is dismissed.
* It should call [function@Gtk.ColorDialog.choose_rgba_finish]
* to obtain the result.
*
* Since: 4.10
*/
void
gtk_color_dialog_choose_rgba (GtkColorDialog *self,
GtkWindow *parent,
const GdkRGBA *initial_color,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GtkWidget *window;
GTask *task;
g_return_if_fail (GTK_IS_COLOR_DIALOG (self));
window = gtk_color_chooser_dialog_new (self->title, parent);
if (initial_color)
gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (window), initial_color);
gtk_color_chooser_set_use_alpha (GTK_COLOR_CHOOSER (window), self->with_alpha);
gtk_window_set_modal (GTK_WINDOW (window), self->modal);
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, gtk_color_dialog_choose_rgba);
g_task_set_task_data (task, window, (GDestroyNotify) gtk_window_destroy);
if (cancellable)
g_signal_connect (cancellable, "cancelled", G_CALLBACK (cancelled_cb), task);
g_signal_connect_swapped (window, "response", G_CALLBACK (response_cb), task);
gtk_window_present (GTK_WINDOW (window));
}
/**
* gtk_color_dialog_choose_rgba_finish:
* @self: a `GtkColorDialog`
* @result: a `GAsyncResult`
* @color: (out caller-allocates): return location for the color
* @error: return location for an error
*
* Finishes the [function@Gtk.ColorDialog.choose_rgba] call and
* returns the resulting color.
*
* Returns: `TRUE` if a color was selected. Otherwise,
* `FALSE` is returned and @error is set
*
* Since: 4.10
*/
gboolean
gtk_color_dialog_choose_rgba_finish (GtkColorDialog *self,
GAsyncResult *result,
GdkRGBA *color,
GError **error)
{
GdkRGBA *ret;
ret = g_task_propagate_pointer (G_TASK (result), error);
if (ret)
{
*color = *ret;
return TRUE;
}
return FALSE;
}
/* }}} */
/* vim:set foldmethod=marker expandtab: */

73
gtk/gtkcolordialog.h Normal file
View File

@@ -0,0 +1,73 @@
/* GTK - The GIMP Toolkit
*
* Copyright (C) 2022 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gdk/gdk.h>
#include <gtk/gtkwindow.h>
G_BEGIN_DECLS
#define GTK_TYPE_COLOR_DIALOG (gtk_color_dialog_get_type ())
GDK_AVAILABLE_IN_4_10
G_DECLARE_FINAL_TYPE (GtkColorDialog, gtk_color_dialog, GTK, COLOR_DIALOG, GObject)
GDK_AVAILABLE_IN_4_10
GtkColorDialog *gtk_color_dialog_new (void);
GDK_AVAILABLE_IN_4_10
const char * gtk_color_dialog_get_title (GtkColorDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_color_dialog_set_title (GtkColorDialog *self,
const char *title);
GDK_AVAILABLE_IN_4_10
gboolean gtk_color_dialog_get_modal (GtkColorDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_color_dialog_set_modal (GtkColorDialog *self,
gboolean modal);
GDK_AVAILABLE_IN_4_10
gboolean gtk_color_dialog_get_with_alpha (GtkColorDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_color_dialog_set_with_alpha (GtkColorDialog *self,
gboolean with_alpha);
GDK_AVAILABLE_IN_4_10
void gtk_color_dialog_choose_rgba (GtkColorDialog *self,
GtkWindow *parent,
const GdkRGBA *initial_color,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GDK_AVAILABLE_IN_4_10
gboolean gtk_color_dialog_choose_rgba_finish (GtkColorDialog *self,
GAsyncResult *result,
GdkRGBA *color,
GError **error);
G_END_DECLS

483
gtk/gtkcolordialogbutton.c Normal file
View File

@@ -0,0 +1,483 @@
/*
* GTK - The GIMP Toolkit
* Copyright (C) 2022 Red Hat, Inc.
* All rights reserved.
*
* This Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtkcolordialogbutton.h"
#include "gtkbinlayout.h"
#include "gtkbutton.h"
#include "gtkcolorswatchprivate.h"
#include "gtkdragsource.h"
#include "gtkdroptarget.h"
#include <glib/gi18n-lib.h>
#include "gtkmain.h"
#include "gtkmarshalers.h"
#include "gtkprivate.h"
#include "gtksnapshot.h"
#include "gtkwidgetprivate.h"
static gboolean drop (GtkDropTarget *dest,
const GValue *value,
double x,
double y,
GtkColorDialogButton *self);
static GdkContentProvider *
drag_prepare (GtkDragSource *source,
double x,
double y,
GtkColorDialogButton *self);
static void button_clicked (GtkColorDialogButton *self);
/**
* GtkColorDialogButton:
*
* The `GtkColorDialogButton` is a wrapped around a [class@Gtk.ColorDialog]
* and allows to open a color chooser dialog to change the color.
*
* ![An example GtkColorDialogButton](color-button.png)
*
* It is suitable widget for selecting a color in a preference dialog.
*
* # CSS nodes
*
* ```
* colorbutton
* ╰── button.color
* ╰── [content]
* ```
*
* `GtkColorDialogButton` has a single CSS node with name colorbutton which
* contains a button node. To differentiate it from a plain `GtkButton`,
* it gets the .color style class.
*/
/* {{{ GObject implementation */
struct _GtkColorDialogButton
{
GtkWidget parent_instance;
GtkWidget *button;
GtkWidget *swatch;
GtkColorDialog *dialog;
GdkRGBA color;
};
/* Properties */
enum
{
PROP_DIALOG = 1,
PROP_COLOR,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES];
G_DEFINE_TYPE (GtkColorDialogButton, gtk_color_dialog_button, GTK_TYPE_WIDGET)
static void
gtk_color_dialog_button_init (GtkColorDialogButton *self)
{
PangoLayout *layout;
PangoRectangle rect;
GtkDragSource *source;
GtkDropTarget *dest;
self->button = gtk_button_new ();
g_signal_connect_swapped (self->button, "clicked", G_CALLBACK (button_clicked), self);
gtk_widget_set_parent (self->button, GTK_WIDGET (self));
self->swatch = g_object_new (GTK_TYPE_COLOR_SWATCH,
"accessible-role", GTK_ACCESSIBLE_ROLE_IMG,
"selectable", FALSE,
"has-menu", FALSE,
"can-drag", FALSE,
NULL);
gtk_widget_set_can_focus (self->swatch, FALSE);
gtk_widget_remove_css_class (self->swatch, "activatable");
layout = gtk_widget_create_pango_layout (GTK_WIDGET (self), "Black");
pango_layout_get_pixel_extents (layout, NULL, &rect);
g_object_unref (layout);
gtk_widget_set_size_request (self->swatch, rect.width, rect.height);
gtk_button_set_child (GTK_BUTTON (self->button), self->swatch);
dest = gtk_drop_target_new (GDK_TYPE_RGBA, GDK_ACTION_COPY);
g_signal_connect (dest, "drop", G_CALLBACK (drop), self);
gtk_widget_add_controller (GTK_WIDGET (self->button), GTK_EVENT_CONTROLLER (dest));
source = gtk_drag_source_new ();
g_signal_connect (source, "prepare", G_CALLBACK (drag_prepare), self);
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (source),
GTK_PHASE_CAPTURE);
gtk_widget_add_controller (self->button, GTK_EVENT_CONTROLLER (source));
gtk_widget_add_css_class (self->button, "color");
}
static void
gtk_color_dialog_button_set_property (GObject *object,
unsigned int param_id,
const GValue *value,
GParamSpec *pspec)
{
GtkColorDialogButton *self = GTK_COLOR_DIALOG_BUTTON (object);
switch (param_id)
{
case PROP_DIALOG:
gtk_color_dialog_button_set_dialog (self, g_value_get_object (value));
break;
case PROP_COLOR:
gtk_color_dialog_button_set_color (self, g_value_get_boxed (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
}
}
static void
gtk_color_dialog_button_get_property (GObject *object,
unsigned int param_id,
GValue *value,
GParamSpec *pspec)
{
GtkColorDialogButton *self = GTK_COLOR_DIALOG_BUTTON (object);
switch (param_id)
{
case PROP_DIALOG:
g_value_set_object (value, self->dialog);
break;
case PROP_COLOR:
g_value_set_boxed (value, &self->color);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
}
}
static void
gtk_color_dialog_button_dispose (GObject *object)
{
GtkColorDialogButton *self = GTK_COLOR_DIALOG_BUTTON (object);
g_clear_pointer (&self->button, gtk_widget_unparent);
G_OBJECT_CLASS (gtk_color_dialog_button_parent_class)->dispose (object);
}
static void
gtk_color_dialog_button_finalize (GObject *object)
{
GtkColorDialogButton *self = GTK_COLOR_DIALOG_BUTTON (object);
g_clear_object (&self->dialog);
G_OBJECT_CLASS (gtk_color_dialog_button_parent_class)->finalize (object);
}
static void
gtk_color_dialog_button_class_init (GtkColorDialogButtonClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
object_class->get_property = gtk_color_dialog_button_get_property;
object_class->set_property = gtk_color_dialog_button_set_property;
object_class->dispose = gtk_color_dialog_button_dispose;
object_class->finalize = gtk_color_dialog_button_finalize;
widget_class->grab_focus = gtk_widget_grab_focus_child;
widget_class->focus = gtk_widget_focus_child;
/**
* GtkColorDialogButton:dialog: (attributes org.gtk.Property.get=gtk_color_dialog_button_get_dialog org.gtk.Property.set=gtk_color_dialog_button_set_dialog)
*
* The `GtkColorDialog` that contains parameters for
* the color chooser dialog.
*
* Since: 4.10
*/
properties[PROP_DIALOG] =
g_param_spec_object ("dialog", NULL, NULL,
GTK_TYPE_COLOR_DIALOG,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkColorDialogButton:color: (attributes org.gtk.Property.get=gtk_color_dialog_button_get_color org.gtk.Property.set=gtk_color_dialog_button_set_color)
*
* The selected color.
*
* This property can be set to give the button its initial
* color, and it will be updated to reflect the users choice
* in the color chooser dialog.
*
* Listen to `notify::color` to get informed about changes
* to the buttons color.
*
* Since: 4.10
*/
properties[PROP_COLOR] =
g_param_spec_boxed ("color", NULL, NULL,
GDK_TYPE_RGBA,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
gtk_widget_class_set_css_name (widget_class, "colorbutton");
}
/* }}} */
/* {{{ Private API, callbacks */
static guint
scale_round (double value,
double scale)
{
value = floor (value * scale + 0.5);
value = CLAMP (value, 0, scale);
return (guint)value;
}
static char *
accessible_color_name (const GdkRGBA *color)
{
if (color->alpha < 1.0)
return g_strdup_printf (_("Red %d%%, Green %d%%, Blue %d%%, Alpha %d%%"),
scale_round (color->red, 100),
scale_round (color->green, 100),
scale_round (color->blue, 100),
scale_round (color->alpha, 100));
else
return g_strdup_printf (_("Red %d%%, Green %d%%, Blue %d%%"),
scale_round (color->red, 100),
scale_round (color->green, 100),
scale_round (color->blue, 100));
}
static gboolean
drop (GtkDropTarget *dest,
const GValue *value,
double x,
double y,
GtkColorDialogButton *self)
{
GdkRGBA *color = g_value_get_boxed (value);
gtk_color_dialog_button_set_color (self, color);
return TRUE;
}
static GdkContentProvider *
drag_prepare (GtkDragSource *source,
double x,
double y,
GtkColorDialogButton *self)
{
GdkRGBA color;
gtk_color_swatch_get_rgba (GTK_COLOR_SWATCH (self->swatch), &color);
return gdk_content_provider_new_typed (GDK_TYPE_RGBA, &color);
}
static void
color_chosen (GObject *source,
GAsyncResult *result,
gpointer data)
{
GtkColorDialogButton *self = data;
GdkRGBA color;
GError *error = NULL;
if (gtk_color_dialog_choose_rgba_finish (self->dialog, result, &color, &error))
{
gtk_color_dialog_button_set_color (self, &color);
}
else
{
g_print ("%s\n", error->message);
g_error_free (error);
}
}
static void
button_clicked (GtkColorDialogButton *self)
{
GtkRoot *root = gtk_widget_get_root (GTK_WIDGET (self));
GtkWindow *parent = NULL;
if (GTK_IS_WINDOW (root))
parent = GTK_WINDOW (root);
gtk_color_dialog_choose_rgba (self->dialog, parent, &self->color,
NULL, color_chosen, self);
}
/* }}} */
/* {{{ Constructor */
/**
* gtk_color_dialog_button_new:
* @dialog: (nullable) (transfer full): the `GtkColorDialog` to use
*
* Creates a new `GtkColorDialogButton` with the
* given `GtkColorDialog`.
*
* You can pass `NULL` to this function and set a `GtkColorDialog`
* later. The button will be insensitive until that happens.
*
* Returns: the new `GtkColorDialogButton`
*
* Since: 4.10
*/
GtkWidget *
gtk_color_dialog_button_new (GtkColorDialog *dialog)
{
GtkWidget *self;
g_return_val_if_fail (GTK_IS_COLOR_DIALOG (dialog), NULL);
self = g_object_new (GTK_TYPE_COLOR_DIALOG_BUTTON,
"dialog", dialog,
NULL);
g_clear_object (&dialog);
return self;
}
/* }}} */
/* {{{ Getters and setters */
/**
* gtk_color_dialog_button_get_dialog:
* @self: a `GtkColorDialogButton`
*
* Returns the `GtkColorDialog` of @self.
*
* Returns: (transfer none) (nullable): the `GtkColorDialog`
*
* Since: 4.10
*/
GtkColorDialog *
gtk_color_dialog_button_get_dialog (GtkColorDialogButton *self)
{
g_return_val_if_fail (GTK_IS_COLOR_DIALOG_BUTTON (self), NULL);
return self->dialog;
}
/**
* gtk_color_dialog_button_set_dialog:
* @self: a `GtkColorDialogButton`
* @dialog: the new `GtkColorDialog`
*
* Sets a `GtkColorDialog` object to use for
* creating the color chooser dialog that is
* presented when the user clicks the button.
*
* Since: 4.10
*/
void
gtk_color_dialog_button_set_dialog (GtkColorDialogButton *self,
GtkColorDialog *dialog)
{
g_return_if_fail (GTK_IS_COLOR_DIALOG_BUTTON (self));
g_return_if_fail (GTK_IS_COLOR_DIALOG (dialog));
if (!g_set_object (&self->dialog, dialog))
return;
gtk_widget_set_sensitive (self->button, dialog != NULL);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DIALOG]);
}
/**
* gtk_color_dialog_button_get_color:
* @self: a `GtkColorDialogButton`
*
* Returns the color of the button.
*
* This function is what should be used to obtain
* the color that was choosen by the user. To get
* informed about changes, listen to "notify::color".
*
* Returns: the color
*
* Since: 4.10
*/
const GdkRGBA *
gtk_color_dialog_button_get_color (GtkColorDialogButton *self)
{
g_return_val_if_fail (GTK_IS_COLOR_DIALOG_BUTTON (self), NULL);
return &self->color;
}
/**
* gtk_color_dialog_button_set_color:
* @self: a `GtkColorDialogButton`
* @color: the new color
*
* Sets the color of the button.
*
* Since: 4.10
*/
void
gtk_color_dialog_button_set_color (GtkColorDialogButton *self,
const GdkRGBA *color)
{
char *text;
g_return_if_fail (GTK_IS_COLOR_DIALOG_BUTTON (self));
g_return_if_fail (color != NULL);
if (gdk_rgba_equal (&self->color, color))
return;
self->color = *color;
gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (self->swatch), color);
text = accessible_color_name (color);
gtk_accessible_update_property (GTK_ACCESSIBLE (self->swatch),
GTK_ACCESSIBLE_PROPERTY_LABEL, text,
-1);
g_free (text);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_COLOR]);
}
/* }}} */
/* vim:set foldmethod=marker expandtab: */

View File

@@ -0,0 +1,53 @@
/*
* GTK - The GIMP Toolkit
* Copyright (C) 2022 Red Hat, Inc.
* All rights reserved.
*
* This Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtkbutton.h>
#include <gtk/gtkcolordialog.h>
G_BEGIN_DECLS
#define GTK_TYPE_COLOR_DIALOG_BUTTON (gtk_color_dialog_button_get_type ())
GDK_AVAILABLE_IN_4_10
G_DECLARE_FINAL_TYPE (GtkColorDialogButton, gtk_color_dialog_button, GTK, COLOR_DIALOG_BUTTON, GtkWidget)
GDK_AVAILABLE_IN_4_10
GtkWidget * gtk_color_dialog_button_new (GtkColorDialog *dialog);
GDK_AVAILABLE_IN_4_10
GtkColorDialog *gtk_color_dialog_button_get_dialog (GtkColorDialogButton *self);
GDK_AVAILABLE_IN_4_10
void gtk_color_dialog_button_set_dialog (GtkColorDialogButton *self,
GtkColorDialog *dialog);
GDK_AVAILABLE_IN_4_10
const GdkRGBA * gtk_color_dialog_button_get_color (GtkColorDialogButton *self);
GDK_AVAILABLE_IN_4_10
void gtk_color_dialog_button_set_color (GtkColorDialogButton *self,
const GdkRGBA *color);
G_END_DECLS

914
gtk/gtkfiledialog.c Normal file
View File

@@ -0,0 +1,914 @@
/*
* GTK - The GIMP Toolkit
* Copyright (C) 2022 Red Hat, Inc.
* All rights reserved.
*
* This Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtkfiledialog.h"
#include "gtk/gtkchoice.h"
#include "gtkfilechooserdialog.h"
#include <glib/gi18n-lib.h>
/**
* GtkFileDialog:
*
* A `GtkFileDialog` object collects the arguments that
* are needed to present a file chooser dialog to the
* user, such as a title for the dialog and whether it
* should be modal.
*
* The dialog is shown with the [function@Gtk.FileDialog.choose_rgba]
* function. This API follows the GIO async pattern, and the
* result can be obtained by calling
* [function@Gtk.FileDialog.choose_rgba_finish].
*
* See [class@Gtk.FileDialogButton] for a convenient control
* that uses `GtkFileDialog` and presents the results.
*
* `GtkFileDialog was added in GTK 4.10.
*/
/* {{{ GObject implementation */
struct _GtkFileDialog
{
GObject parent_instance;
char *title;
unsigned int modal : 1;
unsigned int select_multiple : 1;
unsigned int create_folders : 1;
GListModel *filters;
GListModel *shortcuts;
GListModel *choices;
};
enum
{
PROP_TITLE = 1,
PROP_MODAL,
PROP_SELECT_MULTIPLE,
PROP_CREATE_FOLDERS,
PROP_FILTERS,
PROP_SHORTCUTS,
PROP_CHOICES,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES];
G_DEFINE_TYPE (GtkFileDialog, gtk_file_dialog, G_TYPE_OBJECT)
static void
gtk_file_dialog_init (GtkFileDialog *self)
{
self->title = g_strdup (_("Pick a File"));
self->modal = TRUE;
self->create_folders = TRUE;
}
static void
gtk_file_dialog_finalize (GObject *object)
{
GtkFileDialog *self = GTK_FILE_DIALOG (object);
g_free (self->title);
g_clear_object (&self->filters);
g_clear_object (&self->shortcuts);
g_clear_object (&self->choices);
G_OBJECT_CLASS (gtk_file_dialog_parent_class)->finalize (object);
}
static void
gtk_file_dialog_get_property (GObject *object,
unsigned int property_id,
GValue *value,
GParamSpec *pspec)
{
GtkFileDialog *self = GTK_FILE_DIALOG (object);
switch (property_id)
{
case PROP_TITLE:
g_value_set_string (value, self->title);
break;
case PROP_MODAL:
g_value_set_boolean (value, self->modal);
break;
case PROP_SELECT_MULTIPLE:
g_value_set_boolean (value, self->select_multiple);
break;
case PROP_CREATE_FOLDERS:
g_value_set_boolean (value, self->create_folders);
break;
case PROP_FILTERS:
g_value_set_object (value, self->filters);
break;
case PROP_SHORTCUTS:
g_value_set_object (value, self->shortcuts);
break;
case PROP_CHOICES:
g_value_set_object (value, self->choices);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_file_dialog_set_property (GObject *object,
unsigned int prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkFileDialog *self = GTK_FILE_DIALOG (object);
switch (prop_id)
{
case PROP_TITLE:
gtk_file_dialog_set_title (self, g_value_get_string (value));
break;
case PROP_MODAL:
gtk_file_dialog_set_modal (self, g_value_get_boolean (value));
break;
case PROP_SELECT_MULTIPLE:
gtk_file_dialog_set_select_multiple (self, g_value_get_boolean (value));
break;
case PROP_CREATE_FOLDERS:
gtk_file_dialog_set_create_folders (self, g_value_get_boolean (value));
break;
case PROP_FILTERS:
gtk_file_dialog_set_filters (self, g_value_get_object (value));
break;
case PROP_SHORTCUTS:
gtk_file_dialog_set_shortcuts (self, g_value_get_object (value));
break;
case PROP_CHOICES:
gtk_file_dialog_set_choices (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_file_dialog_class_init (GtkFileDialogClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = gtk_file_dialog_finalize;
object_class->get_property = gtk_file_dialog_get_property;
object_class->set_property = gtk_file_dialog_set_property;
/**
* GtkFileDialog:title: (attributes org.gtk.Property.get=gtk_file_dialog_get_title org.gtk.Property.set=gtk_color_dialog_set_title)
*
* A title that may be shown on the file chooser
* dialog that is presented by [function@Gtk.FileDialog.choose_rgba].
*
* Since: 4.10
*/
properties[PROP_TITLE] =
g_param_spec_string ("title", NULL, NULL,
NULL,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFileDialog:modal: (attributes org.gtk.Property.get=gtk_file_dialog_get_modal org.gtk.Property.set=gtk_color_dialog_set_modal)
*
* Whether the file chooser dialog is modal.
*
* Since: 4.10
*/
properties[PROP_MODAL] =
g_param_spec_boolean ("modal", NULL, NULL,
TRUE,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFileDialog:select-multiple: (attributes org.gtk.Property.get=gtk_file_dialog_get_select_multiple org.gtk.Property.set=gtk_color_dialog_set_select_multiple)
*
* Whether the file chooser dialog allows to select more than one file.
*
* Since: 4.10
*/
properties[PROP_SELECT_MULTIPLE] =
g_param_spec_boolean ("select-multiple", NULL, NULL,
FALSE,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFileDialog:create-folders: (attributes org.gtk.Property.get=gtk_file_dialog_get_create_folders org.gtk.Property.set=gtk_color_dialog_set_create_folders)
*
* Whether the file chooser dialog will allow to create new folders.
*
* Since: 4.10
*/
properties[PROP_CREATE_FOLDERS] =
g_param_spec_boolean ("create-folders", NULL, NULL,
TRUE,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
properties[PROP_FILTERS] =
g_param_spec_object ("filters", NULL, NULL,
G_TYPE_LIST_MODEL,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
properties[PROP_SHORTCUTS] =
g_param_spec_object ("shortcuts", NULL, NULL,
G_TYPE_LIST_MODEL,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
properties[PROP_CHOICES] =
g_param_spec_object ("choices", NULL, NULL,
G_TYPE_LIST_MODEL,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
}
/* }}} */
/* {{{ Utilities */
static void
file_chooser_set_filters (GtkFileChooser *chooser,
GListModel *filters)
{
if (!filters)
return;
for (unsigned int i = 0; i < g_list_model_get_n_items (filters); i++)
{
GtkFileFilter *filter = g_list_model_get_item (filters, i);
gtk_file_chooser_add_filter (chooser, filter);
g_object_unref (filter);
}
}
static void
file_chooser_set_shortcuts (GtkFileChooser *chooser,
GListModel *shortcuts)
{
if (!shortcuts)
return;
for (unsigned int i = 0; i < g_list_model_get_n_items (shortcuts); i++)
{
GFile *shortcut = g_list_model_get_item (shortcuts, i);
gtk_file_chooser_add_shortcut_folder (chooser, shortcut, NULL);
g_object_unref (shortcut);
}
}
static void
file_chooser_set_choices (GtkFileChooser *chooser,
GListModel *choices)
{
if (!choices)
return;
for (unsigned int i = 0; i < g_list_model_get_n_items (choices); i++)
{
GtkChoice *choice = g_list_model_get_item (choices, i);
char *id;
const char *label;
char **options;
const char * const *option_labels;
label = gtk_choice_get_label (choice);
option_labels = gtk_choice_get_options (choice);
id = g_strdup_printf ("choice %u", i);
if (option_labels)
{
GStrvBuilder *strv = g_strv_builder_new ();
for (unsigned int j = 0; option_labels[j]; j++)
{
char *option = g_strdup_printf ("option %u", j);
g_strv_builder_add (strv, option);
g_free (option);
}
options = g_strv_builder_end (strv);
}
else
options = NULL;
gtk_file_chooser_add_choice (chooser, id, label, (const char **)options, (const char **)option_labels);
g_free (id);
g_strfreev (options);
g_object_unref (choice);
}
}
static char **
file_chooser_get_options (GtkFileChooser *chooser,
GListModel *choices)
{
GStrvBuilder *strv;
if (!choices)
return NULL;
strv = g_strv_builder_new ();
for (unsigned int i = 0; i < g_list_model_get_n_items (choices); i++)
{
GtkChoice *choice = g_list_model_get_item (choices, i);
char *id;
const char *option;
unsigned int pos;
const char *option_label;
id = g_strdup_printf ("choice %u", i);
option = gtk_file_chooser_get_choice (chooser, id);
pos = (unsigned int) g_ascii_strtoull (option + strlen ("option "), NULL, 10);
option_label = gtk_choice_get_options (choice)[pos];
g_strv_builder_add (strv, option_label);
g_free (id);
g_object_unref (choice);
}
return g_strv_builder_end (strv);
}
/* }}} */
/* {{{ Public API */
/* {{{ Constructor */
/**
* gtk_file_dialog_new:
*
* Creates a new `GtkFileDialog` object.
*
* Returns: the new `GtkFileDialog`
*
* Since: 4.10
*/
GtkFileDialog *
gtk_file_dialog_new (void)
{
return g_object_new (GTK_TYPE_FILE_DIALOG, NULL);
}
/* }}} */
/* {{{ Getters and setters */
/**
* gtk_file_dialog_get_title:
* @self: a `GtkFileDialog`
*
* Returns the title that will be shown on the
* file chooser dialog.
*
* Returns: the title
*
* Since: 4.10
*/
const char *
gtk_file_dialog_get_title (GtkFileDialog *self)
{
g_return_val_if_fail (GTK_IS_FILE_DIALOG (self), NULL);
return self->title;
}
/**
* gtk_file_dialog_set_title:
* @self: a `GtkFileDialog`
* @title: the new title
*
* Sets the title that will be shown on the
* file chooser dialog.
*
* Since: 4.10
*/
void
gtk_file_dialog_set_title (GtkFileDialog *self,
const char *title)
{
char *new_title;
g_return_if_fail (GTK_IS_FILE_DIALOG (self));
g_return_if_fail (title != NULL);
if (g_strcmp0 (self->title, title) == 0)
return;
new_title = g_strdup (title);
g_free (self->title);
self->title = new_title;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TITLE]);
}
/**
* gtk_file_dialog_get_modal:
* @self: a `GtkFileDialog`
*
* Returns whether the file chooser dialog
* blocks interaction with the parent window
* while it is presented.
*
* Returns: `TRUE` if the file chooser dialog is modal
*
* Since: 4.10
*/
gboolean
gtk_file_dialog_get_modal (GtkFileDialog *self)
{
g_return_val_if_fail (GTK_IS_FILE_DIALOG (self), TRUE);
return self->modal;
}
/**
* gtk_file_dialog_set_modal:
* @self: a `GtkFileDialog`
* @modal: the new value
*
* Sets whether the file chooser dialog
* blocks interaction with the parent window
* while it is presented.
*
* Since: 4.10
*/
void
gtk_file_dialog_set_modal (GtkFileDialog *self,
gboolean modal)
{
g_return_if_fail (GTK_IS_FILE_DIALOG (self));
if (self->modal == modal)
return;
self->modal = modal;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODAL]);
}
/**
* gtk_file_dialog_get_select_multiple:
* @self: a `GtkFileDialog`
*
* Returns whether the file chooser dialog
* allows to select multiple files.
*
* Returns: `TRUE` if the file chooser dialog allows multi-selection
*
* Since: 4.10
*/
gboolean
gtk_file_dialog_get_select_multiple (GtkFileDialog *self)
{
g_return_val_if_fail (GTK_IS_FILE_DIALOG (self), FALSE);
return self->select_multiple;
}
/**
* gtk_file_dialog_set_select_multiple:
* @self: a `GtkFileDialog`
* @modal: the new value
*
* Sets whether the file chooser dialog
* allows to select multiple files.
*
* Since: 4.10
*/
void
gtk_file_dialog_set_select_multiple (GtkFileDialog *self,
gboolean select_multiple)
{
g_return_if_fail (GTK_IS_FILE_DIALOG (self));
if (self->select_multiple == select_multiple)
return;
self->select_multiple = select_multiple;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECT_MULTIPLE]);
}
/**
* gtk_file_dialog_get_create_folders:
* @self: a `GtkFileDialog`
*
* Returns whether the file chooser dialog
* allows to create folders.
*
* Returns: `TRUE` if the file chooser dialog allows folder creation
*
* Since: 4.10
*/
gboolean
gtk_file_dialog_get_create_folders (GtkFileDialog *self)
{
g_return_val_if_fail (GTK_IS_FILE_DIALOG (self), FALSE);
return self->create_folders;
}
/**
* gtk_file_dialog_set_create_folders:
* @self: a `GtkFileDialog`
* @modal: the new value
*
* Sets whether the file chooser dialog
* allows to create folders.
*
* Since: 4.10
*/
void
gtk_file_dialog_set_create_folders (GtkFileDialog *self,
gboolean create_folders)
{
g_return_if_fail (GTK_IS_FILE_DIALOG (self));
if (self->create_folders == create_folders)
return;
self->create_folders = create_folders;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CREATE_FOLDERS]);
}
void
gtk_file_dialog_set_filters (GtkFileDialog *self,
GListModel *filters)
{
g_return_if_fail (GTK_IS_FILE_DIALOG (self));
g_return_if_fail (G_IS_LIST_MODEL (filters));
g_return_if_fail (g_list_model_get_item_type (G_LIST_MODEL (filters)) == GTK_TYPE_FILE_FILTER);
if (!g_set_object (&self->filters, filters))
return;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FILTERS]);
}
GListModel *
gtk_file_dialog_get_filters (GtkFileDialog *self)
{
g_return_val_if_fail (GTK_IS_FILE_DIALOG (self), NULL);
return self->filters;
}
void
gtk_file_dialog_set_shortcuts (GtkFileDialog *self,
GListModel *shortcuts)
{
g_return_if_fail (GTK_IS_FILE_DIALOG (self));
g_return_if_fail (G_IS_LIST_MODEL (shortcuts));
g_return_if_fail (g_list_model_get_item_type (G_LIST_MODEL (shortcuts)) == G_TYPE_FILE);
if (!g_set_object (&self->shortcuts, shortcuts))
return;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHORTCUTS]);
}
GListModel *
gtk_file_dialog_get_shortcuts (GtkFileDialog *self)
{
g_return_val_if_fail (GTK_IS_FILE_DIALOG (self), NULL);
return self->shortcuts;
}
void
gtk_file_dialog_set_choices (GtkFileDialog *self,
GListModel *choices)
{
g_return_if_fail (GTK_IS_FILE_DIALOG (self));
g_return_if_fail (G_IS_LIST_MODEL (choices));
g_return_if_fail (g_list_model_get_item_type (G_LIST_MODEL (choices)) == GTK_TYPE_CHOICE);
if (!g_set_object (&self->choices, choices))
return;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CHOICES]);
}
GListModel *
gtk_file_dialog_get_choices (GtkFileDialog *self)
{
g_return_val_if_fail (GTK_IS_FILE_DIALOG (self), NULL);
return self->choices;
}
/* }}} */
/* {{{ Async API */
typedef struct
{
GListModel *files;
char **options;
} FileResult;
static void response_cb (GTask *task,
int response);
static void
cancelled_cb (GCancellable *cancellable,
GTask *task)
{
response_cb (task, GTK_RESPONSE_CANCEL);
}
static void
response_cb (GTask *task,
int response)
{
GCancellable *cancellable;
cancellable = g_task_get_cancellable (task);
if (cancellable)
g_signal_handlers_disconnect_by_func (cancellable, cancelled_cb, task);
if (response == GTK_RESPONSE_OK)
{
GtkFileDialog *self;
GtkFileChooserDialog *window;
FileResult file_result;
self = GTK_FILE_DIALOG (g_task_get_source_object (task));
window = GTK_FILE_CHOOSER_DIALOG (g_task_get_task_data (task));
file_result.files = gtk_file_chooser_get_files (GTK_FILE_CHOOSER (window));
file_result.options = file_chooser_get_options (GTK_FILE_CHOOSER (window),
gtk_file_dialog_get_choices (self));
g_task_return_pointer (task, &file_result, NULL);
}
else
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled");
g_object_unref (task);
}
static void
dialog_response (GtkDialog *dialog,
int response,
GTask *task)
{
response_cb (task, response);
}
static GtkFileChooserDialog *
create_file_chooser_dialog (GtkFileDialog *self,
GtkWindow *parent,
GtkFileChooserAction action)
{
GtkWidget *window;
const char *accept[] = {
N_("_Open"), N_("_Save"), N_("_Select")
};
window = gtk_file_chooser_dialog_new (self->title, parent,
action,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_(accept[action]), GTK_RESPONSE_OK,
NULL);
gtk_window_set_modal (GTK_WINDOW (window), self->modal);
gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (window), self->select_multiple);
gtk_file_chooser_set_create_folders (GTK_FILE_CHOOSER (window), self->create_folders);
file_chooser_set_filters (GTK_FILE_CHOOSER (window), self->filters);
file_chooser_set_shortcuts (GTK_FILE_CHOOSER (window), self->shortcuts);
file_chooser_set_choices (GTK_FILE_CHOOSER (window), self->choices);
return GTK_FILE_CHOOSER_DIALOG (window);
}
static gboolean
finish_file_op (GtkFileDialog *self,
GAsyncResult *result,
GListModel **files,
char ***options,
GError **error)
{
FileResult *ret;
ret = g_task_propagate_pointer (G_TASK (result), error);
if (ret)
{
*files = ret->files;
if (options)
*options = ret->options;
else
g_strfreev (ret->options);
return TRUE;
}
return FALSE;
}
/**
* gtk_file_dialog_open:
* @self: a `GtkFileDialog`
* @parent: (nullable): the parent `GtkWindow`
* @initial_file: (nullable): the file to select initially
* @cancellable: (nullable): a `GCancellable` to cancel the operation
* @callback: (scope async): a callback to call when the operation is complete
* @user_data: (closure callback): data to pass to @callback
*
* This function initiates a file selection operation by
* presenting a file chooser dialog to the user.
*
* The @callback will be called when the dialog is dismissed.
* It should call [function@Gtk.FileDialog.open_finish]
* to obtain the result.
*
* Since: 4.10
*/
void
gtk_file_dialog_open (GtkFileDialog *self,
GtkWindow *parent,
GFile *initial_file,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GtkFileChooserDialog *window;
GTask *task;
g_return_if_fail (GTK_IS_FILE_DIALOG (self));
window = create_file_chooser_dialog (self, parent, GTK_FILE_CHOOSER_ACTION_OPEN);
if (initial_file)
gtk_file_chooser_set_file (GTK_FILE_CHOOSER (window), initial_file, NULL);
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, gtk_file_dialog_open);
g_task_set_task_data (task, window, (GDestroyNotify) gtk_window_destroy);
if (cancellable)
g_signal_connect (cancellable, "cancelled", G_CALLBACK (cancelled_cb), task);
g_signal_connect (window, "response", G_CALLBACK (dialog_response), task);
gtk_window_present (GTK_WINDOW (window));
}
/**
* gtk_file_dialog_open_finish:
* @self: a `GtkFileDialog`
* @result: a `GAsyncResult`
* @files: (out caller-allocates): return location for the selected files
* @options: (out caller-allocates): return location for choices
* @error: return location for an error
*
* Finishes the [function@Gtk.FileDialog.open] call and
* returns the resulting files as a `GListModel` of `GFiles`.
*
* Returns: `TRUE` if a file was selected. Otherwise,
* `FALSE` is returned and @error is set
*
* Since: 4.10
*/
gboolean
gtk_file_dialog_open_finish (GtkFileDialog *self,
GAsyncResult *result,
GListModel **files,
char ***options,
GError **error)
{
return finish_file_op (self, result, files, options, error);
}
void
gtk_file_dialog_select_folder (GtkFileDialog *self,
GtkWindow *parent,
GFile *current_folder,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GtkFileChooserDialog *window;
GTask *task;
g_return_if_fail (GTK_IS_FILE_DIALOG (self));
window = create_file_chooser_dialog (self, parent, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
if (current_folder)
gtk_file_chooser_set_file (GTK_FILE_CHOOSER (window), current_folder, NULL);
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, gtk_file_dialog_select_folder);
g_task_set_task_data (task, window, (GDestroyNotify) gtk_window_destroy);
if (cancellable)
g_signal_connect (cancellable, "cancelled", G_CALLBACK (cancelled_cb), task);
g_signal_connect (window, "response", G_CALLBACK (dialog_response), task);
gtk_window_present (GTK_WINDOW (window));
}
gboolean
gtk_file_dialog_select_folder_finish (GtkFileDialog *self,
GAsyncResult *result,
GListModel **files,
char ***options,
GError **error)
{
return finish_file_op (self, result, files, options, error);
}
void
gtk_file_dialog_save (GtkFileDialog *self,
GtkWindow *parent,
GFile *current_folder,
const char *current_name,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GtkFileChooserDialog *window;
GTask *task;
g_return_if_fail (GTK_IS_FILE_DIALOG (self));
window = create_file_chooser_dialog (self, parent, GTK_FILE_CHOOSER_ACTION_SAVE);
if (current_folder)
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (window), current_folder, NULL);
if (current_name)
gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (window), current_name);
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, gtk_file_dialog_save);
g_task_set_task_data (task, window, (GDestroyNotify) gtk_window_destroy);
if (cancellable)
g_signal_connect (cancellable, "cancelled", G_CALLBACK (cancelled_cb), task);
g_signal_connect (window, "response", G_CALLBACK (dialog_response), task);
gtk_window_present (GTK_WINDOW (window));
}
gboolean
gtk_file_dialog_save_finish (GtkFileDialog *self,
GAsyncResult *result,
GListModel **files,
char ***options,
GError **error)
{
return finish_file_op (self, result, files, options, error);
}
/* }}} */
/* }}} */
/* vim:set foldmethod=marker expandtab: */

130
gtk/gtkfiledialog.h Normal file
View File

@@ -0,0 +1,130 @@
/* GTK - The GIMP Toolkit
*
* Copyright (C) 2022 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gdk/gdk.h>
#include <gtk/gtkwindow.h>
G_BEGIN_DECLS
#define GTK_TYPE_FILE_DIALOG (gtk_file_dialog_get_type ())
GDK_AVAILABLE_IN_4_10
G_DECLARE_FINAL_TYPE (GtkFileDialog, gtk_file_dialog, GTK, FILE_DIALOG, GObject)
GDK_AVAILABLE_IN_4_10
GtkFileDialog * gtk_file_dialog_new (void);
GDK_AVAILABLE_IN_4_10
const char * gtk_file_dialog_get_title (GtkFileDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_file_dialog_set_title (GtkFileDialog *self,
const char *title);
GDK_AVAILABLE_IN_4_10
gboolean gtk_file_dialog_get_modal (GtkFileDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_file_dialog_set_modal (GtkFileDialog *self,
gboolean modal);
GDK_AVAILABLE_IN_4_10
gboolean gtk_file_dialog_get_select_multiple (GtkFileDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_file_dialog_set_select_multiple (GtkFileDialog *self,
gboolean select_multiple);
GDK_AVAILABLE_IN_4_10
gboolean gtk_file_dialog_get_create_folders (GtkFileDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_file_dialog_set_create_folders (GtkFileDialog *self,
gboolean create_folders);
GDK_AVAILABLE_IN_4_10
void gtk_file_dialog_set_filters (GtkFileDialog *self,
GListModel *filters);
GDK_AVAILABLE_IN_4_10
GListModel * gtk_file_dialog_get_filters (GtkFileDialog *dialog);
GDK_AVAILABLE_IN_4_10
void gtk_file_dialog_set_shortcuts (GtkFileDialog *self,
GListModel *filters);
GDK_AVAILABLE_IN_4_10
GListModel * gtk_file_dialog_get_shortcuts (GtkFileDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_file_dialog_set_choices (GtkFileDialog *self,
GListModel *filters);
GDK_AVAILABLE_IN_4_10
GListModel * gtk_file_dialog_get_choices (GtkFileDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_file_dialog_open (GtkFileDialog *self,
GtkWindow *parent,
GFile *current_file,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GDK_AVAILABLE_IN_4_10
gboolean gtk_file_dialog_open_finish (GtkFileDialog *self,
GAsyncResult *result,
GListModel **files,
char ***options,
GError **error);
GDK_AVAILABLE_IN_4_10
void gtk_file_dialog_select_folder (GtkFileDialog *self,
GtkWindow *parent,
GFile *current_folder,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GDK_AVAILABLE_IN_4_10
gboolean gtk_file_dialog_select_folder_finish
(GtkFileDialog *self,
GAsyncResult *result,
GListModel **files,
char ***options,
GError **error);
GDK_AVAILABLE_IN_4_10
void gtk_file_dialog_save (GtkFileDialog *dialog,
GtkWindow *parent,
GFile *current_folder,
const char *current_name,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GDK_AVAILABLE_IN_4_10
gboolean gtk_file_dialog_save_finish (GtkFileDialog *self,
GAsyncResult *result,
GListModel **files,
char ***choices,
GError **error);
G_END_DECLS

671
gtk/gtkfontdialog.c Normal file
View File

@@ -0,0 +1,671 @@
/*
* GTK - The GIMP Toolkit
* Copyright (C) 2022 Red Hat, Inc.
* All rights reserved.
*
* This Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtkfontdialog.h"
#include "gtkfontchooserdialog.h"
#include "gtkbutton.h"
#include "gtktypebuiltins.h"
#include <glib/gi18n-lib.h>
/**
* GtkFontDialog:
*
* A `GtkFontDialog` object collects the arguments that
* are needed to present a font chooser dialog to the
* user, such as a title for the dialog and whether it
* should be modal.
*
* The dialog is shown with the [function@Gtk.FontDialog.choose_font]
* function. This API follows the GIO async pattern, and the
* result can be obtained by calling
* [function@Gtk.FontDialog.choose_font_finish].
*
* See [class@Gtk.FontDialogButton] for a convenient control
* that uses `GtkFontDialog` and presents the results.
*
* `GtkFontDialog was added in GTK 4.10.
*/
/* {{{ GObject implementation */
struct _GtkFontDialog
{
GObject parent_instance;
char *title;
GtkFontChooserLevel level;
PangoLanguage *language;
PangoFontMap *fontmap;
unsigned int modal : 1;
GtkFontFilterFunc filter;
gpointer filter_data;
GDestroyNotify filter_data_destroy;
};
enum
{
PROP_TITLE = 1,
PROP_MODAL,
PROP_LEVEL,
PROP_LANGUAGE,
PROP_FONTMAP,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES];
G_DEFINE_TYPE (GtkFontDialog, gtk_font_dialog, G_TYPE_OBJECT)
#define DEFAULT_LEVEL (GTK_FONT_CHOOSER_LEVEL_FAMILY| \
GTK_FONT_CHOOSER_LEVEL_STYLE| \
GTK_FONT_CHOOSER_LEVEL_SIZE)
static void
gtk_font_dialog_init (GtkFontDialog *self)
{
self->title = g_strdup (_("Pick a Font"));
self->modal = TRUE;
self->level = DEFAULT_LEVEL;
self->language = pango_language_get_default ();
}
static void
gtk_font_dialog_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GtkFontDialog *self = GTK_FONT_DIALOG (object);
switch (property_id)
{
case PROP_TITLE:
g_value_set_string (value, self->title);
break;
case PROP_MODAL:
g_value_set_boolean (value, self->modal);
break;
case PROP_LEVEL:
g_value_set_flags (value, self->level);
break;
case PROP_LANGUAGE:
g_value_set_boxed (value, self->language);
break;
case PROP_FONTMAP:
g_value_set_object (value, self->fontmap);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_font_dialog_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GtkFontDialog *self = GTK_FONT_DIALOG (object);
switch (property_id)
{
case PROP_TITLE:
gtk_font_dialog_set_title (self, g_value_get_string (value));
break;
case PROP_MODAL:
gtk_font_dialog_set_modal (self, g_value_get_boolean (value));
break;
case PROP_LEVEL:
gtk_font_dialog_set_level (self, g_value_get_flags (value));
break;
case PROP_LANGUAGE:
gtk_font_dialog_set_language (self, g_value_get_boxed (value));
break;
case PROP_FONTMAP:
gtk_font_dialog_set_fontmap (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_font_dialog_finalize (GObject *object)
{
GtkFontDialog *self = GTK_FONT_DIALOG (object);
g_free (self->title);
g_clear_object (&self->fontmap);
g_clear_pointer (&self->filter_data, self->filter_data_destroy);
G_OBJECT_CLASS (gtk_font_dialog_parent_class)->finalize (object);
}
static void
gtk_font_dialog_class_init (GtkFontDialogClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->get_property = gtk_font_dialog_get_property;
object_class->set_property = gtk_font_dialog_set_property;
object_class->finalize = gtk_font_dialog_finalize;
/**
* GtkFontDialog:title: (attributes org.gtk.Property.get=gtk_font_dialog_get_title org.gtk.Property.set=gtk_font_dialog_set_title)
*
* A title that may be shown on the font chooser
* dialog that is presented by [function@Gtk.FontDialog.choose_font].
*
* Since: 4.10
*/
properties[PROP_TITLE] =
g_param_spec_string ("title", NULL, NULL,
NULL,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFontDialog:modal: (attributes org.gtk.Property.get=gtk_font_dialog_get_modal org.gtk.Property.set=gtk_font_dialog_set_modal)
*
* Whether the font chooser dialog is modal.
*
* Since: 4.10
*/
properties[PROP_MODAL] =
g_param_spec_boolean ("modal", NULL, NULL,
TRUE,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFontDialog:level: (attributes org.gtk.Property.get=gtk_font_dialog_get_level org.gtk.Property.set=gtk_font_dialog_set_level)
*
* The level of granularity to offer for selecting fonts.
*
* Since: 4.10
*/
properties[PROP_LEVEL] =
g_param_spec_flags ("level", NULL, NULL,
GTK_TYPE_FONT_CHOOSER_LEVEL,
DEFAULT_LEVEL,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFontDialog:language: (attributes org.gtk.Property.get=gtk_font_dialog_get_language org.gtk.Property.set=gtk_font_dialog_set_language)
*
* The language for which the font features are selected.
*
* Since: 4.10
*/
properties[PROP_LANGUAGE] =
g_param_spec_boxed ("language", NULL, NULL,
PANGO_TYPE_LANGUAGE,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFontDialog:fontmap: (attributes org.gtk.Property.get=gtk_font_dialog_get_fontmap org.gtk.Property.set=gtk_font_dialog_set_fontmap)
*
* Sets a custom font map to select fonts from.
*
* A custom font map can be used to present application-specific
* fonts instead of or in addition to the normal system fonts.
*
* Since: 4.10
*/
properties[PROP_FONTMAP] =
g_param_spec_object ("fontmap", NULL, NULL,
PANGO_TYPE_FONT_MAP,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
}
/* }}} */
/* {{{ Constructor */
/**
* gtk_font_dialog_new:
*
* Creates a new `GtkFontDialog` object.
*
* Returns: the new `GtkFontDialog`
*
* Since: 4.10
*/
GtkFontDialog *
gtk_font_dialog_new (void)
{
return g_object_new (GTK_TYPE_FONT_DIALOG, NULL);
}
/* }}} */
/* {{{ Getters and setters */
/**
* gtk_font_dialog_get_title:
* @self: a `GtkFontDialog`
*
* Returns the title that will be shown on the
* font chooser dialog.
*
* Returns: the title
*
* Since: 4.10
*/
const char *
gtk_font_dialog_get_title (GtkFontDialog *self)
{
g_return_val_if_fail (GTK_IS_FONT_DIALOG (self), NULL);
return self->title;
}
/**
* gtk_font_dialog_set_title:
* @self: a `GtkFontDialog`
* @title: the new title
*
* Sets the title that will be shown on the
* font chooser dialog.
*
* Since: 4.10
*/
void
gtk_font_dialog_set_title (GtkFontDialog *self,
const char *title)
{
char *new_title;
g_return_if_fail (GTK_IS_FONT_DIALOG (self));
g_return_if_fail (title != NULL);
if (g_str_equal (self->title, title))
return;
new_title = g_strdup (title);
g_free (self->title);
self->title = new_title;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TITLE]);
}
/**
* gtk_font_dialog_get_modal:
* @self: a `GtkFontDialog`
*
* Returns whether the font chooser dialog
* blocks interaction with the parent window
* while it is presented.
*
* Returns: `TRUE` if the font chooser dialog is modal
*
* Since: 4.10
*/
gboolean
gtk_font_dialog_get_modal (GtkFontDialog *self)
{
g_return_val_if_fail (GTK_IS_FONT_DIALOG (self), TRUE);
return self->modal;
}
/**
* gtk_font_dialog_set_modal:
* @self: a `GtkFontDialog`
* @modal: the new value
*
* Sets whether the font chooser dialog
* blocks interaction with the parent window
* while it is presented.
*
* Since: 4.10
*/
void
gtk_font_dialog_set_modal (GtkFontDialog *self,
gboolean modal)
{
g_return_if_fail (GTK_IS_FONT_DIALOG (self));
if (self->modal == modal)
return;
self->modal = modal;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODAL]);
}
/**
* gtk_font_dialog_get_level:
* @self: a `GtkFontDialog`
*
* Returns the level of granularity for selecting fonts.
*
* Returns: the level of granularity
*
* Since: 4.10
*/
GtkFontChooserLevel
gtk_font_dialog_get_level (GtkFontDialog *self)
{
g_return_val_if_fail (GTK_IS_FONT_DIALOG (self), DEFAULT_LEVEL);
return self->level;
}
/**
* gtk_font_dialog_set_level:
* @self: a `GtkFontDialog`
* @level: the new value
*
* Sets the level of granularity for selecting fonts.
*
* Since: 4.10
*/
void
gtk_font_dialog_set_level (GtkFontDialog *self,
GtkFontChooserLevel level)
{
g_return_if_fail (GTK_IS_FONT_DIALOG (self));
if (self->level == level)
return;
self->level = level;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LEVEL]);
}
/**
* gtk_font_dialog_get_language:
* @self: a `GtkFontDialog`
*
* Returns the language for which font features are applied.
*
* Returns: (nullable): the language for font features
*
* Since: 4.10
*/
PangoLanguage *
gtk_font_dialog_get_language (GtkFontDialog *self)
{
g_return_val_if_fail (GTK_IS_FONT_DIALOG (self), NULL);
return self->language;
}
/**
* gtk_font_dialog_set_language:
* @self: a `GtkFontDialog`
* @language: the language for font features
*
* Sets the language for which font features are applied.
*
* Since: 4.10
*/
void
gtk_font_dialog_set_language (GtkFontDialog *self,
PangoLanguage *language)
{
g_return_if_fail (GTK_IS_FONT_DIALOG (self));
if (self->language == language)
return;
self->language = language;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LANGUAGE]);
}
/**
* gtk_font_dialog_get_fontmap
* @self: a `GtkFontDialog`
*
* Returns the fontmap from which fonts are selected,
* or `NULL` for the default fontmap.
*
* Returns: (nullable) (transfer none): the fontmap
*
* Since: 4.10
*/
PangoFontMap *
gtk_font_dialog_get_fontmap (GtkFontDialog *self)
{
g_return_val_if_fail (GTK_IS_FONT_DIALOG (self), NULL);
return self->fontmap;
}
/**
* gtk_font_dialog_set_fontmap:
* @self: a `GtkFontDialog`
* @fontmap: (nullable): the fontmap
*
* Sets the fontmap from which fonts are selected.
*
* If @fontmap is `NULL`, the default fontmap is used.
*
* Since: 4.10
*/
void
gtk_font_dialog_set_fontmap (GtkFontDialog *self,
PangoFontMap *fontmap)
{
g_return_if_fail (GTK_IS_FONT_DIALOG (self));
if (g_set_object (&self->fontmap, fontmap))
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FONTMAP]);
}
/**
* gtk_font_dialog_set_filter:
* @self: a `GtkFontDialog`
* @filter: (nullable): a `GtkFontFilterFunc`
* @user_data: (closure): data to pass to @filter
* @destroy: function to call to free @data when it is no longer needed
*
* Adds a filter function that decides which fonts to display
* in the font chooser dialog.
*
* Since: 4.10
*/
void
gtk_font_dialog_set_filter (GtkFontDialog *self,
GtkFontFilterFunc filter,
gpointer filter_data,
GDestroyNotify filter_data_destroy)
{
g_return_if_fail (GTK_IS_FONT_DIALOG (self));
g_clear_pointer (&self->filter_data, self->filter_data_destroy);
self->filter = filter;
self->filter_data = filter_data;
self->filter_data_destroy = filter_data_destroy;
}
/* }}} */
/* {{{ Async API */
static void response_cb (GTask *task,
int response);
static void
cancelled_cb (GCancellable *cancellable,
GTask *task)
{
response_cb (task, GTK_RESPONSE_CANCEL);
}
typedef struct
{
PangoFontDescription *font_desc;
char *font_features;
} FontResult;
static void
response_cb (GTask *task,
int response)
{
GCancellable *cancellable;
cancellable = g_task_get_cancellable (task);
if (cancellable)
g_signal_handlers_disconnect_by_func (cancellable, cancelled_cb, task);
if (response == GTK_RESPONSE_OK)
{
GtkFontChooserDialog *window;
FontResult font_result;
window = GTK_FONT_CHOOSER_DIALOG (g_task_get_task_data (task));
font_result.font_desc = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (window));
font_result.font_features = gtk_font_chooser_get_font_features (GTK_FONT_CHOOSER (window));
g_task_return_pointer (task, &font_result, NULL);
}
else
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled");
g_object_unref (task);
}
static void
dialog_response (GtkDialog *dialog,
int response,
GTask *task)
{
response_cb (task, response);
}
/**
* gtk_font_dialog_choose_font:
* @self: a `GtkFontDialog`
* @parent: (nullable): the parent `GtkWindow`
* @initial_font: (nullable): the font to select initially
* @cancellable: (nullable): a `GCancellable` to cancel the operation
* @callback: (scope async): a callback to call when the operation is complete
* @user_data: (closure callback): data to pass to @callback
*
* This function initiates a font selection operation by
* presenting a font chooser dialog to the user.
*
* The @callback will be called when the dialog is dismissed.
* It should call [function@Gtk.FontDialog.choose_font_finish]
* to obtain the result.
*
* Since: 4.10
*/
void
gtk_font_dialog_choose_font (GtkFontDialog *self,
GtkWindow *parent,
PangoFontDescription *initial_font,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GtkFontChooserDialog *window;
GTask *task;
g_return_if_fail (GTK_IS_FONT_DIALOG (self));
window = GTK_FONT_CHOOSER_DIALOG (gtk_font_chooser_dialog_new (self->title, parent));
gtk_font_chooser_set_level (GTK_FONT_CHOOSER (window), self->level);
if (self->language)
gtk_font_chooser_set_language (GTK_FONT_CHOOSER (window), pango_language_to_string (self->language));
if (self->fontmap)
gtk_font_chooser_set_font_map (GTK_FONT_CHOOSER (window), self->fontmap);
if (initial_font)
gtk_font_chooser_set_font_desc (GTK_FONT_CHOOSER (window), initial_font);
if (self->filter)
gtk_font_chooser_set_filter_func (GTK_FONT_CHOOSER (window),
self->filter,
self->filter_data,
self->filter_data_destroy);
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, gtk_font_dialog_choose_font);
g_task_set_task_data (task, window, (GDestroyNotify) gtk_window_destroy);
if (cancellable)
g_signal_connect (cancellable, "cancelled", G_CALLBACK (cancelled_cb), task);
g_signal_connect (window, "response", G_CALLBACK (dialog_response), task);
gtk_window_present (GTK_WINDOW (window));
}
/**
* gtk_font_dialog_choose_font_finish:
* @self: a `GtkFontDialog`
* @result: a `GAsyncResult`
* @font_desc: (out caller-allocates): return location for font description
* @font_features: (out caller-allocates): return location for font features
* @error: return location for an error
*
* Finishes the [function@Gtk.FontDialog.choose_font] call and
* returns the resulting font description and font features.
*
* Returns: `TRUE` if a font was selected. Otherwise,
* `FALSE` is returned and @error is set
*
* Since: 4.10
*/
gboolean
gtk_font_dialog_choose_font_finish (GtkFontDialog *self,
GAsyncResult *result,
PangoFontDescription **font_desc,
char **font_features,
GError **error)
{
FontResult *font_result;
font_result = g_task_propagate_pointer (G_TASK (result), error);
if (font_result)
{
*font_desc = font_result->font_desc;
*font_features = font_result->font_features;
return TRUE;
}
return FALSE;
}
/* }}} */
/* vim:set foldmethod=marker expandtab: */

97
gtk/gtkfontdialog.h Normal file
View File

@@ -0,0 +1,97 @@
/* GTK - The GIMP Toolkit
*
* Copyright (C) 2022 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gdk/gdk.h>
#include <gtk/gtkwindow.h>
#include <gtk/gtkfontchooser.h>
G_BEGIN_DECLS
#define GTK_TYPE_FONT_DIALOG (gtk_font_dialog_get_type ())
GDK_AVAILABLE_IN_4_10
G_DECLARE_FINAL_TYPE (GtkFontDialog, gtk_font_dialog, GTK, FONT_DIALOG, GObject)
GDK_AVAILABLE_IN_4_10
GtkFontDialog * gtk_font_dialog_new (void);
GDK_AVAILABLE_IN_4_10
void gtk_font_dialog_set_title (GtkFontDialog *self,
const char *title);
GDK_AVAILABLE_IN_4_10
gboolean gtk_font_dialog_get_modal (GtkFontDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_font_dialog_set_modal (GtkFontDialog *self,
gboolean modal);
GDK_AVAILABLE_IN_4_10
const char * gtk_font_dialog_get_title (GtkFontDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_font_dialog_set_level (GtkFontDialog *self,
GtkFontChooserLevel level);
GDK_AVAILABLE_IN_4_10
GtkFontChooserLevel
gtk_font_dialog_get_level (GtkFontDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_font_dialog_set_language (GtkFontDialog *self,
PangoLanguage *language);
GDK_AVAILABLE_IN_4_10
PangoLanguage * gtk_font_dialog_get_language (GtkFontDialog *self);
GDK_AVAILABLE_IN_4_10
PangoFontMap * gtk_font_dialog_get_fontmap (GtkFontDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_font_dialog_set_fontmap (GtkFontDialog *self,
PangoFontMap *fontmap);
GDK_AVAILABLE_IN_4_10
void gtk_font_dialog_set_filter (GtkFontDialog *self,
GtkFontFilterFunc filter,
gpointer user_data,
GDestroyNotify destroy);
GDK_AVAILABLE_IN_4_10
void gtk_font_dialog_choose_font (GtkFontDialog *self,
GtkWindow *parent,
PangoFontDescription *initial_font,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GDK_AVAILABLE_IN_4_10
gboolean gtk_font_dialog_choose_font_finish
(GtkFontDialog *self,
GAsyncResult *result,
PangoFontDescription **font_desc,
char **font_features,
GError **error);
G_END_DECLS

778
gtk/gtkfontdialogbutton.c Normal file
View File

@@ -0,0 +1,778 @@
/*
* GTK - The GIMP Toolkit
* Copyright (C) 2022 Red Hat, Inc.
* All rights reserved.
*
* This Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtkfontdialogbutton.h"
#include "gtkbinlayout.h"
#include "gtkbox.h"
#include "gtkseparator.h"
#include "gtkbutton.h"
#include "gtklabel.h"
#include <glib/gi18n-lib.h>
#include "gtkmain.h"
#include "gtkprivate.h"
#include "gtkwidgetprivate.h"
static void button_clicked (GtkFontDialogButton *self);
/**
* GtkFontDialogButton:
*
* The `GtkFontDialogButton` is wrapped around a [class@Gtk.FontDialog]
* and allows to open a font chooser dialog to change the font.
*
* ![An example GtkFontDialogButton](font-button.png)
*
* It is suitable widget for selecting a font in a preference dialog.
*
* # CSS nodes
*
* ```
* fontbutton
* ╰── button.font
* ╰── [content]
* ```
*
* `GtkFontDialogButton` has a single CSS node with name fontbutton which
* contains a button node with the .font style class.
*/
/* {{{ GObject implementation */
struct _GtkFontDialogButton
{
GtkWidget parent_instance;
GtkWidget *button;
GtkWidget *font_label;
GtkWidget *size_label;
GtkWidget *font_size_box;
guint use_font : 1;
guint use_size : 1;
GtkFontDialog *dialog;
PangoFontDescription *font_desc;
char *font_features;
PangoFontFamily *font_family;
PangoFontFace *font_face;
};
/* Properties */
enum
{
PROP_DIALOG = 1,
PROP_FONT_DESC,
PROP_FONT_FEATURES,
PROP_USE_FONT,
PROP_USE_SIZE,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES];
G_DEFINE_TYPE (GtkFontDialogButton, gtk_font_dialog_button, GTK_TYPE_WIDGET)
static void
gtk_font_dialog_button_init (GtkFontDialogButton *self)
{
GtkWidget *box;
PangoFontDescription *font_desc;
self->button = gtk_button_new ();
g_signal_connect_swapped (self->button, "clicked", G_CALLBACK (button_clicked), self);
self->font_label = gtk_label_new (_("Font"));
gtk_widget_set_hexpand (self->font_label, TRUE);
self->size_label = gtk_label_new ("14");
self->font_size_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_append (GTK_BOX (box), self->font_label);
gtk_box_append (GTK_BOX (self->font_size_box), gtk_separator_new (GTK_ORIENTATION_VERTICAL));
gtk_box_append (GTK_BOX (self->font_size_box), self->size_label);
gtk_box_append (GTK_BOX (box), self->font_size_box);
gtk_button_set_child (GTK_BUTTON (self->button), box);
gtk_widget_set_parent (self->button, GTK_WIDGET (self));
self->use_font = FALSE;
self->use_size = FALSE;
font_desc = pango_font_description_from_string ("Sans 12");
gtk_font_dialog_button_set_font_desc (self, font_desc);
pango_font_description_free (font_desc);
gtk_widget_add_css_class (self->button, "font");
}
static void
gtk_font_dialog_button_set_property (GObject *object,
unsigned int param_id,
const GValue *value,
GParamSpec *pspec)
{
GtkFontDialogButton *self = GTK_FONT_DIALOG_BUTTON (object);
switch (param_id)
{
case PROP_DIALOG:
gtk_font_dialog_button_set_dialog (self, g_value_get_object (value));
break;
case PROP_FONT_DESC:
gtk_font_dialog_button_set_font_desc (self, g_value_get_boxed (value));
break;
case PROP_FONT_FEATURES:
gtk_font_dialog_button_set_font_features (self, g_value_get_string (value));
break;
case PROP_USE_FONT:
gtk_font_dialog_button_set_use_font (self, g_value_get_boolean (value));
break;
case PROP_USE_SIZE:
gtk_font_dialog_button_set_use_size (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
}
}
static void
gtk_font_dialog_button_get_property (GObject *object,
unsigned int param_id,
GValue *value,
GParamSpec *pspec)
{
GtkFontDialogButton *self = GTK_FONT_DIALOG_BUTTON (object);
switch (param_id)
{
case PROP_DIALOG:
g_value_set_object (value, self->dialog);
break;
case PROP_FONT_DESC:
g_value_set_boxed (value, self->font_desc);
break;
case PROP_FONT_FEATURES:
g_value_set_string (value, self->font_features);
break;
case PROP_USE_FONT:
g_value_set_boolean (value, self->use_font);
break;
case PROP_USE_SIZE:
g_value_set_boolean (value, self->use_size);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
}
}
static void
gtk_font_dialog_button_dispose (GObject *object)
{
GtkFontDialogButton *self = GTK_FONT_DIALOG_BUTTON (object);
g_clear_pointer (&self->button, gtk_widget_unparent);
G_OBJECT_CLASS (gtk_font_dialog_button_parent_class)->dispose (object);
}
static void
gtk_font_dialog_button_finalize (GObject *object)
{
GtkFontDialogButton *self = GTK_FONT_DIALOG_BUTTON (object);
g_clear_object (&self->dialog);
pango_font_description_free (self->font_desc);
g_clear_object (&self->font_family);
g_clear_object (&self->font_face);
g_free (self->font_features);
G_OBJECT_CLASS (gtk_font_dialog_button_parent_class)->finalize (object);
}
static void
gtk_font_dialog_button_class_init (GtkFontDialogButtonClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
object_class->get_property = gtk_font_dialog_button_get_property;
object_class->set_property = gtk_font_dialog_button_set_property;
object_class->dispose = gtk_font_dialog_button_dispose;
object_class->finalize = gtk_font_dialog_button_finalize;
widget_class->grab_focus = gtk_widget_grab_focus_child;
widget_class->focus = gtk_widget_focus_child;
/**
* GtkFontDialogButton:dialog: (attributes org.gtk.Property.get=gtk_font_dialog_button_get_dialog org.gtk.Property.set=gtk_font_dialog_button_set_dialog)
*
* The `GtkFontDialog` that contains parameters for
* the font chooser dialog.
*
* Since: 4.10
*/
properties[PROP_DIALOG] =
g_param_spec_object ("dialog", NULL, NULL,
GTK_TYPE_FONT_DIALOG,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFontDialogButton:font-desc: (attributes org.gtk.Property.get=gtk_font_dialog_button_get_font_desc org.gtk.Property.set=gtk_font_dialog_button_set_font_desc)
*
* The selected font.
*
* This property can be set to give the button its initial
* font, and it will be updated to reflect the users choice
* in the font chooser dialog.
*
* Listen to `notify::font-desc` to get informed about changes
* to the buttons font.
*
* Since: 4.10
*/
properties[PROP_FONT_DESC] =
g_param_spec_boxed ("font-desc", NULL, NULL,
PANGO_TYPE_FONT_DESCRIPTION,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFontDialogButton:font-features: (attributes org.gtk.Property.get=gtk_font_dialog_button_get_font_features org.gtk.Property.set=gtk_font_dialog_button_set_font_features)
*
* The selected font features.
*
* This property will be updated to reflect the users choice
* in the font chooser dialog.
*
* Listen to `notify::font-features` to get informed about changes
* to the buttons font features.
*
* Since: 4.10
*/
properties[PROP_FONT_FEATURES] =
g_param_spec_string ("font-features", NULL, NULL,
NULL,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFontDialogButton:use-font: (attributes org.gtk.Property.get=gtk_font_dialog_button_get_use_font org.gtk.Property.set=gtk_font_dialog_button_set_use_font)
*
* Whether the buttons label will be drawn in the selected font.
*/
properties[PROP_USE_FONT] =
g_param_spec_boolean ("use-font", NULL, NULL,
FALSE,
GTK_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkFontDialogButton:use-size: (attributes org.gtk.Property.get=gtk_font_dialog_button_get_use_size org.gtk.Property.set=gtk_font_dialog_button_set_use_size)
*
* Whether the buttons label will use the selected font size.
*/
properties[PROP_USE_SIZE] =
g_param_spec_boolean ("use-size", NULL, NULL,
FALSE,
GTK_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
gtk_widget_class_set_css_name (widget_class, "fontbutton");
}
/* }}} */
/* {{{ Private API, callbacks */
static void
font_chosen (GObject *source,
GAsyncResult *result,
gpointer data)
{
GtkFontDialogButton *self = data;
PangoFontDescription *desc;
char *features;
GError *error = NULL;
if (gtk_font_dialog_choose_font_finish (self->dialog, result, &desc, &features, &error))
{
gtk_font_dialog_button_set_font_desc (self, desc);
gtk_font_dialog_button_set_font_features (self, features);
}
else
{
g_print ("%s\n", error->message);
g_error_free (error);
}
}
static void
button_clicked (GtkFontDialogButton *self)
{
GtkRoot *root = gtk_widget_get_root (GTK_WIDGET (self));
GtkWindow *parent = NULL;
if (GTK_IS_WINDOW (root))
parent = GTK_WINDOW (root);
gtk_font_dialog_choose_font (self->dialog, parent, self->font_desc,
NULL, font_chosen, self);
}
static gboolean
font_description_style_equal (const PangoFontDescription *a,
const PangoFontDescription *b)
{
return (pango_font_description_get_weight (a) == pango_font_description_get_weight (b) &&
pango_font_description_get_style (a) == pango_font_description_get_style (b) &&
pango_font_description_get_stretch (a) == pango_font_description_get_stretch (b) &&
pango_font_description_get_variant (a) == pango_font_description_get_variant (b));
}
static void
update_font_data (GtkFontDialogButton *self)
{
PangoFontMap *fontmap = NULL;
const char *family_name;
g_assert (self->font_desc != NULL);
g_clear_object (&self->font_family);
g_clear_object (&self->font_face);
family_name = pango_font_description_get_family (self->font_desc);
if (family_name == NULL)
return;
if (self->dialog)
fontmap = gtk_font_dialog_get_fontmap (self->dialog);
if (!fontmap)
fontmap = pango_cairo_font_map_get_default ();
for (unsigned int i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (fontmap)); i++)
{
PangoFontFamily *family = g_list_model_get_item (G_LIST_MODEL (fontmap), i);
const char *name = pango_font_family_get_name (family);
g_object_unref (family);
if (g_ascii_strcasecmp (name, family_name) == 0)
{
g_set_object (&self->font_family, family);
break;
}
}
for (unsigned i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->font_family)); i++)
{
PangoFontFace *face = g_list_model_get_item (G_LIST_MODEL (self->font_family), i);
PangoFontDescription *tmp_desc = pango_font_face_describe (face);
g_object_unref (face);
if (font_description_style_equal (tmp_desc, self->font_desc))
{
g_set_object (&self->font_face, face);
pango_font_description_free (tmp_desc);
break;
}
else
pango_font_description_free (tmp_desc);
}
}
static void
update_font_info (GtkFontDialogButton *self)
{
const char *fam_name;
const char *face_name;
char *family_style;
GtkFontChooserLevel level;
if (self->font_family)
fam_name = pango_font_family_get_name (self->font_family);
else
fam_name = C_("font", "None");
if (self->font_face)
face_name = pango_font_face_get_face_name (self->font_face);
else
face_name = "";
if (self->dialog)
level = gtk_font_dialog_get_level (self->dialog);
else
level = GTK_FONT_CHOOSER_LEVEL_STYLE|GTK_FONT_CHOOSER_LEVEL_SIZE;
if ((level & GTK_FONT_CHOOSER_LEVEL_STYLE) != 0)
family_style = g_strconcat (fam_name, " ", face_name, NULL);
else
family_style = g_strdup (fam_name);
gtk_label_set_text (GTK_LABEL (self->font_label), family_style);
g_free (family_style);
if ((level & GTK_FONT_CHOOSER_LEVEL_SIZE) != 0)
{
/* mirror Pango, which doesn't translate this either */
char *size = g_strdup_printf ("%2.4g%s",
pango_font_description_get_size (self->font_desc) / (double)PANGO_SCALE,
pango_font_description_get_size_is_absolute (self->font_desc) ? "px" : "");
gtk_label_set_text (GTK_LABEL (self->size_label), size);
g_free (size);
gtk_widget_show (self->font_size_box);
}
else
gtk_widget_hide (self->font_size_box);
}
static void
apply_use_font (GtkFontDialogButton *self)
{
if (!self->use_font)
gtk_label_set_attributes (GTK_LABEL (self->font_label), NULL);
else
{
PangoFontDescription *desc;
PangoAttrList *attrs;
PangoLanguage *language = NULL;
desc = pango_font_description_copy (self->font_desc);
if (!self->use_size)
pango_font_description_unset_fields (desc, PANGO_FONT_MASK_SIZE);
attrs = pango_attr_list_new ();
/* Prevent font fallback */
pango_attr_list_insert (attrs, pango_attr_fallback_new (FALSE));
/* Force current font and features */
pango_attr_list_insert (attrs, pango_attr_font_desc_new (desc));
if (self->font_features)
pango_attr_list_insert (attrs, pango_attr_font_features_new (self->font_features));
if (self->dialog)
language = gtk_font_dialog_get_language (self->dialog);
if (language)
pango_attr_list_insert (attrs, pango_attr_language_new (language));
gtk_label_set_attributes (GTK_LABEL (self->font_label), attrs);
pango_attr_list_unref (attrs);
pango_font_description_free (desc);
}
}
/* }}} */
/* {{{ Public API */
/* {{{ Constructor */
/**
* gtk_font_dialog_button_new:
* @dialog: (nullable) (transfer full): the `GtkFontDialog` to use
*
* Creates a new `GtkFontDialogButton` with the
* given `GtkFontDialog`.
*
* You can pass `NULL` to this function and set a `GtkFontDialog`
* later. The button will be insensitive until that happens.
*
* Returns: the new `GtkFontDialogButton`
*
* Since: 4.10
*/
GtkWidget *
gtk_font_dialog_button_new (GtkFontDialog *dialog)
{
GtkWidget *self;
g_return_val_if_fail (GTK_IS_FONT_DIALOG (dialog), NULL);
self = g_object_new (GTK_TYPE_FONT_DIALOG_BUTTON,
"dialog", dialog,
NULL);
g_clear_object (&dialog);
return self;
}
/* }}} */
/* {{{ Setters and Getters */
/**
* gtk_font_dialog_button_set_dialog:
* @self: a `GtkFontDialogButton`
* @dialog: the new `GtkFontDialog`
*
* Sets a `GtkFontDialog` object to use for
* creating the font chooser dialog that is
* presented when the user clicks the button.
*
* Since: 4.10
*/
void
gtk_font_dialog_button_set_dialog (GtkFontDialogButton *self,
GtkFontDialog *dialog)
{
g_return_if_fail (GTK_IS_FONT_DIALOG_BUTTON (self));
g_return_if_fail (dialog == NULL || GTK_IS_FONT_DIALOG (dialog));
if (!g_set_object (&self->dialog, dialog))
return;
gtk_widget_set_sensitive (self->button, dialog != NULL);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DIALOG]);
}
/**
* gtk_font_dialog_button_get_dialog:
* @self: a `GtkFontDialogButton`
*
* Returns the `GtkFontDialog` of @self.
*
* Returns: (nullable) (transfer none): the `GtkFontDialog`
*
* Since: 4.10
*/
GtkFontDialog *
gtk_font_dialog_button_get_dialog (GtkFontDialogButton *self)
{
g_return_val_if_fail (GTK_IS_FONT_DIALOG_BUTTON (self), NULL);
return self->dialog;
}
/**
* gtk_font_dialog_button_set_font_desc:
* @self: a `GtkFontDialogButton`
* @font_desc: the new font
*
* Sets the font of the button.
*
* Since: 4.10
*/
void
gtk_font_dialog_button_set_font_desc (GtkFontDialogButton *self,
PangoFontDescription *font_desc)
{
g_return_if_fail (GTK_IS_FONT_DIALOG_BUTTON (self));
g_return_if_fail (font_desc != NULL);
if (self->font_desc == font_desc ||
(self->font_desc && font_desc &&
pango_font_description_equal (self->font_desc, font_desc)))
return;
if (self->font_desc)
pango_font_description_free (self->font_desc);
self->font_desc = pango_font_description_copy (font_desc);
update_font_data (self);
update_font_info (self);
apply_use_font (self);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FONT_DESC]);
}
/**
* gtk_font_dialog_button_get_font_desc:
* @self: a `GtkFontDialogButton`
*
* Returns the font of the button.
*
* This function is what should be used to obtain
* the font that was choosen by the user. To get
* informed about changes, listen to "notify::font-desc".
*
* Returns: (transfer none) (nullable): the font
*
* Since: 4.10
*/
PangoFontDescription *
gtk_font_dialog_button_get_font_desc (GtkFontDialogButton *self)
{
g_return_val_if_fail (GTK_IS_FONT_DIALOG_BUTTON (self), NULL);
return self->font_desc;
}
/**
* gtk_font_dialog_button_set_font_features:
* @self: a `GtkFontDialogButton`
* @font_features: (nullable): the font features
*
* Sets the font features of the button.
*
* Since: 4.10
*/
void
gtk_font_dialog_button_set_font_features (GtkFontDialogButton *self,
const char *font_features)
{
char *new_features;
g_return_if_fail (GTK_IS_FONT_DIALOG_BUTTON (self));
if (g_strcmp0 (self->font_features, font_features) == 0)
return;
new_features = g_strdup (font_features);
g_free (self->font_features);
self->font_features = new_features;
apply_use_font (self);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FONT_FEATURES]);
}
/**
* gtk_font_dialog_button_get_font_features:
* @self: a `GtkFontDialogButton`
*
* Returns the font features of the button.
*
* This function is what should be used to obtain
* the font features that were choosen by the user.
* To get informed about changes, listen to
* "notify::font-features".
*
* Returns: (transfer none) (nullable): the font features
*
* Since: 4.10
*/
const char *
gtk_font_dialog_button_get_font_features (GtkFontDialogButton *self)
{
g_return_val_if_fail (GTK_IS_FONT_DIALOG_BUTTON (self), NULL);
return self->font_features;
}
/**
* gtk_font_dialog_button_set_use_font:
* @self: a `GtkFontDialogButton`
* @use_font: If `TRUE`, font name will be written using
* the chosen font
*
* If @use_font is `TRUE`, the font name will be written
* using the selected font.
*
* Since: 4.10
*/
void
gtk_font_dialog_button_set_use_font (GtkFontDialogButton *self,
gboolean use_font)
{
g_return_if_fail (GTK_IS_FONT_DIALOG_BUTTON (self));
if (self->use_font == use_font)
return;
self->use_font = use_font;
apply_use_font (self);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_USE_FONT]);
}
/**
* gtk_font_dialog_button_get_use_font:
* @self: a `GtkFontDialogButton`
*
* Returns whether the selected font is used in the label.
*
* Returns: whether the selected font is used in the label
*
* Since: 4.10
*/
gboolean
gtk_font_dialog_button_get_use_font (GtkFontDialogButton *self)
{
g_return_val_if_fail (GTK_IS_FONT_DIALOG_BUTTON (self), FALSE);
return self->use_font;
}
/**
* gtk_font_dialog_button_set_use_size:
* @self: a `GtkFontDialogButton`
* @use_size: If `TRUE`, font name will be written using
* the chosen font size
*
* If @use_size is `TRUE`, the font name will be written
* using the selected font size.
*
* Since: 4.10
*/
void
gtk_font_dialog_button_set_use_size (GtkFontDialogButton *self,
gboolean use_size)
{
g_return_if_fail (GTK_IS_FONT_DIALOG_BUTTON (self));
if (self->use_size == use_size)
return;
self->use_size = use_size;
apply_use_font (self);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_USE_SIZE]);
}
/**
* gtk_font_dialog_button_get_use_size:
* @self: a `GtkFontDialogButton`
*
* Returns whether the selected font size is used in the label.
*
* Returns: whether the selected font size is used in the label
*
* Since: 4.10
*/
gboolean
gtk_font_dialog_button_get_use_size (GtkFontDialogButton *self)
{
g_return_val_if_fail (GTK_IS_FONT_DIALOG_BUTTON (self), FALSE);
return self->use_size;
}
/* }}} */
/* }}} */
/* vim:set foldmethod=marker expandtab: */

76
gtk/gtkfontdialogbutton.h Normal file
View File

@@ -0,0 +1,76 @@
/*
* GTK - The GIMP Toolkit
* Copyright (C) 2022 Red Hat, Inc.
* All rights reserved.
*
* This Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtkbutton.h>
#include <gtk/gtkfontdialog.h>
G_BEGIN_DECLS
#define GTK_TYPE_FONT_DIALOG_BUTTON (gtk_font_dialog_button_get_type ())
GDK_AVAILABLE_IN_4_10
G_DECLARE_FINAL_TYPE (GtkFontDialogButton, gtk_font_dialog_button, GTK, FONT_DIALOG_BUTTON, GtkWidget)
GDK_AVAILABLE_IN_4_10
GtkWidget * gtk_font_dialog_button_new (GtkFontDialog *dialog);
GDK_AVAILABLE_IN_4_10
void gtk_font_dialog_button_set_dialog (GtkFontDialogButton *self,
GtkFontDialog *dialog);
GDK_AVAILABLE_IN_4_10
GtkFontDialog * gtk_font_dialog_button_get_dialog (GtkFontDialogButton *self);
GDK_AVAILABLE_IN_4_10
void gtk_font_dialog_button_set_font_desc (GtkFontDialogButton *self,
PangoFontDescription *font_desc);
GDK_AVAILABLE_IN_4_10
PangoFontDescription *
gtk_font_dialog_button_get_font_desc (GtkFontDialogButton *self);
GDK_AVAILABLE_IN_4_10
void gtk_font_dialog_button_set_font_features
(GtkFontDialogButton *self,
const char *font_features);
GDK_AVAILABLE_IN_4_10
const char * gtk_font_dialog_button_get_font_features (GtkFontDialogButton *self);
GDK_AVAILABLE_IN_4_10
void gtk_font_dialog_button_set_use_font (GtkFontDialogButton *self,
gboolean use_font);
GDK_AVAILABLE_IN_4_10
gboolean gtk_font_dialog_button_get_use_font (GtkFontDialogButton *self);
GDK_AVAILABLE_IN_4_10
void gtk_font_dialog_button_set_use_size (GtkFontDialogButton *self,
gboolean use_size);
GDK_AVAILABLE_IN_4_10
gboolean gtk_font_dialog_button_get_use_size (GtkFontDialogButton *self);
G_END_DECLS

715
gtk/gtkinfodialog.c Normal file
View File

@@ -0,0 +1,715 @@
/*
* GTK - The GIMP Toolkit
* Copyright (C) 2022 Red Hat, Inc.
* All rights reserved.
*
* This Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtkinfodialog.h"
#include "gtkbutton.h"
#include "gtkmessagedialog.h"
#include <glib/gi18n-lib.h>
/**
* GtkInfoDialog:
*
* A `GtkInfoDialog` object collects the arguments that
* are needed to present a info chooser dialog to the
* user, such as a title for the dialog and whether it
* should be modal.
*
* The dialog is shown with the [function@Gtk.InfoDialog.present]
* function. This API follows the GIO async pattern, and the
* result can be obtained by calling
* [function@Gtk.InfoDialog.present_finish].
*
* `GtkInfoDialog was added in GTK 4.10.
*/
/* {{{ GObject implementation */
struct _GtkInfoDialog
{
GObject parent_instance;
char *heading;
char *body;
char **buttons;
unsigned int modal : 1;
unsigned int heading_use_markup: 1;
unsigned int body_use_markup: 1;
};
enum
{
PROP_MODAL = 1,
PROP_HEADING,
PROP_HEADING_USE_MARKUP,
PROP_BODY,
PROP_BODY_USE_MARKUP,
PROP_BUTTONS,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES];
G_DEFINE_TYPE (GtkInfoDialog, gtk_info_dialog, G_TYPE_OBJECT)
static void
gtk_info_dialog_init (GtkInfoDialog *self)
{
self->modal = TRUE;
}
static void
gtk_info_dialog_finalize (GObject *object)
{
GtkInfoDialog *self = GTK_INFO_DIALOG (object);
g_free (self->heading);
g_free (self->body);
g_strfreev (self->buttons);
G_OBJECT_CLASS (gtk_info_dialog_parent_class)->finalize (object);
}
static void
gtk_info_dialog_get_property (GObject *object,
unsigned int property_id,
GValue *value,
GParamSpec *pspec)
{
GtkInfoDialog *self = GTK_INFO_DIALOG (object);
switch (property_id)
{
case PROP_MODAL:
g_value_set_boolean (value, self->modal);
break;
case PROP_HEADING:
g_value_set_string (value, self->heading);
break;
case PROP_HEADING_USE_MARKUP:
g_value_set_boolean (value, self->heading_use_markup);
break;
case PROP_BODY:
g_value_set_string (value, self->body);
break;
case PROP_BODY_USE_MARKUP:
g_value_set_boolean (value, self->body_use_markup);
break;
case PROP_BUTTONS:
g_value_set_boxed (value, self->buttons);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_info_dialog_set_property (GObject *object,
unsigned int prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkInfoDialog *self = GTK_INFO_DIALOG (object);
switch (prop_id)
{
case PROP_MODAL:
gtk_info_dialog_set_modal (self, g_value_get_boolean (value));
break;
case PROP_HEADING:
gtk_info_dialog_set_heading (self, g_value_get_string (value));
break;
case PROP_HEADING_USE_MARKUP:
gtk_info_dialog_set_heading_use_markup (self, g_value_get_boolean (value));
break;
case PROP_BODY:
gtk_info_dialog_set_body (self, g_value_get_string (value));
break;
case PROP_BODY_USE_MARKUP:
gtk_info_dialog_set_body_use_markup (self, g_value_get_boolean (value));
break;
case PROP_BUTTONS:
gtk_info_dialog_set_buttons (self, g_value_get_boxed (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_info_dialog_class_init (GtkInfoDialogClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = gtk_info_dialog_finalize;
object_class->get_property = gtk_info_dialog_get_property;
object_class->set_property = gtk_info_dialog_set_property;
/**
* GtkInfoDialog:modal: (attributes org.gtk.Property.get=gtk_info_dialog_get_modal org.gtk.Property.set=gtk_color_dialog_set_modal)
*
* Whether the info chooser dialog is modal.
*
* Since: 4.10
*/
properties[PROP_MODAL] =
g_param_spec_boolean ("modal", NULL, NULL,
TRUE,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkInfoDialog:heading: (attributes org.gtk.Property.get=gtk_info_dialog_get_heading org.gtk.Property.set=gtk_color_dialog_set_heading)
*
* The heading for the dialog that is presented
* by [function@Gtk.InfoDialog.present].
*
* Since: 4.10
*/
properties[PROP_HEADING] =
g_param_spec_string ("heading", NULL, NULL,
NULL,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkInfoDialog:heading-use-markup: (attributes org.gtk.Property.get=gtk_info_dialog_get_heading_use_markup org.gtk.Property.set=gtk_color_dialog_set_heading_use_markup)
*
* Whether the heading uses markup.
*
* Since: 4.10
*/
properties[PROP_HEADING_USE_MARKUP] =
g_param_spec_boolean ("heading-use-markup", NULL, NULL,
FALSE,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkInfoDialog:body: (attributes org.gtk.Property.get=gtk_info_dialog_get_body org.gtk.Property.set=gtk_color_dialog_set_body)
*
* The body text for the dialog that is presented
* by [function@Gtk.InfoDialog.present].
*
* Since: 4.10
*/
properties[PROP_BODY] =
g_param_spec_string ("body", NULL, NULL,
NULL,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkInfoDialog:body-use-markup: (attributes org.gtk.Property.get=gtk_info_dialog_get_body_use_markup org.gtk.Property.set=gtk_color_dialog_set_body_use_markup)
*
* Whether the body text uses markup.
*
* Since: 4.10
*/
properties[PROP_BODY_USE_MARKUP] =
g_param_spec_boolean ("body-use-markup", NULL, NULL,
FALSE,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkInfoDialog:buttons: (attributes org.gtk.Property.get=gtk_info_dialog_get_buttons org.gtk.Property.set=gtk_color_dialog_set_buttons)
*
* The labels for buttons to show in the dialog that is presented
* by [function@Gtk.InfoDialog.present].
*
* The labels should be translated and may contain a _ to indicate
* mnemonic characters.
*
* Since: 4.10
*/
properties[PROP_BUTTONS] =
g_param_spec_boxed ("buttons", NULL, NULL,
G_TYPE_STRV,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
}
/* }}} */
/* {{{ Constructor */
/**
* gtk_info_dialog_new:
*
* Creates a new `GtkInfoDialog` object.
*
* Returns: the new `GtkInfoDialog`
*
* Since: 4.10
*/
GtkInfoDialog *
gtk_info_dialog_new (void)
{
return g_object_new (GTK_TYPE_INFO_DIALOG, NULL);
}
/* }}} */
/* {{{ Getters and setters */
/**
* gtk_info_dialog_get_modal:
* @self: a `GtkInfoDialog`
*
* Returns whether the info chooser dialog
* blocks interaction with the parent window
* while it is presented.
*
* Returns: `TRUE` if the info chooser dialog is modal
*
* Since: 4.10
*/
gboolean
gtk_info_dialog_get_modal (GtkInfoDialog *self)
{
g_return_val_if_fail (GTK_IS_INFO_DIALOG (self), TRUE);
return self->modal;
}
/**
* gtk_info_dialog_set_modal:
* @self: a `GtkInfoDialog`
* @modal: the new value
*
* Sets whether the info chooser dialog
* blocks interaction with the parent window
* while it is presented.
*
* Since: 4.10
*/
void
gtk_info_dialog_set_modal (GtkInfoDialog *self,
gboolean modal)
{
g_return_if_fail (GTK_IS_INFO_DIALOG (self));
if (self->modal == modal)
return;
self->modal = modal;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODAL]);
}
/**
* gtk_info_dialog_get_heading:
* @self: a `GtkInfoDialog`
*
* Returns the heading that will be shown in the
* info dialog.
*
* Returns: the heading
*
* Since: 4.10
*/
const char *
gtk_info_dialog_get_heading (GtkInfoDialog *self)
{
g_return_val_if_fail (GTK_IS_INFO_DIALOG (self), NULL);
return self->heading;
}
/**
* gtk_info_dialog_set_heading:
* @self: a `GtkInfoDialog`
* @text: the new heading
*
* Sets the heading that will be shown in the
* info dialog.
*
* Since: 4.10
*/
void
gtk_info_dialog_set_heading (GtkInfoDialog *self,
const char *text)
{
char *new_text;
g_return_if_fail (GTK_IS_INFO_DIALOG (self));
g_return_if_fail (text != NULL);
if (g_strcmp0 (self->heading, text) == 0)
return;
new_text = g_strdup (text);
g_free (self->heading);
self->heading = new_text;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HEADING]);
}
/**
* gtk_info_dialog_get_heading_use_markup:
* @self: a `GtkInfoDialog`
*
* Returns whether the heading uses markup.
*
* Returns: `TRUE` if the heading uses markup
*
* Since: 4.10
*/
gboolean
gtk_info_dialog_get_heading_use_markup (GtkInfoDialog *self)
{
g_return_val_if_fail (GTK_IS_INFO_DIALOG (self), FALSE);
return self->heading_use_markup;
}
/**
* gtk_info_dialog_set_heading_use_markup:
* @self: a `GtkInfoDialog`
* @use_markup: the new value
*
* Sets whether the heading uses markup.
*
* Since: 4.10
*/
void
gtk_info_dialog_set_heading_use_markup (GtkInfoDialog *self,
gboolean use_markup)
{
g_return_if_fail (GTK_IS_INFO_DIALOG (self));
if (self->heading_use_markup == use_markup)
return;
self->heading_use_markup = use_markup;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HEADING_USE_MARKUP]);
}
/**
* gtk_info_dialog_get_body:
* @self: a `GtkInfoDialog`
*
* Returns the body text that will be shown
* in the info dialog.
*
* Returns: the body text
*
* Since: 4.10
*/
const char *
gtk_info_dialog_get_body (GtkInfoDialog *self)
{
g_return_val_if_fail (GTK_IS_INFO_DIALOG (self), NULL);
return self->body;
}
/**
* gtk_info_dialog_set_body:
* @self: a `GtkInfoDialog`
* @text: the new text
*
* Sets the body text that will be shown
* in the info dialog.
*
* Since: 4.10
*/
void
gtk_info_dialog_set_body (GtkInfoDialog *self,
const char *text)
{
char *new_text;
g_return_if_fail (GTK_IS_INFO_DIALOG (self));
g_return_if_fail (text != NULL);
if (g_strcmp0 (self->body, text) == 0)
return;
new_text = g_strdup (text);
g_free (self->body);
self->body = new_text;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_BODY]);
}
/**
* gtk_info_dialog_get_body_use_markup:
* @self: a `GtkInfoDialog`
*
* Returns whether the body text uses markup.
*
* Returns: `TRUE` if body text uses markup
*
* Since: 4.10
*/
gboolean
gtk_info_dialog_get_body_use_markup (GtkInfoDialog *self)
{
g_return_val_if_fail (GTK_IS_INFO_DIALOG (self), FALSE);
return self->body_use_markup;
}
/**
* gtk_info_dialog_set_body_use_markup:
* @self: a `GtkInfoDialog`
* @use_markup: the new value
*
* Sets whether the body text uses markup.
*
* Since: 4.10
*/
void
gtk_info_dialog_set_body_use_markup (GtkInfoDialog *self,
gboolean use_markup)
{
g_return_if_fail (GTK_IS_INFO_DIALOG (self));
if (self->body_use_markup == use_markup)
return;
self->body_use_markup = use_markup;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_BODY_USE_MARKUP]);
}
/**
* gtk_info_dialog_get_buttons:
* @self: a `GtkInfoDialog`
*
* Returns the button labels for the info dialog.
*
* Returns: (nullable) (transfer none): the button labels
*
* Since: 4.10
*/
const char * const *
gtk_info_dialog_get_buttons (GtkInfoDialog *self)
{
g_return_val_if_fail (GTK_IS_INFO_DIALOG (self), NULL);
return (const char * const *) self->buttons;
}
/**
* gtk_info_dialog_set_buttons:
* @self: a `GtkInfoDialog`
* @labels: the new button labels
*
* Sets the button labels for the info dialog.
*
* Since: 4.10
*/
void
gtk_info_dialog_set_buttons (GtkInfoDialog *self,
const char * const *labels)
{
g_return_if_fail (GTK_IS_INFO_DIALOG (self));
g_return_if_fail (labels != NULL);
g_strfreev (self->buttons);
self->buttons = g_strdupv ((char **)labels);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_BUTTONS]);
}
/* }}} */
/* {{{ Convenience API */
/**
* gtk_info_dialog_set_heading_markup:
* @self: a `GtkInfoDialog`
* @text: the new heading
*
* Sets the heading that will be shown in the
* info dialog, and marks it as using markup.
*
* Since: 4.10
*/
void
gtk_info_dialog_set_heading_markup (GtkInfoDialog *self,
const char *text)
{
g_object_freeze_notify (G_OBJECT (self));
gtk_info_dialog_set_heading (self, text);
gtk_info_dialog_set_heading_use_markup (self, TRUE);
g_object_thaw_notify (G_OBJECT (self));
}
/**
* gtk_info_dialog_set_body_markup:
* @self: a `GtkInfoDialog`
* @text: the new heading
*
* Sets the body text that will be shown in the
* info dialog, and marks it as using markup.
*
* Since: 4.10
*/
void
gtk_info_dialog_set_body_markup (GtkInfoDialog *self,
const char *text)
{
g_object_freeze_notify (G_OBJECT (self));
gtk_info_dialog_set_body (self, text);
gtk_info_dialog_set_body_use_markup (self, TRUE);
g_object_thaw_notify (G_OBJECT (self));
}
/* }}} */
/* {{{ Async API */
static void response_cb (GTask *task,
int response);
static void
cancelled_cb (GCancellable *cancellable,
GTask *task)
{
response_cb (task, -1);
}
static void
response_cb (GTask *task,
int response)
{
GCancellable *cancellable;
cancellable = g_task_get_cancellable (task);
if (cancellable)
g_signal_handlers_disconnect_by_func (cancellable, cancelled_cb, task);
if (response >= 0)
g_task_return_int (task, response);
else
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled");
g_object_unref (task);
}
static void
dialog_response (GtkDialog *dialog,
int response,
GTask *task)
{
response_cb (task, response);
}
/**
* gtk_info_dialog_present:
* @self: a `GtkInfoDialog`
* @parent: (nullable): the parent `GtkWindow`
* @cancellable: (nullable): a `GCancellable` to cancel the operation
* @callback: (scope async): a callback to call when the operation is complete
* @user_data: (closure callback): data to pass to @callback
*
* This function presents an info dialog to the user.
*
* The @callback will be called when the dialog is dismissed.
* It should call [function@Gtk.InfoDialog.present_finish]
* to obtain the result.
*
* Since: 4.10
*/
void
gtk_info_dialog_present (GtkInfoDialog *self,
GtkWindow *parent,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GtkMessageDialog *window;
GTask *task;
g_return_if_fail (GTK_IS_INFO_DIALOG (self));
window = g_object_new (GTK_TYPE_MESSAGE_DIALOG,
"transient-for", parent,
"destroy-with-parent", TRUE,
"modal", self->modal,
"text", self->heading,
"use-markup", self->heading_use_markup,
"secondary-text", self->body,
"secondary-use-markup", self->body_use_markup,
NULL);
if (self->buttons)
{
for (int i = 0; self->buttons[i]; i++)
gtk_dialog_add_button (GTK_DIALOG (window), self->buttons[i], i);
}
else
gtk_dialog_add_button (GTK_DIALOG (window), _("Close"), 0);
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, gtk_info_dialog_present);
g_task_set_task_data (task, window, (GDestroyNotify) gtk_window_destroy);
if (cancellable)
g_signal_connect (cancellable, "cancelled", G_CALLBACK (cancelled_cb), task);
g_signal_connect (window, "response", G_CALLBACK (dialog_response), task);
gtk_window_present (GTK_WINDOW (window));
}
/**
* gtk_info_dialog_present_finish:
* @self: a `GtkInfoDialog`
* @result: a `GAsyncResult`
* @button: (out caller-allocates): return location for button
* @error: return location for an error
*
* Finishes the [function@Gtk.InfoDialog.present] call
* and returns the button that was clicked.
*
* Returns: `TRUE` if a button was clicked.
* Otherwise, `FALSE` is returned and @error is set
*
* Since: 4.10
*/
gboolean
gtk_info_dialog_present_finish (GtkInfoDialog *self,
GAsyncResult *result,
int *button,
GError **error)
{
*button = (int) g_task_propagate_int (G_TASK (result), error);
return ! (error && *error);
}
/* }}} */
/* vim:set foldmethod=marker expandtab: */

104
gtk/gtkinfodialog.h Normal file
View File

@@ -0,0 +1,104 @@
/* GTK - The GIMP Toolkit
*
* Copyright (C) 2022 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gdk/gdk.h>
#include <gtk/gtkwindow.h>
G_BEGIN_DECLS
#define GTK_TYPE_INFO_DIALOG (gtk_info_dialog_get_type ())
GDK_AVAILABLE_IN_4_10
G_DECLARE_FINAL_TYPE (GtkInfoDialog, gtk_info_dialog, GTK, INFO_DIALOG, GObject)
GDK_AVAILABLE_IN_4_10
GtkInfoDialog * gtk_info_dialog_new (void);
GDK_AVAILABLE_IN_4_10
gboolean gtk_info_dialog_get_modal (GtkInfoDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_info_dialog_set_modal (GtkInfoDialog *self,
gboolean modal);
GDK_AVAILABLE_IN_4_10
const char * gtk_info_dialog_get_heading (GtkInfoDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_info_dialog_set_heading (GtkInfoDialog *self,
const char *text);
GDK_AVAILABLE_IN_4_10
void gtk_info_dialog_set_heading_markup (GtkInfoDialog *self,
const char *text);
GDK_AVAILABLE_IN_4_10
gboolean gtk_info_dialog_get_heading_use_markup
(GtkInfoDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_info_dialog_set_heading_use_markup
(GtkInfoDialog *self,
gboolean use_markup);
GDK_AVAILABLE_IN_4_10
const char * gtk_info_dialog_get_body (GtkInfoDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_info_dialog_set_body (GtkInfoDialog *self,
const char *text);
GDK_AVAILABLE_IN_4_10
void gtk_info_dialog_set_body_markup (GtkInfoDialog *self,
const char *text);
GDK_AVAILABLE_IN_4_10
gboolean gtk_info_dialog_get_body_use_markup (GtkInfoDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_info_dialog_set_body_use_markup (GtkInfoDialog *self,
gboolean use_markup);
GDK_AVAILABLE_IN_4_10
void gtk_info_dialog_set_buttons (GtkInfoDialog *self,
const char * const *labels);
GDK_AVAILABLE_IN_4_10
const char * const *
gtk_info_dialog_get_buttons (GtkInfoDialog *self);
GDK_AVAILABLE_IN_4_10
void gtk_info_dialog_present (GtkInfoDialog *self,
GtkWindow *parent,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GDK_AVAILABLE_IN_4_10
gboolean gtk_info_dialog_present_finish (GtkInfoDialog *self,
GAsyncResult *result,
int *button,
GError **error);
G_END_DECLS

View File

@@ -109,6 +109,7 @@ gtk_private_sources = files([
'gtkfilechoosernativeportal.c',
'gtkfilechooserutils.c',
'gtkfilechoosercell.c',
'gtkfiledialog.c',
'gtkfilesystemmodel.c',
'gtkfilethumbnail.c',
'gtkgizmo.c',
@@ -182,10 +183,13 @@ gtk_public_sources = files([
'gtkcenterbox.c',
'gtkcenterlayout.c',
'gtkcheckbutton.c',
'gtkchoice.c',
'gtkcolorbutton.c',
'gtkcolorchooser.c',
'gtkcolorchooserdialog.c',
'gtkcolorchooserwidget.c',
'gtkcolordialog.c',
'gtkcolordialogbutton.c',
'gtkcolorutils.c',
'gtkcolumnview.c',
'gtkcolumnviewcolumn.c',
@@ -237,6 +241,8 @@ gtk_public_sources = files([
'gtkfontchooserdialog.c',
'gtkfontchooserutils.c',
'gtkfontchooserwidget.c',
'gtkfontdialog.c',
'gtkfontdialogbutton.c',
'gtkframe.c',
'gtkgesture.c',
'gtkgesturedrag.c',
@@ -261,6 +267,7 @@ gtk_public_sources = files([
'gtkimmodule.c',
'gtkimmulticontext.c',
'gtkinfobar.c',
'gtkinfodialog.c',
'gtkinscription.c',
'gtklabel.c',
'gtklayoutchild.c',
@@ -438,10 +445,13 @@ gtk_public_headers = files([
'gtkcenterbox.h',
'gtkcenterlayout.h',
'gtkcheckbutton.h',
'gtkchoice.h',
'gtkcolorbutton.h',
'gtkcolorchooser.h',
'gtkcolorchooserdialog.h',
'gtkcolorchooserwidget.h',
'gtkcolordialog.h',
'gtkcolordialogbutton.h',
'gtkcolorutils.h',
'gtkcolumnview.h',
'gtkcolumnviewcolumn.h',
@@ -481,6 +491,7 @@ gtk_public_headers = files([
'gtkfilechooserdialog.h',
'gtkfilechoosernative.h',
'gtkfilechooserwidget.h',
'gtkfiledialog.h',
'gtkfilefilter.h',
'gtkfilter.h',
'gtkfilterlistmodel.h',
@@ -492,6 +503,8 @@ gtk_public_headers = files([
'gtkfontchooser.h',
'gtkfontchooserdialog.h',
'gtkfontchooserwidget.h',
'gtkfontdialog.h',
'gtkfontdialogbutton.h',
'gtkframe.h',
'gtkgesture.h',
'gtkgesturedrag.h',
@@ -515,6 +528,7 @@ gtk_public_headers = files([
'gtkimmodule.h',
'gtkimmulticontext.h',
'gtkinfobar.h',
'gtkinfodialog.h',
'gtkinscription.h',
'gtklabel.h',
'gtklayoutchild.h',