forked from AuroraMiddleware/gtk
601dfeeff9
Fixes https://gitlab.gnome.org/GNOME/gtk/-/issues/3525 Fixes https://gitlab.gnome.org/GNOME/gtk/-/issues/3550
968 lines
30 KiB
C
968 lines
30 KiB
C
/* GTK - The GIMP Toolkit
|
|
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
|
*
|
|
* 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/>.
|
|
*/
|
|
|
|
/*
|
|
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
|
|
* file for a list of people on the GTK+ Team. See the ChangeLog
|
|
* files for a list of changes. These files are distributed with
|
|
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "gtkcheckbutton.h"
|
|
|
|
#include "gtkactionhelperprivate.h"
|
|
#include "gtkboxlayout.h"
|
|
#include "gtkbuiltiniconprivate.h"
|
|
#include "gtkcssnumbervalueprivate.h"
|
|
#include "gtkgestureclick.h"
|
|
#include "gtkintl.h"
|
|
#include "gtklabel.h"
|
|
#include "gtkprivate.h"
|
|
#include "gtkshortcuttrigger.h"
|
|
#include "gtkstylecontextprivate.h"
|
|
#include "gtkwidgetprivate.h"
|
|
#include "gtkmodelbuttonprivate.h"
|
|
|
|
/**
|
|
* SECTION:gtkcheckbutton
|
|
* @Short_description: Create widgets with a discrete toggle button
|
|
* @Title: GtkCheckButton
|
|
* @See_also: #GtkButton, #GtkToggleButton
|
|
*
|
|
* A #GtkCheckButton places a label next to an indicator.
|
|
*
|
|
* # CSS nodes
|
|
*
|
|
* |[<!-- language="plain" -->
|
|
* checkbutton[.text-button]
|
|
* ├── check
|
|
* ╰── [label]
|
|
* ]|
|
|
*
|
|
* A #GtkCheckButton has a main node with name checkbutton. If the
|
|
* #GtkCheckButton:label property is set, it contains a label child.
|
|
* The indicator node is named check when no group is set, and radio
|
|
* if the checkbutton is grouped together with other checkbuttons.
|
|
*
|
|
* # Accessibility
|
|
*
|
|
* GtkCheckButton uses the #GTK_ACCESSIBLE_ROLE_CHECKBOX role.
|
|
*/
|
|
|
|
typedef struct {
|
|
GtkWidget *indicator_widget;
|
|
GtkWidget *label_widget;
|
|
|
|
guint inconsistent: 1;
|
|
guint active: 1;
|
|
guint use_underline: 1;
|
|
|
|
GtkCheckButton *group_next;
|
|
GtkCheckButton *group_prev;
|
|
|
|
GtkActionHelper *action_helper;
|
|
} GtkCheckButtonPrivate;
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_ACTIVE,
|
|
PROP_GROUP,
|
|
PROP_LABEL,
|
|
PROP_INCONSISTENT,
|
|
PROP_USE_UNDERLINE,
|
|
|
|
/* actionable properties */
|
|
PROP_ACTION_NAME,
|
|
PROP_ACTION_TARGET,
|
|
LAST_PROP = PROP_ACTION_NAME
|
|
};
|
|
|
|
enum {
|
|
TOGGLED,
|
|
ACTIVATE,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static void gtk_check_button_actionable_iface_init (GtkActionableInterface *iface);
|
|
|
|
static guint signals[LAST_SIGNAL] = { 0 };
|
|
static GParamSpec *props[LAST_PROP] = { NULL, };
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (GtkCheckButton, gtk_check_button, GTK_TYPE_WIDGET,
|
|
G_ADD_PRIVATE (GtkCheckButton)
|
|
G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIONABLE, gtk_check_button_actionable_iface_init))
|
|
|
|
static void
|
|
gtk_check_button_dispose (GObject *object)
|
|
{
|
|
GtkCheckButtonPrivate *priv = gtk_check_button_get_instance_private (GTK_CHECK_BUTTON (object));
|
|
|
|
g_clear_object (&priv->action_helper);
|
|
|
|
g_clear_pointer (&priv->indicator_widget, gtk_widget_unparent);
|
|
g_clear_pointer (&priv->label_widget, gtk_widget_unparent);
|
|
|
|
gtk_check_button_set_group (GTK_CHECK_BUTTON (object), NULL);
|
|
|
|
G_OBJECT_CLASS (gtk_check_button_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
button_role_changed (GtkCheckButton *self)
|
|
{
|
|
GtkCheckButtonPrivate *priv = gtk_check_button_get_instance_private (self);
|
|
|
|
if (gtk_action_helper_get_role (priv->action_helper) == GTK_BUTTON_ROLE_RADIO)
|
|
gtk_css_node_set_name (gtk_widget_get_css_node (priv->indicator_widget),
|
|
g_quark_from_static_string("radio"));
|
|
else
|
|
gtk_css_node_set_name (gtk_widget_get_css_node (priv->indicator_widget),
|
|
g_quark_from_static_string("check"));
|
|
}
|
|
|
|
static void
|
|
ensure_action_helper (GtkCheckButton *self)
|
|
{
|
|
GtkCheckButtonPrivate *priv = gtk_check_button_get_instance_private (self);
|
|
|
|
if (priv->action_helper)
|
|
return;
|
|
|
|
priv->action_helper = gtk_action_helper_new (GTK_ACTIONABLE (self));
|
|
g_signal_connect_swapped (priv->action_helper, "notify::role",
|
|
G_CALLBACK (button_role_changed), self);
|
|
}
|
|
|
|
static void
|
|
gtk_check_button_set_action_name (GtkActionable *actionable,
|
|
const char *action_name)
|
|
{
|
|
GtkCheckButton *self = GTK_CHECK_BUTTON (actionable);
|
|
GtkCheckButtonPrivate *priv = gtk_check_button_get_instance_private (self);
|
|
|
|
ensure_action_helper (self);
|
|
|
|
gtk_action_helper_set_action_name (priv->action_helper, action_name);
|
|
}
|
|
|
|
static void
|
|
gtk_check_button_set_action_target_value (GtkActionable *actionable,
|
|
GVariant *action_target)
|
|
{
|
|
GtkCheckButton *self = GTK_CHECK_BUTTON (actionable);
|
|
GtkCheckButtonPrivate *priv = gtk_check_button_get_instance_private (self);
|
|
|
|
ensure_action_helper (self);
|
|
|
|
gtk_action_helper_set_action_target_value (priv->action_helper, action_target);
|
|
}
|
|
|
|
static void
|
|
gtk_check_button_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
switch (prop_id)
|
|
{
|
|
case PROP_ACTIVE:
|
|
gtk_check_button_set_active (GTK_CHECK_BUTTON (object), g_value_get_boolean (value));
|
|
break;
|
|
case PROP_GROUP:
|
|
gtk_check_button_set_group (GTK_CHECK_BUTTON (object), g_value_get_object (value));
|
|
break;
|
|
case PROP_LABEL:
|
|
gtk_check_button_set_label (GTK_CHECK_BUTTON (object), g_value_get_string (value));
|
|
break;
|
|
case PROP_INCONSISTENT:
|
|
gtk_check_button_set_inconsistent (GTK_CHECK_BUTTON (object), g_value_get_boolean (value));
|
|
break;
|
|
case PROP_USE_UNDERLINE:
|
|
gtk_check_button_set_use_underline (GTK_CHECK_BUTTON (object), g_value_get_boolean (value));
|
|
break;
|
|
case PROP_ACTION_NAME:
|
|
gtk_check_button_set_action_name (GTK_ACTIONABLE (object), g_value_get_string (value));
|
|
break;
|
|
case PROP_ACTION_TARGET:
|
|
gtk_check_button_set_action_target_value (GTK_ACTIONABLE (object), g_value_get_variant (value));
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_check_button_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkCheckButtonPrivate *priv = gtk_check_button_get_instance_private (GTK_CHECK_BUTTON (object));
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_ACTIVE:
|
|
g_value_set_boolean (value, gtk_check_button_get_active (GTK_CHECK_BUTTON (object)));
|
|
break;
|
|
case PROP_LABEL:
|
|
g_value_set_string (value, gtk_check_button_get_label (GTK_CHECK_BUTTON (object)));
|
|
break;
|
|
case PROP_INCONSISTENT:
|
|
g_value_set_boolean (value, gtk_check_button_get_inconsistent (GTK_CHECK_BUTTON (object)));
|
|
break;
|
|
case PROP_USE_UNDERLINE:
|
|
g_value_set_boolean (value, gtk_check_button_get_use_underline (GTK_CHECK_BUTTON (object)));
|
|
break;
|
|
case PROP_ACTION_NAME:
|
|
g_value_set_string (value, gtk_action_helper_get_action_name (priv->action_helper));
|
|
break;
|
|
case PROP_ACTION_TARGET:
|
|
g_value_set_variant (value, gtk_action_helper_get_action_target_value (priv->action_helper));
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static const char *
|
|
gtk_check_button_get_action_name (GtkActionable *actionable)
|
|
{
|
|
GtkCheckButton *self = GTK_CHECK_BUTTON (actionable);
|
|
GtkCheckButtonPrivate *priv = gtk_check_button_get_instance_private (self);
|
|
|
|
return gtk_action_helper_get_action_name (priv->action_helper);
|
|
}
|
|
|
|
static GVariant *
|
|
gtk_check_button_get_action_target_value (GtkActionable *actionable)
|
|
{
|
|
GtkCheckButton *self = GTK_CHECK_BUTTON (actionable);
|
|
GtkCheckButtonPrivate *priv = gtk_check_button_get_instance_private (self);
|
|
|
|
return gtk_action_helper_get_action_target_value (priv->action_helper);
|
|
}
|
|
|
|
static void
|
|
gtk_check_button_actionable_iface_init (GtkActionableInterface *iface)
|
|
{
|
|
iface->get_action_name = gtk_check_button_get_action_name;
|
|
iface->set_action_name = gtk_check_button_set_action_name;
|
|
iface->get_action_target_value = gtk_check_button_get_action_target_value;
|
|
iface->set_action_target_value = gtk_check_button_set_action_target_value;
|
|
}
|
|
|
|
static void
|
|
click_pressed_cb (GtkGestureClick *gesture,
|
|
guint n_press,
|
|
double x,
|
|
double y,
|
|
GtkWidget *widget)
|
|
{
|
|
if (gtk_widget_get_focus_on_click (widget) && !gtk_widget_has_focus (widget))
|
|
gtk_widget_grab_focus (widget);
|
|
|
|
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
|
|
}
|
|
|
|
static void
|
|
click_released_cb (GtkGestureClick *gesture,
|
|
guint n_press,
|
|
double x,
|
|
double y,
|
|
GtkWidget *widget)
|
|
{
|
|
GtkCheckButton *self = GTK_CHECK_BUTTON (widget);
|
|
GtkCheckButtonPrivate *priv = gtk_check_button_get_instance_private (self);
|
|
|
|
if (priv->active && (priv->group_prev || priv->group_next))
|
|
return;
|
|
|
|
gtk_check_button_set_active (self, !priv->active);
|
|
|
|
if (priv->action_helper)
|
|
gtk_action_helper_activate (priv->action_helper);
|
|
}
|
|
|
|
static void
|
|
update_accessible_state (GtkCheckButton *check_button)
|
|
{
|
|
GtkCheckButtonPrivate *priv = gtk_check_button_get_instance_private (check_button);
|
|
|
|
GtkAccessibleTristate checked_state;
|
|
|
|
if (priv->inconsistent)
|
|
checked_state = GTK_ACCESSIBLE_TRISTATE_MIXED;
|
|
else if (priv->active)
|
|
checked_state = GTK_ACCESSIBLE_TRISTATE_TRUE;
|
|
else
|
|
checked_state = GTK_ACCESSIBLE_TRISTATE_FALSE;
|
|
|
|
gtk_accessible_update_state (GTK_ACCESSIBLE (check_button),
|
|
GTK_ACCESSIBLE_STATE_CHECKED, checked_state,
|
|
-1);
|
|
}
|
|
|
|
|
|
static GtkCheckButton *
|
|
get_group_next (GtkCheckButton *self)
|
|
{
|
|
return ((GtkCheckButtonPrivate *)gtk_check_button_get_instance_private (self))->group_next;
|
|
}
|
|
|
|
static GtkCheckButton *
|
|
get_group_prev (GtkCheckButton *self)
|
|
{
|
|
return ((GtkCheckButtonPrivate *)gtk_check_button_get_instance_private (self))->group_prev;
|
|
}
|
|
|
|
static GtkCheckButton *
|
|
get_group_first (GtkCheckButton *self)
|
|
{
|
|
GtkCheckButton *group_first = NULL;
|
|
GtkCheckButton *iter;
|
|
|
|
/* Find first in group */
|
|
iter = self;
|
|
while (iter)
|
|
{
|
|
group_first = iter;
|
|
|
|
iter = get_group_prev (iter);
|
|
if (!iter)
|
|
break;
|
|
}
|
|
|
|
g_assert (group_first);
|
|
|
|
return group_first;
|
|
}
|
|
|
|
static GtkCheckButton *
|
|
get_group_active_button (GtkCheckButton *self)
|
|
{
|
|
GtkCheckButton *iter;
|
|
|
|
for (iter = get_group_first (self); iter; iter = get_group_next (iter))
|
|
{
|
|
if (gtk_check_button_get_active (iter))
|
|
return iter;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static gboolean
|
|
gtk_check_button_focus (GtkWidget *widget,
|
|
GtkDirectionType direction)
|
|
{
|
|
GtkCheckButton *self = GTK_CHECK_BUTTON (widget);
|
|
GtkCheckButton *active_button;
|
|
|
|
active_button = get_group_active_button (self);
|
|
|
|
if (gtk_widget_is_focus (widget))
|
|
{
|
|
GtkCheckButton *iter;
|
|
GPtrArray *child_array;
|
|
GtkWidget *new_focus = NULL;
|
|
guint index;
|
|
gboolean found;
|
|
guint i;
|
|
|
|
if (direction == GTK_DIR_TAB_FORWARD ||
|
|
direction == GTK_DIR_TAB_BACKWARD)
|
|
return FALSE;
|
|
|
|
child_array = g_ptr_array_new ();
|
|
for (iter = get_group_first (self); iter; iter = get_group_next (iter))
|
|
g_ptr_array_add (child_array, iter);
|
|
|
|
gtk_widget_focus_sort (widget, direction, child_array);
|
|
found = g_ptr_array_find (child_array, widget, &index);
|
|
|
|
if (found)
|
|
{
|
|
/* Start at the *next* widget in the list */
|
|
if (index < child_array->len - 1)
|
|
index ++;
|
|
}
|
|
else
|
|
{
|
|
/* Search from the start of the list */
|
|
index = 0;
|
|
}
|
|
|
|
for (i = index; i < child_array->len; i ++)
|
|
{
|
|
GtkWidget *child = g_ptr_array_index (child_array, i);
|
|
|
|
if (gtk_widget_get_mapped (child) && gtk_widget_is_sensitive (child))
|
|
{
|
|
new_focus = child;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if (new_focus)
|
|
{
|
|
gtk_widget_grab_focus (new_focus);
|
|
gtk_check_button_set_active (GTK_CHECK_BUTTON (new_focus), TRUE);
|
|
if (active_button && active_button != (GtkCheckButton *)new_focus)
|
|
gtk_check_button_set_active (GTK_CHECK_BUTTON (active_button), FALSE);
|
|
}
|
|
|
|
g_ptr_array_free (child_array, TRUE);
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (active_button && active_button != self)
|
|
return FALSE;
|
|
|
|
gtk_widget_grab_focus (widget);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_check_button_real_activate (GtkCheckButton *check_button)
|
|
{
|
|
gtk_check_button_set_active (check_button,
|
|
!gtk_check_button_get_active (check_button));
|
|
}
|
|
|
|
static void
|
|
gtk_check_button_class_init (GtkCheckButtonClass *class)
|
|
{
|
|
const guint activate_keyvals[] = {
|
|
GDK_KEY_space,
|
|
GDK_KEY_KP_Space,
|
|
GDK_KEY_Return,
|
|
GDK_KEY_ISO_Enter,
|
|
GDK_KEY_KP_Enter
|
|
};
|
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
|
|
GtkShortcutAction *activate_action;
|
|
|
|
object_class->dispose = gtk_check_button_dispose;
|
|
object_class->set_property = gtk_check_button_set_property;
|
|
object_class->get_property = gtk_check_button_get_property;
|
|
|
|
widget_class->focus = gtk_check_button_focus;
|
|
|
|
class->activate = gtk_check_button_real_activate;
|
|
|
|
props[PROP_ACTIVE] =
|
|
g_param_spec_boolean ("active",
|
|
P_("Active"),
|
|
P_("If the toggle button should be pressed in"),
|
|
FALSE,
|
|
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
|
props[PROP_GROUP] =
|
|
g_param_spec_object ("group",
|
|
P_("Group"),
|
|
P_("The check button whose group this widget belongs to."),
|
|
GTK_TYPE_CHECK_BUTTON,
|
|
GTK_PARAM_WRITABLE);
|
|
props[PROP_LABEL] =
|
|
g_param_spec_string ("label",
|
|
P_("Label"),
|
|
P_("Text of the label widget inside the button, if the button contains a label widget"),
|
|
NULL,
|
|
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
props[PROP_INCONSISTENT] =
|
|
g_param_spec_boolean ("inconsistent",
|
|
P_("Inconsistent"),
|
|
P_("If the check button is in an “in between” state"),
|
|
FALSE,
|
|
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
props[PROP_USE_UNDERLINE] =
|
|
g_param_spec_boolean ("use-underline",
|
|
P_("Use underline"),
|
|
P_("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"),
|
|
FALSE,
|
|
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
g_object_class_install_properties (object_class, LAST_PROP, props);
|
|
|
|
g_object_class_override_property (object_class, PROP_ACTION_NAME, "action-name");
|
|
g_object_class_override_property (object_class, PROP_ACTION_TARGET, "action-target");
|
|
|
|
|
|
/**
|
|
* GtkCheckButton::toggled:
|
|
*
|
|
* Emitted when the buttons's #GtkCheckButton:active flag changes.
|
|
*/
|
|
signals[TOGGLED] =
|
|
g_signal_new (I_("toggled"),
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GtkCheckButtonClass, toggled),
|
|
NULL, NULL,
|
|
NULL,
|
|
G_TYPE_NONE, 0);
|
|
|
|
/**
|
|
* GtkCheckButton::activate:
|
|
* @widget: the object which received the signal.
|
|
*
|
|
* The ::activate signal on GtkCheckButton is an action signal and
|
|
* emitting it causes the button to animate press then release.
|
|
* Applications should never connect to this signal, but use the
|
|
* #GtkCheckButton::toggled signal.
|
|
*/
|
|
signals[ACTIVATE] =
|
|
g_signal_new (I_ ("activate"),
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
|
|
G_STRUCT_OFFSET (GtkCheckButtonClass, activate),
|
|
NULL, NULL,
|
|
NULL,
|
|
G_TYPE_NONE, 0);
|
|
|
|
gtk_widget_class_set_activate_signal (widget_class, signals[ACTIVATE]);
|
|
|
|
activate_action = gtk_signal_action_new ("activate");
|
|
for (guint i = 0; i < G_N_ELEMENTS (activate_keyvals); i++)
|
|
{
|
|
GtkShortcut *activate_shortcut = gtk_shortcut_new (gtk_keyval_trigger_new (activate_keyvals[i], 0),
|
|
g_object_ref (activate_action));
|
|
|
|
gtk_widget_class_add_shortcut (widget_class, activate_shortcut);
|
|
g_object_unref (activate_shortcut);
|
|
}
|
|
g_object_unref (activate_action);
|
|
|
|
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT);
|
|
gtk_widget_class_set_css_name (widget_class, I_("checkbutton"));
|
|
gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_CHECKBOX);
|
|
}
|
|
|
|
static void
|
|
gtk_check_button_init (GtkCheckButton *self)
|
|
{
|
|
GtkCheckButtonPrivate *priv = gtk_check_button_get_instance_private (self);
|
|
GtkGesture *gesture;
|
|
|
|
gtk_widget_set_receives_default (GTK_WIDGET (self), FALSE);
|
|
priv->indicator_widget = gtk_builtin_icon_new ("check");
|
|
gtk_widget_set_halign (priv->indicator_widget, GTK_ALIGN_CENTER);
|
|
gtk_widget_set_valign (priv->indicator_widget, GTK_ALIGN_CENTER);
|
|
gtk_widget_set_parent (priv->indicator_widget, GTK_WIDGET (self));
|
|
|
|
update_accessible_state (self);
|
|
|
|
gesture = gtk_gesture_click_new ();
|
|
gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture), FALSE);
|
|
gtk_gesture_single_set_exclusive (GTK_GESTURE_SINGLE (gesture), TRUE);
|
|
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), GDK_BUTTON_PRIMARY);
|
|
g_signal_connect (gesture, "pressed", G_CALLBACK (click_pressed_cb), self);
|
|
g_signal_connect (gesture, "released", G_CALLBACK (click_released_cb), self);
|
|
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture), GTK_PHASE_CAPTURE);
|
|
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
|
|
|
|
gtk_widget_set_focusable (GTK_WIDGET (self), TRUE);
|
|
}
|
|
|
|
/**
|
|
* gtk_check_button_new:
|
|
*
|
|
* Creates a new #GtkCheckButton.
|
|
*
|
|
* Returns: a #GtkWidget.
|
|
*/
|
|
GtkWidget *
|
|
gtk_check_button_new (void)
|
|
{
|
|
return g_object_new (GTK_TYPE_CHECK_BUTTON, NULL);
|
|
}
|
|
|
|
/**
|
|
* gtk_check_button_new_with_label:
|
|
* @label: (nullable): the text for the check button.
|
|
*
|
|
* Creates a new #GtkCheckButton with a #GtkLabel next to it, if
|
|
* @label is non-%NULL.
|
|
*
|
|
* Returns: a new #GtkCheckButton
|
|
*/
|
|
GtkWidget*
|
|
gtk_check_button_new_with_label (const char *label)
|
|
{
|
|
return g_object_new (GTK_TYPE_CHECK_BUTTON, "label", label, NULL);
|
|
}
|
|
|
|
/**
|
|
* gtk_check_button_new_with_mnemonic:
|
|
* @label: (nullable): The text of the button, with an underscore in front of the
|
|
* mnemonic character
|
|
*
|
|
* Creates a new #GtkCheckButton containing a label. Underscores
|
|
* in @label indicate the mnemonic for the check button.
|
|
*
|
|
* Returns: a new #GtkCheckButton
|
|
*/
|
|
GtkWidget*
|
|
gtk_check_button_new_with_mnemonic (const char *label)
|
|
{
|
|
return g_object_new (GTK_TYPE_CHECK_BUTTON,
|
|
"label", label,
|
|
"use-underline", TRUE,
|
|
NULL);
|
|
}
|
|
|
|
/**
|
|
* gtk_check_button_set_inconsistent:
|
|
* @check_button: a #GtkCheckButton
|
|
* @inconsistent: %TRUE if state is inconsistent
|
|
*
|
|
* If the user has selected a range of elements (such as some text or
|
|
* spreadsheet cells) that are affected by a check button, and the
|
|
* current values in that range are inconsistent, you may want to
|
|
* display the toggle in an "in between" state. Normally you would
|
|
* turn off the inconsistent state again if the user checks the
|
|
* check button. This has to be done manually,
|
|
* gtk_check_button_set_inconsistent only affects visual appearance,
|
|
* not the semantics of the button.
|
|
*/
|
|
void
|
|
gtk_check_button_set_inconsistent (GtkCheckButton *check_button,
|
|
gboolean inconsistent)
|
|
{
|
|
GtkCheckButtonPrivate *priv = gtk_check_button_get_instance_private (check_button);
|
|
|
|
g_return_if_fail (GTK_IS_CHECK_BUTTON (check_button));
|
|
|
|
inconsistent = !!inconsistent;
|
|
if (priv->inconsistent != inconsistent)
|
|
{
|
|
priv->inconsistent = inconsistent;
|
|
|
|
if (inconsistent)
|
|
{
|
|
gtk_widget_set_state_flags (GTK_WIDGET (check_button), GTK_STATE_FLAG_INCONSISTENT, FALSE);
|
|
gtk_widget_set_state_flags (priv->indicator_widget, GTK_STATE_FLAG_INCONSISTENT, FALSE);
|
|
}
|
|
else
|
|
{
|
|
gtk_widget_unset_state_flags (GTK_WIDGET (check_button), GTK_STATE_FLAG_INCONSISTENT);
|
|
gtk_widget_unset_state_flags (priv->indicator_widget, GTK_STATE_FLAG_INCONSISTENT);
|
|
}
|
|
|
|
update_accessible_state (check_button);
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (check_button), props[PROP_INCONSISTENT]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gtk_check_button_get_inconsistent:
|
|
* @check_button: a #GtkCheckButton
|
|
*
|
|
* Returns whether the check button is in an inconsistent state.
|
|
*
|
|
* Returns: %TRUE if @check_button is currently in an 'in between' state, %FALSE otherwise.
|
|
*/
|
|
gboolean
|
|
gtk_check_button_get_inconsistent (GtkCheckButton *check_button)
|
|
{
|
|
GtkCheckButtonPrivate *priv = gtk_check_button_get_instance_private (check_button);
|
|
|
|
g_return_val_if_fail (GTK_IS_CHECK_BUTTON (check_button), FALSE);
|
|
|
|
return priv->inconsistent;
|
|
}
|
|
|
|
/**
|
|
* gtk_check_button_get_active:
|
|
* @self: a #GtkCheckButton
|
|
*
|
|
* Returns the current value of the #GtkCheckButton:active property.
|
|
*
|
|
* Returns: The value of the #GtkCheckButton:active property.
|
|
* See gtk_check_button_set_active() for details on how to set a new value.
|
|
*/
|
|
gboolean
|
|
gtk_check_button_get_active (GtkCheckButton *self)
|
|
{
|
|
GtkCheckButtonPrivate *priv = gtk_check_button_get_instance_private (self);
|
|
|
|
g_return_val_if_fail (GTK_IS_CHECK_BUTTON (self), FALSE);
|
|
|
|
return priv->active;
|
|
}
|
|
|
|
/**
|
|
* gtk_check_button_set_active:
|
|
* @self: a #GtkCheckButton
|
|
* @setting: the new value to set
|
|
*
|
|
* Sets the new value of the #GtkCheckButton:active property.
|
|
* See also gtk_check_button_get_active().
|
|
*
|
|
* Setting #GtkCheckButton:active to %TRUE will add the `:checked:` state to
|
|
* both the checkbutton and the indicator CSS node.
|
|
*/
|
|
void
|
|
gtk_check_button_set_active (GtkCheckButton *self,
|
|
gboolean setting)
|
|
{
|
|
GtkCheckButtonPrivate *priv = gtk_check_button_get_instance_private (self);
|
|
|
|
g_return_if_fail (GTK_IS_CHECK_BUTTON (self));
|
|
|
|
setting = !!setting;
|
|
|
|
if (setting == priv->active)
|
|
return;
|
|
|
|
if (setting)
|
|
{
|
|
gtk_widget_set_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_CHECKED, FALSE);
|
|
gtk_widget_set_state_flags (priv->indicator_widget, GTK_STATE_FLAG_CHECKED, FALSE);
|
|
}
|
|
else
|
|
{
|
|
gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_CHECKED);
|
|
gtk_widget_unset_state_flags (priv->indicator_widget, GTK_STATE_FLAG_CHECKED);
|
|
}
|
|
|
|
if (setting && (priv->group_prev || priv->group_next))
|
|
{
|
|
GtkCheckButton *group_first = NULL;
|
|
GtkCheckButton *iter;
|
|
|
|
group_first = get_group_first (self);
|
|
g_assert (group_first);
|
|
|
|
/* Set all buttons in group to !active */
|
|
for (iter = group_first; iter; iter = get_group_next (iter))
|
|
gtk_check_button_set_active (iter, FALSE);
|
|
|
|
/* ... and the next code block will set this one to active */
|
|
}
|
|
|
|
priv->active = setting;
|
|
update_accessible_state (self);
|
|
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_ACTIVE]);
|
|
g_signal_emit (self, signals[TOGGLED], 0);
|
|
}
|
|
|
|
/**
|
|
* gtk_check_button_get_label:
|
|
* @self: a #GtkCheckButton
|
|
*
|
|
* Returns the label of the checkbutton.
|
|
*
|
|
* Returns: (nullable) (transfer none): The label @self shows next to the indicator.
|
|
* If no label is shown, %NULL will be returned.
|
|
*/
|
|
const char *
|
|
gtk_check_button_get_label (GtkCheckButton *self)
|
|
{
|
|
GtkCheckButtonPrivate *priv = gtk_check_button_get_instance_private (self);
|
|
|
|
g_return_val_if_fail (GTK_IS_CHECK_BUTTON (self), "");
|
|
|
|
if (priv->label_widget)
|
|
return gtk_label_get_label (GTK_LABEL (priv->label_widget));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* gtk_check_button_set_label:
|
|
* @self: a #GtkCheckButton
|
|
* @label: (nullable): The text shown next to the indicator, or %NULL
|
|
* to show no text
|
|
*
|
|
* Sets the text of @self. If #GtkCheckButton:use-underline is %TRUE,
|
|
* the underscore in @label is interpreted as mnemonic indicator,
|
|
* see gtk_check_button_set_use_underline() for details on this behavior.
|
|
*
|
|
*/
|
|
void
|
|
gtk_check_button_set_label (GtkCheckButton *self,
|
|
const char *label)
|
|
{
|
|
GtkCheckButtonPrivate *priv = gtk_check_button_get_instance_private (self);
|
|
|
|
g_return_if_fail (GTK_IS_CHECK_BUTTON (self));
|
|
|
|
if (label == NULL || label[0] == '\0')
|
|
{
|
|
g_clear_pointer (&priv->label_widget, gtk_widget_unparent);
|
|
gtk_widget_remove_css_class (GTK_WIDGET (self), "text-button");
|
|
}
|
|
else
|
|
{
|
|
if (!priv->label_widget)
|
|
{
|
|
priv->label_widget = gtk_label_new (NULL);
|
|
gtk_widget_set_hexpand (priv->label_widget, TRUE);
|
|
gtk_label_set_xalign (GTK_LABEL (priv->label_widget), 0.0f);
|
|
gtk_label_set_use_underline (GTK_LABEL (priv->label_widget), priv->use_underline);
|
|
gtk_widget_insert_after (priv->label_widget, GTK_WIDGET (self), priv->indicator_widget);
|
|
}
|
|
gtk_widget_add_css_class (GTK_WIDGET (self), "text-button");
|
|
gtk_label_set_label (GTK_LABEL (priv->label_widget), label);
|
|
}
|
|
|
|
gtk_accessible_update_property (GTK_ACCESSIBLE (self),
|
|
GTK_ACCESSIBLE_PROPERTY_LABEL, label,
|
|
-1);
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_LABEL]);
|
|
}
|
|
|
|
/**
|
|
* gtk_check_button_set_group:
|
|
* @self: a #GtkCheckButton
|
|
* @group: (nullable) (transfer none): another #GtkCheckButton to
|
|
* form a group with
|
|
*
|
|
* Adds @self to the group of @group. In a group of multiple check buttons,
|
|
* only one button can be active at a time.
|
|
*
|
|
* Setting the group of a check button also changes the css name of the
|
|
* indicator widget's CSS node to 'radio'.
|
|
*
|
|
* The behavior of a checkbutton in a group is also commonly known as
|
|
* a 'radio button'.
|
|
*
|
|
* Note that the same effect can be achieved via the #GtkActionable
|
|
* api, by using the same action with parameter type and state type 's'
|
|
* for all buttons in the group, and giving each button its own target
|
|
* value.
|
|
*/
|
|
void
|
|
gtk_check_button_set_group (GtkCheckButton *self,
|
|
GtkCheckButton *group)
|
|
{
|
|
GtkCheckButtonPrivate *priv = gtk_check_button_get_instance_private (self);
|
|
GtkCheckButtonPrivate *group_priv = gtk_check_button_get_instance_private (group);
|
|
|
|
g_return_if_fail (GTK_IS_CHECK_BUTTON (self));
|
|
|
|
if (!group)
|
|
{
|
|
if (priv->group_prev)
|
|
{
|
|
GtkCheckButtonPrivate *p = gtk_check_button_get_instance_private (priv->group_prev);
|
|
p->group_next = priv->group_next;
|
|
}
|
|
if (priv->group_next)
|
|
{
|
|
GtkCheckButtonPrivate *p = gtk_check_button_get_instance_private (priv->group_next);
|
|
p->group_prev = priv->group_prev;
|
|
}
|
|
|
|
priv->group_next = NULL;
|
|
priv->group_prev = NULL;
|
|
|
|
if (priv->indicator_widget)
|
|
gtk_css_node_set_name (gtk_widget_get_css_node (priv->indicator_widget),
|
|
g_quark_from_static_string("check"));
|
|
|
|
return;
|
|
}
|
|
|
|
if (priv->group_next == group)
|
|
return;
|
|
|
|
priv->group_prev = NULL;
|
|
if (group_priv->group_prev)
|
|
{
|
|
GtkCheckButtonPrivate *prev = gtk_check_button_get_instance_private (group_priv->group_prev);
|
|
|
|
prev->group_next = self;
|
|
priv->group_prev = group_priv->group_prev;
|
|
}
|
|
|
|
group_priv->group_prev = self;
|
|
priv->group_next = group;
|
|
|
|
if (priv->indicator_widget)
|
|
gtk_css_node_set_name (gtk_widget_get_css_node (priv->indicator_widget),
|
|
g_quark_from_static_string("radio"));
|
|
|
|
gtk_css_node_set_name (gtk_widget_get_css_node (group_priv->indicator_widget),
|
|
g_quark_from_static_string("radio"));
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_GROUP]);
|
|
}
|
|
|
|
/**
|
|
* gtk_check_button_get_use_underline:
|
|
* @self: a #GtkCheckButton
|
|
*
|
|
* Returns the current value of the #GtkCheckButton:use-underline property.
|
|
*
|
|
* Returns: The value of the #GtkCheckButton:use-underline property.
|
|
* See gtk_check_button_set_use_underline() for details on how to set a new value.
|
|
*/
|
|
gboolean
|
|
gtk_check_button_get_use_underline (GtkCheckButton *self)
|
|
{
|
|
GtkCheckButtonPrivate *priv = gtk_check_button_get_instance_private (self);
|
|
|
|
g_return_val_if_fail (GTK_IS_CHECK_BUTTON (self), FALSE);
|
|
|
|
return priv->use_underline;
|
|
}
|
|
|
|
/**
|
|
* gtk_check_button_set_use_underline:
|
|
* @self: a #GtkCheckButton
|
|
* @setting: the new value to set
|
|
*
|
|
* Sets the new value of the #GtkCheckButton:use-underline property.
|
|
* See also gtk_check_button_get_use_underline().
|
|
*
|
|
* If @setting is %TRUE, an underscore character in @self's label indicates
|
|
* a mnemonic accelerator key. This behavior is similar to #GtkLabel:use-underline.
|
|
*/
|
|
void
|
|
gtk_check_button_set_use_underline (GtkCheckButton *self,
|
|
gboolean setting)
|
|
{
|
|
GtkCheckButtonPrivate *priv = gtk_check_button_get_instance_private (self);
|
|
|
|
g_return_if_fail (GTK_IS_CHECK_BUTTON (self));
|
|
|
|
setting = !!setting;
|
|
|
|
if (setting == priv->use_underline)
|
|
return;
|
|
|
|
priv->use_underline = setting;
|
|
if (priv->label_widget)
|
|
gtk_label_set_use_underline (GTK_LABEL (priv->label_widget), priv->use_underline);
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_USE_UNDERLINE]);
|
|
}
|