*
* Copyright 2026 Eva M
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "BGE::WDGT"
#include "../bge-animation-private.h"
#include "bge-marshalers.h"
#include "bge-wdgt-spec-private.h"
#include "bge.h"
#include "fmt/parser.h"
#include "graphene-gobject.h"
#include "lookup/lookup.h"
#include "util.h"
#include "util/marshal.h"
typedef enum
{
VALUE_ALLOCATION_HEIGHT = 0,
VALUE_ALLOCATION_TRANSFORM,
VALUE_ALLOCATION_WIDTH,
VALUE_CHILD,
VALUE_CLOSURE,
VALUE_COERCION,
VALUE_COMPONENT,
VALUE_CONSTANT,
VALUE_ITERATOR,
VALUE_MEASURE_FOR_SIZE,
VALUE_MEASURE_MINIMUM_HEIGHT,
VALUE_MEASURE_MINIMUM_WIDTH,
VALUE_MEASURE_NATURAL_HEIGHT,
VALUE_MEASURE_NATURAL_WIDTH,
VALUE_OBJECT,
VALUE_PATH,
VALUE_PROPERTY,
VALUE_REFERENCE_OBJECT,
VALUE_TICK_TIME,
VALUE_TRACK_TRANSITION,
VALUE_TRANSFORM,
VALUE_VARIABLE,
VALUE_WIDGET_HEIGHT,
VALUE_WIDGET_WIDTH,
} ValueKind;
typedef enum
{
TRANSITION_EASE = 0,
TRANSITION_SPRING,
} TransitionKind;
typedef struct _ForeachData ForeachData;
static void
deinit_value (gpointer ptr);
BGE_DEFINE_DATA (
value,
Value,
{
char *name;
GType type;
ValueKind kind;
union
{
struct
{
GValue value;
} constant;
struct
{
GPtrArray *params;
} component;
struct
{
ForeachData *context;
} iterator;
struct
{
TransformCallFunc func;
ValueData *next;
GPtrArray *args;
} transform;
struct
{
GPtrArray *funcs;
GPtrArray *argss;
} path;
struct
{
GPtrArray *args;
GClosureMarshal marshal;
GCallback func;
gpointer user_data;
GDestroyNotify destroy_user_data;
} closure;
struct
{
ValueData *src;
struct
{
ValueData *damping_ratio;
ValueData *mass;
ValueData *stiffness;
} spring;
} track_transition;
struct
{
ValueData *value;
} coercion;
struct
{
ValueData *object;
char *prop_name;
GParamFlags pspec_flags;
} property;
struct
{
ValueData *parent_widget;
char *builder_type;
GPtrArray *css_classes;
} child;
struct
{
ValueData *widget;
} allocation;
};
},
deinit_value (self);)
BGE_DEFINE_DATA (
foreach,
Foreach,
{
ValueData *model;
GHashTable *values;
ForeachData *parent;
},
BGE_RELEASE_DATA (model, value_data_unref);
BGE_RELEASE_DATA (values, g_hash_table_unref);
BGE_RELEASE_DATA (parent, foreach_data_unref))
static void
deinit_value (gpointer ptr)
{
ValueData *value = ptr;
g_clear_pointer (&value->name, g_free);
switch (value->kind)
{
case VALUE_CONSTANT:
g_value_unset (&value->constant.value);
break;
case VALUE_ITERATOR:
g_clear_pointer (&value->iterator.context, foreach_data_unref);
break;
case VALUE_COMPONENT:
g_clear_pointer (&value->component.params, g_ptr_array_unref);
break;
case VALUE_TRANSFORM:
g_clear_pointer (&value->transform.next, value_data_unref);
g_clear_pointer (&value->transform.args, g_ptr_array_unref);
break;
case VALUE_PATH:
g_clear_pointer (&value->path.funcs, g_ptr_array_unref);
g_clear_pointer (&value->path.argss, g_ptr_array_unref);
break;
case VALUE_CLOSURE:
g_clear_pointer (&value->closure.args, g_ptr_array_unref);
if (value->closure.destroy_user_data != NULL)
g_clear_pointer (&value->closure.user_data, value->closure.destroy_user_data);
break;
case VALUE_COERCION:
g_clear_pointer (&value->coercion.value, value_data_unref);
break;
case VALUE_TRACK_TRANSITION:
g_clear_pointer (&value->track_transition.src, value_data_unref);
g_clear_pointer (&value->track_transition.spring.damping_ratio, value_data_unref);
g_clear_pointer (&value->track_transition.spring.mass, value_data_unref);
g_clear_pointer (&value->track_transition.spring.stiffness, value_data_unref);
break;
case VALUE_PROPERTY:
g_clear_pointer (&value->property.object, value_data_unref);
g_clear_pointer (&value->property.prop_name, g_free);
break;
case VALUE_CHILD:
g_clear_pointer (&value->child.parent_widget, value_data_unref);
g_clear_pointer (&value->child.builder_type, g_free);
g_clear_pointer (&value->child.css_classes, g_ptr_array_unref);
break;
case VALUE_ALLOCATION_WIDTH:
case VALUE_ALLOCATION_HEIGHT:
case VALUE_ALLOCATION_TRANSFORM:
g_clear_pointer (&value->allocation.widget, value_data_unref);
break;
case VALUE_MEASURE_FOR_SIZE:
case VALUE_MEASURE_MINIMUM_HEIGHT:
case VALUE_MEASURE_MINIMUM_WIDTH:
case VALUE_MEASURE_NATURAL_HEIGHT:
case VALUE_MEASURE_NATURAL_WIDTH:
case VALUE_OBJECT:
case VALUE_REFERENCE_OBJECT:
case VALUE_TICK_TIME:
case VALUE_VARIABLE:
case VALUE_WIDGET_HEIGHT:
case VALUE_WIDGET_WIDTH:
break;
default:
g_assert_not_reached ();
}
}
static void
deinit_transition (gpointer ptr);
BGE_DEFINE_DATA (
transition,
Transition,
{
TransitionKind kind;
union
{
struct
{
ValueData *seconds;
ValueData *easing;
} ease;
struct
{
ValueData *damping_ratio;
ValueData *mass;
ValueData *stiffness;
} spring;
};
},
deinit_transition (self);)
static void
deinit_transition (gpointer ptr)
{
TransitionData *transition = ptr;
switch (transition->kind)
{
case TRANSITION_EASE:
g_clear_pointer (&transition->ease.seconds, value_data_unref);
g_clear_pointer (&transition->ease.easing, value_data_unref);
break;
case TRANSITION_SPRING:
g_clear_pointer (&transition->spring.damping_ratio, value_data_unref);
g_clear_pointer (&transition->spring.mass, value_data_unref);
g_clear_pointer (&transition->spring.stiffness, value_data_unref);
break;
default:
g_assert_not_reached ();
}
}
BGE_DEFINE_DATA (
snapshot_call,
SnapshotCall,
{
BgeWdgtSnapshotInstrKind kind;
SnapshotCallFunc func;
GPtrArray *args;
GPtrArray *rest;
ValueData *child;
ForeachData *foreach_context;
},
BGE_RELEASE_DATA (args, g_ptr_array_unref);
BGE_RELEASE_DATA (rest, g_ptr_array_unref);
BGE_RELEASE_DATA (child, value_data_unref);
BGE_RELEASE_DATA (foreach_context, foreach_data_unref))
BGE_DEFINE_DATA (
snapshot,
Snapshot,
{
GPtrArray *calls;
},
BGE_RELEASE_DATA (calls, g_ptr_array_unref))
BGE_DEFINE_DATA (
state,
State,
{
char *name;
GHashTable *setters;
GHashTable *transitions;
SnapshotData *snapshot;
GHashTable *expressions;
GPtrArray *snapshot_deps;
GHashTable *values_need_tick;
gboolean needs_tick;
},
BGE_RELEASE_DATA (name, g_free);
BGE_RELEASE_DATA (setters, g_hash_table_unref);
BGE_RELEASE_DATA (transitions, g_hash_table_unref);
BGE_RELEASE_DATA (snapshot, snapshot_data_unref);
BGE_RELEASE_DATA (expressions, g_hash_table_unref);
BGE_RELEASE_DATA (snapshot_deps, g_ptr_array_unref);
BGE_RELEASE_DATA (values_need_tick, g_hash_table_unref););
struct _BgeWdgtSpec
{
GObject parent_instance;
char *name;
GtkStringList *state_names;
GHashTable *values;
GPtrArray *values_stack;
GPtrArray *foreaches;
GPtrArray *foreach_stack;
GPtrArray *anon_values;
GPtrArray *track_transition_values;
GHashTable *states;
GPtrArray *children;
GPtrArray *nonchildren;
StateData *init_state;
StateData *default_state;
ValueData *reference;
gboolean ready;
};
static void
list_model_iface_init (GListModelInterface *iface);
G_DEFINE_FINAL_TYPE_WITH_CODE (
BgeWdgtSpec,
bge_wdgt_spec,
G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init));
enum
{
PROP_0,
PROP_NAME,
LAST_PROP
};
static GParamSpec *props[LAST_PROP] = { 0 };
static gboolean
check_can_coerce_type (GType dest,
GType src);
static void
coerce_value (const GValue *in,
GType dest_type,
GValue *out);
static ValueData *
wrap_coerce_value (BgeWdgtSpec *self,
ValueData *value,
GType dest_type);
static void
ensure_state_snapshot (StateData *state);
static void
bge_wdgt_spec_dispose (GObject *object)
{
BgeWdgtSpec *self = BGE_WDGT_SPEC (object);
g_clear_pointer (&self->name, g_free);
g_clear_object (&self->state_names);
g_clear_pointer (&self->values, g_hash_table_unref);
g_clear_pointer (&self->values_stack, g_ptr_array_unref);
g_clear_pointer (&self->foreaches, g_ptr_array_unref);
g_clear_pointer (&self->foreach_stack, g_ptr_array_unref);
g_clear_pointer (&self->anon_values, g_ptr_array_unref);
g_clear_pointer (&self->track_transition_values, g_ptr_array_unref);
g_clear_pointer (&self->states, g_hash_table_unref);
g_clear_pointer (&self->children, g_ptr_array_unref);
g_clear_pointer (&self->nonchildren, g_ptr_array_unref);
g_clear_pointer (&self->init_state, state_data_unref);
g_clear_pointer (&self->default_state, state_data_unref);
g_clear_pointer (&self->reference, value_data_unref);
G_OBJECT_CLASS (bge_wdgt_spec_parent_class)->dispose (object);
}
static void
bge_wdgt_spec_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BgeWdgtSpec *self = BGE_WDGT_SPEC (object);
switch (prop_id)
{
case PROP_NAME:
g_value_set_string (value, bge_wdgt_spec_get_name (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bge_wdgt_spec_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BgeWdgtSpec *self = BGE_WDGT_SPEC (object);
switch (prop_id)
{
case PROP_NAME:
bge_wdgt_spec_set_name (self, g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bge_wdgt_spec_class_init (BgeWdgtSpecClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->set_property = bge_wdgt_spec_set_property;
object_class->get_property = bge_wdgt_spec_get_property;
object_class->dispose = bge_wdgt_spec_dispose;
props[PROP_NAME] =
g_param_spec_string (
"name",
NULL, NULL, NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, LAST_PROP, props);
g_type_ensure (BGE_TYPE_EASING);
g_type_ensure (GRAPHENE_TYPE_POINT);
g_type_ensure (GRAPHENE_TYPE_POINT3D);
g_type_ensure (GRAPHENE_TYPE_SIZE);
g_type_ensure (GRAPHENE_TYPE_RECT);
g_type_ensure (GRAPHENE_TYPE_VEC2);
g_type_ensure (GRAPHENE_TYPE_VEC3);
g_type_ensure (GRAPHENE_TYPE_VEC4);
g_type_ensure (GRAPHENE_TYPE_QUAD);
g_type_ensure (GRAPHENE_TYPE_QUATERNION);
g_type_ensure (GRAPHENE_TYPE_MATRIX);
g_type_ensure (GRAPHENE_TYPE_PLANE);
g_type_ensure (GRAPHENE_TYPE_FRUSTUM);
g_type_ensure (GRAPHENE_TYPE_SPHERE);
g_type_ensure (GRAPHENE_TYPE_BOX);
g_type_ensure (GRAPHENE_TYPE_TRIANGLE);
g_type_ensure (GRAPHENE_TYPE_EULER);
g_type_ensure (GRAPHENE_TYPE_RAY);
g_type_ensure (GSK_TYPE_PATH);
g_type_ensure (GSK_TYPE_PATH_POINT);
g_type_ensure (GSK_TYPE_PATH_MEASURE);
g_type_ensure (GSK_TYPE_TRANSFORM);
g_type_ensure (GSK_TYPE_FILL_RULE);
g_type_ensure (GSK_TYPE_LINE_CAP);
g_type_ensure (GSK_TYPE_LINE_JOIN);
g_type_ensure (gtk_at_context_get_type ());
g_type_ensure (gtk_about_dialog_get_type ());
g_type_ensure (gtk_accessible_get_type ());
g_type_ensure (gtk_accessible_announcement_priority_get_type ());
g_type_ensure (gtk_accessible_autocomplete_get_type ());
g_type_ensure (gtk_accessible_invalid_state_get_type ());
g_type_ensure (gtk_accessible_list_get_type ());
g_type_ensure (gtk_accessible_platform_state_get_type ());
g_type_ensure (gtk_accessible_property_get_type ());
g_type_ensure (gtk_accessible_range_get_type ());
g_type_ensure (gtk_accessible_relation_get_type ());
g_type_ensure (gtk_accessible_role_get_type ());
g_type_ensure (gtk_accessible_sort_get_type ());
g_type_ensure (gtk_accessible_state_get_type ());
g_type_ensure (gtk_accessible_text_get_type ());
g_type_ensure (gtk_accessible_text_content_change_get_type ());
g_type_ensure (gtk_accessible_text_granularity_get_type ());
g_type_ensure (gtk_accessible_tristate_get_type ());
g_type_ensure (gtk_action_bar_get_type ());
g_type_ensure (gtk_actionable_get_type ());
g_type_ensure (gtk_activate_action_get_type ());
g_type_ensure (gtk_adjustment_get_type ());
g_type_ensure (gtk_alert_dialog_get_type ());
g_type_ensure (gtk_align_get_type ());
g_type_ensure (gtk_alternative_trigger_get_type ());
g_type_ensure (gtk_any_filter_get_type ());
g_type_ensure (gtk_app_chooser_get_type ());
g_type_ensure (gtk_app_chooser_button_get_type ());
g_type_ensure (gtk_app_chooser_dialog_get_type ());
g_type_ensure (gtk_app_chooser_widget_get_type ());
g_type_ensure (gtk_application_get_type ());
g_type_ensure (gtk_application_inhibit_flags_get_type ());
g_type_ensure (gtk_application_window_get_type ());
g_type_ensure (gtk_arrow_type_get_type ());
g_type_ensure (gtk_aspect_frame_get_type ());
g_type_ensure (gtk_assistant_get_type ());
g_type_ensure (gtk_assistant_page_get_type ());
g_type_ensure (gtk_assistant_page_type_get_type ());
g_type_ensure (gtk_baseline_position_get_type ());
g_type_ensure (gtk_bin_layout_get_type ());
g_type_ensure (gtk_bitset_get_type ());
g_type_ensure (gtk_bitset_iter_get_type ());
g_type_ensure (gtk_bookmark_list_get_type ());
g_type_ensure (gtk_bool_filter_get_type ());
g_type_ensure (gtk_border_get_type ());
g_type_ensure (gtk_border_style_get_type ());
g_type_ensure (gtk_box_get_type ());
g_type_ensure (gtk_box_layout_get_type ());
g_type_ensure (gtk_buildable_get_type ());
g_type_ensure (gtk_builder_get_type ());
g_type_ensure (gtk_builder_cscope_get_type ());
g_type_ensure (gtk_builder_closure_flags_get_type ());
g_type_ensure (gtk_builder_error_get_type ());
g_type_ensure (gtk_builder_list_item_factory_get_type ());
g_type_ensure (gtk_builder_scope_get_type ());
g_type_ensure (gtk_button_get_type ());
g_type_ensure (gtk_buttons_type_get_type ());
g_type_ensure (gtk_cclosure_expression_get_type ());
g_type_ensure (gtk_calendar_get_type ());
g_type_ensure (gtk_callback_action_get_type ());
g_type_ensure (gtk_cell_area_get_type ());
g_type_ensure (gtk_cell_area_box_get_type ());
g_type_ensure (gtk_cell_area_context_get_type ());
g_type_ensure (gtk_cell_editable_get_type ());
g_type_ensure (gtk_cell_layout_get_type ());
g_type_ensure (gtk_cell_renderer_get_type ());
g_type_ensure (gtk_cell_renderer_accel_get_type ());
g_type_ensure (gtk_cell_renderer_accel_mode_get_type ());
g_type_ensure (gtk_cell_renderer_combo_get_type ());
g_type_ensure (gtk_cell_renderer_mode_get_type ());
g_type_ensure (gtk_cell_renderer_pixbuf_get_type ());
g_type_ensure (gtk_cell_renderer_progress_get_type ());
g_type_ensure (gtk_cell_renderer_spin_get_type ());
g_type_ensure (gtk_cell_renderer_spinner_get_type ());
g_type_ensure (gtk_cell_renderer_state_get_type ());
g_type_ensure (gtk_cell_renderer_text_get_type ());
g_type_ensure (gtk_cell_renderer_toggle_get_type ());
g_type_ensure (gtk_cell_view_get_type ());
g_type_ensure (gtk_center_box_get_type ());
g_type_ensure (gtk_center_layout_get_type ());
g_type_ensure (gtk_check_button_get_type ());
g_type_ensure (gtk_closure_expression_get_type ());
g_type_ensure (gtk_collation_get_type ());
g_type_ensure (gtk_color_button_get_type ());
g_type_ensure (gtk_color_chooser_get_type ());
g_type_ensure (gtk_color_chooser_dialog_get_type ());
g_type_ensure (gtk_color_chooser_widget_get_type ());
g_type_ensure (gtk_color_dialog_get_type ());
g_type_ensure (gtk_color_dialog_button_get_type ());
g_type_ensure (gtk_column_view_get_type ());
g_type_ensure (gtk_column_view_cell_get_type ());
g_type_ensure (gtk_column_view_column_get_type ());
g_type_ensure (gtk_column_view_row_get_type ());
g_type_ensure (gtk_column_view_sorter_get_type ());
g_type_ensure (gtk_combo_box_get_type ());
g_type_ensure (gtk_combo_box_text_get_type ());
g_type_ensure (gtk_constant_expression_get_type ());
g_type_ensure (gtk_constraint_get_type ());
g_type_ensure (gtk_constraint_attribute_get_type ());
g_type_ensure (gtk_constraint_guide_get_type ());
g_type_ensure (gtk_constraint_layout_get_type ());
g_type_ensure (gtk_constraint_layout_child_get_type ());
g_type_ensure (gtk_constraint_relation_get_type ());
g_type_ensure (gtk_constraint_strength_get_type ());
g_type_ensure (gtk_constraint_target_get_type ());
g_type_ensure (gtk_constraint_vfl_parser_error_get_type ());
g_type_ensure (gtk_content_fit_get_type ());
g_type_ensure (gtk_corner_type_get_type ());
g_type_ensure (gtk_css_provider_get_type ());
g_type_ensure (gtk_css_section_get_type ());
g_type_ensure (gtk_custom_filter_get_type ());
g_type_ensure (gtk_custom_layout_get_type ());
g_type_ensure (gtk_custom_sorter_get_type ());
g_type_ensure (gtk_debug_flags_get_type ());
g_type_ensure (gtk_delete_type_get_type ());
g_type_ensure (gtk_dialog_get_type ());
g_type_ensure (gtk_dialog_error_get_type ());
g_type_ensure (gtk_dialog_flags_get_type ());
g_type_ensure (gtk_direction_type_get_type ());
g_type_ensure (gtk_directory_list_get_type ());
g_type_ensure (gtk_drag_icon_get_type ());
g_type_ensure (gtk_drag_source_get_type ());
g_type_ensure (gtk_drawing_area_get_type ());
g_type_ensure (gtk_drop_controller_motion_get_type ());
g_type_ensure (gtk_drop_down_get_type ());
g_type_ensure (gtk_drop_target_get_type ());
g_type_ensure (gtk_drop_target_async_get_type ());
g_type_ensure (gtk_editable_get_type ());
g_type_ensure (gtk_editable_label_get_type ());
g_type_ensure (gtk_editable_properties_get_type ());
g_type_ensure (gtk_emoji_chooser_get_type ());
g_type_ensure (gtk_entry_get_type ());
g_type_ensure (gtk_entry_buffer_get_type ());
g_type_ensure (gtk_entry_completion_get_type ());
g_type_ensure (gtk_entry_icon_position_get_type ());
g_type_ensure (gtk_event_controller_get_type ());
g_type_ensure (gtk_event_controller_focus_get_type ());
g_type_ensure (gtk_event_controller_key_get_type ());
g_type_ensure (gtk_event_controller_legacy_get_type ());
g_type_ensure (gtk_event_controller_motion_get_type ());
g_type_ensure (gtk_event_controller_scroll_get_type ());
g_type_ensure (gtk_event_controller_scroll_flags_get_type ());
g_type_ensure (gtk_event_sequence_state_get_type ());
g_type_ensure (gtk_every_filter_get_type ());
g_type_ensure (gtk_expander_get_type ());
g_type_ensure (gtk_expression_get_type ());
g_type_ensure (gtk_expression_watch_get_type ());
g_type_ensure (gtk_file_chooser_get_type ());
g_type_ensure (gtk_file_chooser_action_get_type ());
g_type_ensure (gtk_file_chooser_dialog_get_type ());
g_type_ensure (gtk_file_chooser_error_get_type ());
g_type_ensure (gtk_file_chooser_native_get_type ());
g_type_ensure (gtk_file_chooser_widget_get_type ());
g_type_ensure (gtk_file_dialog_get_type ());
g_type_ensure (gtk_file_filter_get_type ());
g_type_ensure (gtk_file_launcher_get_type ());
g_type_ensure (gtk_filter_get_type ());
g_type_ensure (gtk_filter_change_get_type ());
g_type_ensure (gtk_filter_list_model_get_type ());
g_type_ensure (gtk_filter_match_get_type ());
g_type_ensure (gtk_fixed_get_type ());
g_type_ensure (gtk_fixed_layout_get_type ());
g_type_ensure (gtk_fixed_layout_child_get_type ());
g_type_ensure (gtk_flatten_list_model_get_type ());
g_type_ensure (gtk_flow_box_get_type ());
g_type_ensure (gtk_flow_box_child_get_type ());
g_type_ensure (gtk_font_button_get_type ());
g_type_ensure (gtk_font_chooser_get_type ());
g_type_ensure (gtk_font_chooser_dialog_get_type ());
g_type_ensure (gtk_font_chooser_level_get_type ());
g_type_ensure (gtk_font_chooser_widget_get_type ());
g_type_ensure (gtk_font_dialog_get_type ());
g_type_ensure (gtk_font_dialog_button_get_type ());
g_type_ensure (gtk_font_level_get_type ());
g_type_ensure (gtk_font_rendering_get_type ());
g_type_ensure (gtk_frame_get_type ());
g_type_ensure (gtk_gl_area_get_type ());
g_type_ensure (gtk_gesture_get_type ());
g_type_ensure (gtk_gesture_click_get_type ());
g_type_ensure (gtk_gesture_drag_get_type ());
g_type_ensure (gtk_gesture_long_press_get_type ());
g_type_ensure (gtk_gesture_pan_get_type ());
g_type_ensure (gtk_gesture_rotate_get_type ());
g_type_ensure (gtk_gesture_single_get_type ());
g_type_ensure (gtk_gesture_stylus_get_type ());
g_type_ensure (gtk_gesture_swipe_get_type ());
g_type_ensure (gtk_gesture_zoom_get_type ());
g_type_ensure (gtk_graphics_offload_get_type ());
g_type_ensure (gtk_graphics_offload_enabled_get_type ());
g_type_ensure (gtk_grid_get_type ());
g_type_ensure (gtk_grid_layout_get_type ());
g_type_ensure (gtk_grid_layout_child_get_type ());
g_type_ensure (gtk_grid_view_get_type ());
g_type_ensure (gtk_header_bar_get_type ());
g_type_ensure (gtk_im_context_get_type ());
g_type_ensure (gtk_im_context_simple_get_type ());
g_type_ensure (gtk_im_multicontext_get_type ());
g_type_ensure (gtk_icon_lookup_flags_get_type ());
g_type_ensure (gtk_icon_paintable_get_type ());
g_type_ensure (gtk_icon_size_get_type ());
g_type_ensure (gtk_icon_theme_get_type ());
g_type_ensure (gtk_icon_theme_error_get_type ());
g_type_ensure (gtk_icon_view_get_type ());
g_type_ensure (gtk_icon_view_drop_position_get_type ());
g_type_ensure (gtk_image_get_type ());
g_type_ensure (gtk_image_type_get_type ());
g_type_ensure (gtk_info_bar_get_type ());
g_type_ensure (gtk_input_hints_get_type ());
g_type_ensure (gtk_input_purpose_get_type ());
g_type_ensure (gtk_inscription_get_type ());
g_type_ensure (gtk_inscription_overflow_get_type ());
g_type_ensure (gtk_interface_color_scheme_get_type ());
g_type_ensure (gtk_interface_contrast_get_type ());
g_type_ensure (gtk_justification_get_type ());
g_type_ensure (gtk_keyval_trigger_get_type ());
g_type_ensure (gtk_label_get_type ());
g_type_ensure (gtk_layout_child_get_type ());
g_type_ensure (gtk_layout_manager_get_type ());
g_type_ensure (gtk_level_bar_get_type ());
g_type_ensure (gtk_level_bar_mode_get_type ());
g_type_ensure (gtk_license_get_type ());
g_type_ensure (gtk_link_button_get_type ());
g_type_ensure (gtk_list_base_get_type ());
g_type_ensure (gtk_list_box_get_type ());
g_type_ensure (gtk_list_box_row_get_type ());
g_type_ensure (gtk_list_header_get_type ());
g_type_ensure (gtk_list_item_get_type ());
g_type_ensure (gtk_list_item_factory_get_type ());
g_type_ensure (gtk_list_scroll_flags_get_type ());
g_type_ensure (gtk_list_store_get_type ());
g_type_ensure (gtk_list_tab_behavior_get_type ());
g_type_ensure (gtk_list_view_get_type ());
g_type_ensure (gtk_lock_button_get_type ());
g_type_ensure (gtk_map_list_model_get_type ());
g_type_ensure (gtk_media_controls_get_type ());
g_type_ensure (gtk_media_file_get_type ());
g_type_ensure (gtk_media_stream_get_type ());
g_type_ensure (gtk_menu_button_get_type ());
g_type_ensure (gtk_message_dialog_get_type ());
g_type_ensure (gtk_message_type_get_type ());
g_type_ensure (gtk_mnemonic_action_get_type ());
g_type_ensure (gtk_mnemonic_trigger_get_type ());
g_type_ensure (gtk_mount_operation_get_type ());
g_type_ensure (gtk_movement_step_get_type ());
g_type_ensure (gtk_multi_filter_get_type ());
g_type_ensure (gtk_multi_selection_get_type ());
g_type_ensure (gtk_multi_sorter_get_type ());
g_type_ensure (gtk_named_action_get_type ());
g_type_ensure (gtk_native_get_type ());
g_type_ensure (gtk_native_dialog_get_type ());
g_type_ensure (gtk_natural_wrap_mode_get_type ());
g_type_ensure (gtk_never_trigger_get_type ());
g_type_ensure (gtk_no_selection_get_type ());
g_type_ensure (gtk_notebook_get_type ());
g_type_ensure (gtk_notebook_page_get_type ());
g_type_ensure (gtk_notebook_tab_get_type ());
g_type_ensure (gtk_nothing_action_get_type ());
g_type_ensure (gtk_number_up_layout_get_type ());
g_type_ensure (gtk_numeric_sorter_get_type ());
g_type_ensure (gtk_object_expression_get_type ());
g_type_ensure (gtk_ordering_get_type ());
g_type_ensure (gtk_orientable_get_type ());
g_type_ensure (gtk_orientation_get_type ());
g_type_ensure (gtk_overflow_get_type ());
g_type_ensure (gtk_overlay_get_type ());
g_type_ensure (gtk_overlay_layout_get_type ());
g_type_ensure (gtk_overlay_layout_child_get_type ());
g_type_ensure (gtk_pack_type_get_type ());
g_type_ensure (gtk_pad_action_type_get_type ());
g_type_ensure (gtk_pad_controller_get_type ());
g_type_ensure (gtk_page_orientation_get_type ());
g_type_ensure (gtk_page_set_get_type ());
g_type_ensure (gtk_page_setup_get_type ());
g_type_ensure (gtk_pan_direction_get_type ());
g_type_ensure (gtk_paned_get_type ());
g_type_ensure (gtk_paper_size_get_type ());
g_type_ensure (gtk_param_expression_get_type ());
g_type_ensure (gtk_password_entry_get_type ());
g_type_ensure (gtk_password_entry_buffer_get_type ());
g_type_ensure (gtk_pick_flags_get_type ());
g_type_ensure (gtk_picture_get_type ());
g_type_ensure (gtk_policy_type_get_type ());
g_type_ensure (gtk_popover_get_type ());
g_type_ensure (gtk_popover_menu_get_type ());
g_type_ensure (gtk_popover_menu_bar_get_type ());
g_type_ensure (gtk_popover_menu_flags_get_type ());
g_type_ensure (gtk_position_type_get_type ());
g_type_ensure (gtk_print_context_get_type ());
g_type_ensure (gtk_print_dialog_get_type ());
g_type_ensure (gtk_print_duplex_get_type ());
g_type_ensure (gtk_print_error_get_type ());
g_type_ensure (gtk_print_operation_get_type ());
g_type_ensure (gtk_print_operation_action_get_type ());
g_type_ensure (gtk_print_operation_preview_get_type ());
g_type_ensure (gtk_print_operation_result_get_type ());
g_type_ensure (gtk_print_pages_get_type ());
g_type_ensure (gtk_print_quality_get_type ());
g_type_ensure (gtk_print_settings_get_type ());
g_type_ensure (gtk_print_setup_get_type ());
g_type_ensure (gtk_print_status_get_type ());
g_type_ensure (gtk_progress_bar_get_type ());
g_type_ensure (gtk_propagation_limit_get_type ());
g_type_ensure (gtk_propagation_phase_get_type ());
g_type_ensure (gtk_property_expression_get_type ());
g_type_ensure (gtk_range_get_type ());
g_type_ensure (gtk_recent_info_get_type ());
g_type_ensure (gtk_recent_manager_get_type ());
g_type_ensure (gtk_recent_manager_error_get_type ());
g_type_ensure (gtk_requisition_get_type ());
g_type_ensure (gtk_response_type_get_type ());
g_type_ensure (gtk_revealer_get_type ());
g_type_ensure (gtk_revealer_transition_type_get_type ());
g_type_ensure (gtk_root_get_type ());
g_type_ensure (gtk_scale_get_type ());
g_type_ensure (gtk_scale_button_get_type ());
g_type_ensure (gtk_scroll_info_get_type ());
g_type_ensure (gtk_scroll_step_get_type ());
g_type_ensure (gtk_scroll_type_get_type ());
g_type_ensure (gtk_scrollable_get_type ());
g_type_ensure (gtk_scrollable_policy_get_type ());
g_type_ensure (gtk_scrollbar_get_type ());
g_type_ensure (gtk_scrolled_window_get_type ());
g_type_ensure (gtk_search_bar_get_type ());
g_type_ensure (gtk_search_entry_get_type ());
g_type_ensure (gtk_section_model_get_type ());
g_type_ensure (gtk_selection_filter_model_get_type ());
g_type_ensure (gtk_selection_mode_get_type ());
g_type_ensure (gtk_selection_model_get_type ());
g_type_ensure (gtk_sensitivity_type_get_type ());
g_type_ensure (gtk_separator_get_type ());
g_type_ensure (gtk_settings_get_type ());
g_type_ensure (gtk_shortcut_get_type ());
g_type_ensure (gtk_shortcut_action_get_type ());
g_type_ensure (gtk_shortcut_action_flags_get_type ());
g_type_ensure (gtk_shortcut_controller_get_type ());
g_type_ensure (gtk_shortcut_label_get_type ());
g_type_ensure (gtk_shortcut_manager_get_type ());
g_type_ensure (gtk_shortcut_scope_get_type ());
g_type_ensure (gtk_shortcut_trigger_get_type ());
g_type_ensure (gtk_shortcut_type_get_type ());
g_type_ensure (gtk_shortcuts_group_get_type ());
g_type_ensure (gtk_shortcuts_section_get_type ());
g_type_ensure (gtk_shortcuts_shortcut_get_type ());
g_type_ensure (gtk_shortcuts_window_get_type ());
g_type_ensure (gtk_signal_action_get_type ());
g_type_ensure (gtk_signal_list_item_factory_get_type ());
g_type_ensure (gtk_single_selection_get_type ());
g_type_ensure (gtk_size_group_get_type ());
g_type_ensure (gtk_size_group_mode_get_type ());
g_type_ensure (gtk_size_request_mode_get_type ());
g_type_ensure (gtk_slice_list_model_get_type ());
g_type_ensure (gtk_snapshot_get_type ());
g_type_ensure (gtk_sort_list_model_get_type ());
g_type_ensure (gtk_sort_type_get_type ());
g_type_ensure (gtk_sorter_get_type ());
g_type_ensure (gtk_sorter_change_get_type ());
g_type_ensure (gtk_sorter_order_get_type ());
g_type_ensure (gtk_spin_button_get_type ());
g_type_ensure (gtk_spin_button_update_policy_get_type ());
g_type_ensure (gtk_spin_type_get_type ());
g_type_ensure (gtk_spinner_get_type ());
g_type_ensure (gtk_stack_get_type ());
g_type_ensure (gtk_stack_page_get_type ());
g_type_ensure (gtk_stack_sidebar_get_type ());
g_type_ensure (gtk_stack_switcher_get_type ());
g_type_ensure (gtk_stack_transition_type_get_type ());
g_type_ensure (gtk_state_flags_get_type ());
g_type_ensure (gtk_statusbar_get_type ());
g_type_ensure (gtk_string_filter_get_type ());
g_type_ensure (gtk_string_filter_match_mode_get_type ());
g_type_ensure (gtk_string_list_get_type ());
g_type_ensure (gtk_string_object_get_type ());
g_type_ensure (gtk_string_sorter_get_type ());
g_type_ensure (gtk_style_context_get_type ());
g_type_ensure (gtk_style_context_print_flags_get_type ());
g_type_ensure (gtk_style_provider_get_type ());
g_type_ensure (gtk_switch_get_type ());
g_type_ensure (gtk_symbolic_color_get_type ());
g_type_ensure (gtk_symbolic_paintable_get_type ());
g_type_ensure (gtk_system_setting_get_type ());
g_type_ensure (gtk_text_get_type ());
g_type_ensure (gtk_text_buffer_get_type ());
g_type_ensure (gtk_text_buffer_notify_flags_get_type ());
g_type_ensure (gtk_text_child_anchor_get_type ());
g_type_ensure (gtk_text_direction_get_type ());
g_type_ensure (gtk_text_extend_selection_get_type ());
g_type_ensure (gtk_text_iter_get_type ());
g_type_ensure (gtk_text_mark_get_type ());
g_type_ensure (gtk_text_search_flags_get_type ());
g_type_ensure (gtk_text_tag_get_type ());
g_type_ensure (gtk_text_tag_table_get_type ());
g_type_ensure (gtk_text_view_get_type ());
g_type_ensure (gtk_text_view_layer_get_type ());
g_type_ensure (gtk_text_window_type_get_type ());
g_type_ensure (gtk_toggle_button_get_type ());
g_type_ensure (gtk_tooltip_get_type ());
g_type_ensure (gtk_tree_expander_get_type ());
g_type_ensure (gtk_tree_iter_get_type ());
g_type_ensure (gtk_tree_list_model_get_type ());
g_type_ensure (gtk_tree_list_row_get_type ());
g_type_ensure (gtk_tree_list_row_sorter_get_type ());
g_type_ensure (gtk_tree_model_get_type ());
g_type_ensure (gtk_tree_model_filter_get_type ());
g_type_ensure (gtk_tree_model_flags_get_type ());
g_type_ensure (gtk_tree_model_sort_get_type ());
g_type_ensure (gtk_tree_path_get_type ());
g_type_ensure (gtk_tree_row_reference_get_type ());
g_type_ensure (gtk_tree_selection_get_type ());
g_type_ensure (gtk_tree_sortable_get_type ());
g_type_ensure (gtk_tree_store_get_type ());
g_type_ensure (gtk_tree_view_get_type ());
g_type_ensure (gtk_tree_view_column_get_type ());
g_type_ensure (gtk_tree_view_column_sizing_get_type ());
g_type_ensure (gtk_tree_view_drop_position_get_type ());
g_type_ensure (gtk_tree_view_grid_lines_get_type ());
g_type_ensure (gtk_unit_get_type ());
g_type_ensure (gtk_uri_launcher_get_type ());
g_type_ensure (gtk_video_get_type ());
g_type_ensure (gtk_viewport_get_type ());
g_type_ensure (gtk_volume_button_get_type ());
g_type_ensure (gtk_widget_get_type ());
g_type_ensure (gtk_widget_paintable_get_type ());
g_type_ensure (gtk_window_get_type ());
g_type_ensure (gtk_window_controls_get_type ());
g_type_ensure (gtk_window_gravity_get_type ());
g_type_ensure (gtk_window_group_get_type ());
g_type_ensure (gtk_window_handle_get_type ());
g_type_ensure (gtk_wrap_mode_get_type ());
}
static void
bge_wdgt_spec_init (BgeWdgtSpec *self)
{
self->state_names = gtk_string_list_new (NULL);
self->values = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, value_data_unref);
self->values_stack = g_ptr_array_new_with_free_func ((GDestroyNotify) g_hash_table_unref);
self->foreaches = g_ptr_array_new_with_free_func (foreach_data_unref);
self->foreach_stack = g_ptr_array_new_with_free_func (foreach_data_unref);
self->anon_values = g_ptr_array_new_with_free_func (value_data_unref);
self->track_transition_values = g_ptr_array_new_with_free_func (value_data_unref);
self->states = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, state_data_unref);
self->children = g_ptr_array_new_with_free_func (value_data_unref);
self->nonchildren = g_ptr_array_new_with_free_func (value_data_unref);
self->init_state = state_data_new ();
self->init_state->name = g_strdup ("init");
self->init_state->setters = g_hash_table_new_full (
g_direct_hash, g_direct_equal,
value_data_unref, value_data_unref);
self->init_state->transitions = g_hash_table_new_full (
g_direct_hash, g_direct_equal,
value_data_unref, transition_data_unref);
self->init_state->expressions = g_hash_table_new_full (
g_direct_hash, g_direct_equal,
value_data_unref, (GDestroyNotify) gtk_expression_unref);
self->init_state->snapshot_deps = g_ptr_array_new_with_free_func (
(GDestroyNotify) gtk_expression_unref);
self->init_state->values_need_tick = g_hash_table_new_full (
g_direct_hash, g_direct_equal,
value_data_unref, NULL);
ensure_state_snapshot (self->init_state);
g_hash_table_replace (self->states,
g_strdup ("init"),
state_data_ref (self->init_state));
self->ready = FALSE;
}
static GType
list_model_get_item_type (GListModel *list)
{
return GTK_TYPE_STRING_OBJECT;
}
static guint
list_model_get_n_items (GListModel *list)
{
BgeWdgtSpec *self = BGE_WDGT_SPEC (list);
return g_list_model_get_n_items (G_LIST_MODEL (self->state_names));
}
static gpointer
list_model_get_item (GListModel *list,
guint position)
{
BgeWdgtSpec *self = BGE_WDGT_SPEC (list);
return g_list_model_get_item (G_LIST_MODEL (self->state_names), position);
}
static void
list_model_iface_init (GListModelInterface *iface)
{
iface->get_item_type = list_model_get_item_type;
iface->get_n_items = list_model_get_n_items;
iface->get_item = list_model_get_item;
}
BgeWdgtSpec *
bge_wdgt_spec_new (void)
{
return g_object_new (BGE_TYPE_WDGT_SPEC, NULL);
}
BgeWdgtSpec *
bge_wdgt_spec_new_for_string (const char *string,
GError **error)
{
g_return_val_if_fail (string != NULL, NULL);
return bge_wdgt_parse_string (string, error);
}
BgeWdgtSpec *
bge_wdgt_spec_new_for_resource (const char *resource,
GError **error)
{
g_autoptr (GBytes) bytes = NULL;
gsize size = 0;
gconstpointer data = NULL;
g_return_val_if_fail (resource != NULL, NULL);
bytes = g_resources_lookup_data (resource, G_RESOURCE_LOOKUP_FLAGS_NONE, error);
if (bytes == NULL)
return NULL;
data = g_bytes_get_data (bytes, &size);
return bge_wdgt_parse_string ((const char *) data, error);
}
const char *
bge_wdgt_spec_get_name (BgeWdgtSpec *self)
{
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), NULL);
return self->name;
}
void
bge_wdgt_spec_set_name (BgeWdgtSpec *self,
const char *name)
{
g_return_if_fail (BGE_IS_WDGT_SPEC (self));
if (name == self->name || (name != NULL && self->name != NULL && g_strcmp0 (name, self->name) == 0))
return;
g_clear_pointer (&self->name, g_free);
if (name != NULL)
self->name = g_strdup (name);
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_NAME]);
}
void
bge_wdgt_spec_set_name_take (BgeWdgtSpec *self,
char *name)
{
g_return_if_fail (BGE_IS_WDGT_SPEC (self));
if (name != NULL && self->name != NULL && g_strcmp0 (name, self->name) == 0)
{
g_free (name);
return;
}
g_clear_pointer (&self->name, g_free);
if (name != NULL)
self->name = name;
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_NAME]);
}
gboolean
bge_wdgt_spec_add_constant_source_value (BgeWdgtSpec *self,
const char *name,
const GValue *constant,
GError **error)
{
g_autoptr (ValueData) value = NULL;
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (constant != NULL, FALSE);
if (g_hash_table_contains (self->values, name))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' already exists", name);
return FALSE;
}
value = value_data_new ();
value->name = g_strdup (name);
value->type = constant->g_type;
value->kind = VALUE_CONSTANT;
g_value_copy (constant, g_value_init (&value->constant.value, constant->g_type));
g_hash_table_replace (self->values, g_strdup (name), value_data_ref (value));
return TRUE;
}
gboolean
bge_wdgt_spec_add_component_source_value (BgeWdgtSpec *self,
const char *name,
GType type,
const char *const *components,
guint n_components,
GError **error)
{
GType expected_types[ARGBUF_SIZE] = { 0 };
g_autoptr (ValueData) value = NULL;
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (components != NULL, FALSE);
if (g_hash_table_contains (self->values, name))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' already exists", name);
return FALSE;
}
if (type == GRAPHENE_TYPE_POINT)
{
if (n_components != 2)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"composed point value needs 2 arguments");
return FALSE;
}
expected_types[0] = G_TYPE_DOUBLE;
expected_types[1] = G_TYPE_DOUBLE;
}
else if (type == GRAPHENE_TYPE_POINT3D)
{
if (n_components != 3)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"composed 3d point value needs 3 arguments");
return FALSE;
}
expected_types[0] = G_TYPE_DOUBLE;
expected_types[1] = G_TYPE_DOUBLE;
expected_types[2] = G_TYPE_DOUBLE;
}
else if (type == GRAPHENE_TYPE_SIZE)
{
if (n_components != 2)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"composed 2d size value needs 2 arguments");
return FALSE;
}
expected_types[0] = G_TYPE_DOUBLE;
expected_types[1] = G_TYPE_DOUBLE;
}
else if (type == GRAPHENE_TYPE_RECT)
{
if (n_components != 4)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"composed rectangle value needs 4 arguments");
return FALSE;
}
expected_types[0] = G_TYPE_DOUBLE;
expected_types[1] = G_TYPE_DOUBLE;
expected_types[2] = G_TYPE_DOUBLE;
expected_types[3] = G_TYPE_DOUBLE;
}
else if (type == GSK_TYPE_STROKE)
{
if (n_components != 4)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"composed rectangle value needs 4 arguments");
return FALSE;
}
expected_types[0] = G_TYPE_DOUBLE;
expected_types[1] = GSK_TYPE_LINE_CAP;
expected_types[2] = GSK_TYPE_LINE_JOIN;
expected_types[3] = G_TYPE_DOUBLE;
}
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"type '%s' is not elligible for a component value",
g_type_name (type));
return FALSE;
}
value = value_data_new ();
value->name = g_strdup (name);
value->type = type;
value->kind = VALUE_COMPONENT;
value->component.params = g_ptr_array_new_with_free_func (value_data_unref);
for (guint i = 0; i < n_components; i++)
{
ValueData *value_data = NULL;
value_data = g_hash_table_lookup (self->values, components[i]);
if (value_data == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", components[i]);
return FALSE;
}
if (!g_type_is_a (value_data->type, expected_types[i]))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"component %u for type %s "
"must be of type %s, got %s",
i, g_type_name (type),
g_type_name (expected_types[i]),
g_type_name (value_data->type));
return FALSE;
}
g_ptr_array_add (value->component.params,
value_data_ref (value_data));
}
g_hash_table_replace (self->values, g_strdup (name), value_data_ref (value));
return TRUE;
}
gboolean
bge_wdgt_spec_add_transform_source_value (BgeWdgtSpec *self,
const char *name,
const char *next,
const char *instruction,
const char *const *args,
guint n_args,
GError **error)
{
gboolean result = FALSE;
ValueData *next_data = NULL;
g_autoptr (ValueData) value = NULL;
TransformInstr match = { 0 };
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (next != NULL, FALSE);
g_return_val_if_fail (instruction != NULL, FALSE);
g_return_val_if_fail (n_args == 0 || args != NULL, FALSE);
if (g_hash_table_contains (self->values, name))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' already exists", name);
return FALSE;
}
next_data = g_hash_table_lookup (self->values, next);
if (next_data == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", next);
return FALSE;
}
if (next_data->type != GSK_TYPE_TRANSFORM)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' must be of type %s to "
"build a transform, got %s",
next,
g_type_name (GSK_TYPE_TRANSFORM),
g_type_name (next_data->type));
return FALSE;
}
result = lookup_transform_instr (instruction, &match);
if (!result)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"\"%s\" is not a valid transform builder instruction",
instruction);
return FALSE;
}
if (n_args != match.n_args)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"transform builder function %s requires %u "
"arguments, got %u",
match.name, match.n_args, n_args);
return FALSE;
}
value = value_data_new ();
value->name = g_strdup (name);
value->type = GSK_TYPE_TRANSFORM;
value->kind = VALUE_TRANSFORM;
value->transform.func = match.call;
value->transform.next = value_data_ref (next_data);
value->transform.args = g_ptr_array_new_with_free_func (value_data_unref);
for (guint i = 0; i < n_args; i++)
{
ValueData *value_data = NULL;
value_data = g_hash_table_lookup (self->values, args[i]);
if (value_data == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", args[i]);
return FALSE;
}
if (!g_type_is_a (value_data->type, match.args[i]))
{
if (check_can_coerce_type (match.args[i], value_data->type))
g_ptr_array_add (value->transform.args,
wrap_coerce_value (self, value_data, match.args[i]));
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"argument %u for snapshot instruction %s "
"must be of type %s, got %s",
i, match.name,
g_type_name (match.args[i]),
g_type_name (value_data->type));
return FALSE;
}
}
else
g_ptr_array_add (value->transform.args, value_data_ref (value_data));
}
g_hash_table_replace (self->values, g_strdup (name), value_data_ref (value));
return TRUE;
}
gboolean
bge_wdgt_spec_add_path_source_value (BgeWdgtSpec *self,
const char *name,
const char *const *instructions,
const char *const *const *argss,
const guint *n_argss,
guint n_args,
GError **error)
{
gboolean result = FALSE;
g_autoptr (ValueData) value = NULL;
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (instructions != NULL, FALSE);
g_return_val_if_fail (argss != NULL, FALSE);
g_return_val_if_fail (n_argss != NULL, FALSE);
g_return_val_if_fail (n_args > 0, FALSE);
if (g_hash_table_contains (self->values, name))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' already exists", name);
return FALSE;
}
value = value_data_new ();
value->name = g_strdup (name);
value->type = GSK_TYPE_PATH;
value->kind = VALUE_PATH;
value->path.funcs = g_ptr_array_new ();
value->path.argss = g_ptr_array_new_with_free_func (
(GDestroyNotify) g_ptr_array_unref);
for (guint i = 0; i < n_args; i++)
{
PathBuilderInstr match = { 0 };
g_autoptr (GPtrArray) args = NULL;
result = lookup_path_builder_instr (instructions[i], &match);
if (!result)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"\"%s\" is not a valid path builder instruction",
instructions[i]);
return FALSE;
}
if (n_argss[i] != match.n_args)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"path builder instructions %s requires %u "
"arguments, got %u",
match.name, match.n_args, n_argss[i]);
return FALSE;
}
args = g_ptr_array_new_with_free_func (value_data_unref);
for (guint j = 0; j < n_argss[i]; j++)
{
ValueData *value_data = NULL;
value_data = g_hash_table_lookup (self->values, argss[i][j]);
if (value_data == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", argss[i][j]);
return FALSE;
}
if (!g_type_is_a (value_data->type, match.args[j]))
{
if (check_can_coerce_type (match.args[j], value_data->type))
g_ptr_array_add (args, wrap_coerce_value (self, value_data, match.args[j]));
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"argument %u for path builder instruction %s "
"must be of type %s, got %s",
j, match.name,
g_type_name (match.args[j]),
g_type_name (value_data->type));
return FALSE;
}
}
else
g_ptr_array_add (args, value_data_ref (value_data));
}
g_ptr_array_add (value->path.funcs, match.call);
g_ptr_array_add (value->path.argss, g_steal_pointer (&args));
}
g_hash_table_replace (self->values, g_strdup (name), value_data_ref (value));
return TRUE;
}
gboolean
bge_wdgt_spec_add_instance_source_value (BgeWdgtSpec *self,
const char *name,
GType type,
GError **error)
{
g_autoptr (ValueData) value = NULL;
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
if (g_hash_table_contains (self->values, name))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' already exists", name);
return FALSE;
}
if (!g_type_is_a (type, G_TYPE_OBJECT))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"type '%s' is not an object type",
g_type_name (type));
return FALSE;
}
if (!G_TYPE_IS_INSTANTIATABLE (type) ||
G_TYPE_IS_ABSTRACT (type))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"type '%s' is not instantiable",
g_type_name (type));
return FALSE;
}
value = value_data_new ();
value->kind = VALUE_OBJECT;
value->type = type;
value->name = g_strdup (name);
g_hash_table_replace (self->values, g_strdup (name), value_data_ref (value));
g_ptr_array_add (self->nonchildren, value_data_ref (value));
return TRUE;
}
gboolean
bge_wdgt_spec_add_child_source_value (BgeWdgtSpec *self,
const char *name,
GType type,
const char *parent,
const char *builder_type,
const char *const *css_classes,
guint n_css_classes,
GError **error)
{
g_autoptr (ValueData) value = NULL;
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
if (g_hash_table_contains (self->values, name))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' already exists", name);
return FALSE;
}
if (!g_type_is_a (type, GTK_TYPE_WIDGET))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"type '%s' does not derive from GtkWidget",
g_type_name (type));
return FALSE;
}
if (!G_TYPE_IS_INSTANTIATABLE (type) ||
G_TYPE_IS_ABSTRACT (type))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"type '%s' is not instantiable",
g_type_name (type));
return FALSE;
}
value = value_data_new ();
value->kind = VALUE_CHILD;
value->type = type;
value->name = g_strdup (name);
value->child.builder_type = builder_type != NULL ? g_strdup (builder_type) : NULL;
if (n_css_classes > 0 &&
css_classes != NULL)
{
value->child.css_classes = g_ptr_array_new_with_free_func (g_free);
g_ptr_array_set_size (value->child.css_classes, n_css_classes);
for (guint i = 0; i < n_css_classes; i++)
{
g_ptr_array_index (value->child.css_classes, i) = g_strdup (css_classes[i]);
}
}
if (parent != NULL)
{
ValueData *parent_data = NULL;
parent_data = g_hash_table_lookup (self->values, parent);
if (parent_data == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", parent);
return FALSE;
}
if (!g_type_is_a (parent_data->type, GTK_TYPE_BUILDABLE))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"parent widget '%s' is not of type %s",
g_type_name (GTK_TYPE_BUILDABLE), parent);
return FALSE;
}
value->child.parent_widget = value_data_ref (parent_data);
}
g_hash_table_replace (self->values, g_strdup (name), value_data_ref (value));
g_ptr_array_add (self->children, value_data_ref (value));
return TRUE;
}
gboolean
bge_wdgt_spec_add_cclosure_source_value (BgeWdgtSpec *self,
const char *name,
GType type,
GClosureMarshal marshal,
GCallback func,
const char *const *args,
const GType *arg_types,
guint n_args,
gpointer user_data,
GDestroyNotify destroy_user_data,
GError **error)
{
g_autoptr (ValueData) value = NULL;
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (func != NULL, FALSE);
g_return_val_if_fail (args != NULL, FALSE);
g_return_val_if_fail (n_args > 0, FALSE);
if (g_hash_table_contains (self->values, name))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' already exists", name);
if (user_data != NULL &&
destroy_user_data != NULL)
destroy_user_data (user_data);
return FALSE;
}
if (!G_TYPE_IS_VALUE (type))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"invalid type '%s'", g_type_name (type));
if (user_data != NULL &&
destroy_user_data != NULL)
destroy_user_data (user_data);
return FALSE;
}
value = value_data_new ();
value->kind = VALUE_CLOSURE;
value->name = g_strdup (name);
value->type = type;
value->closure.marshal = marshal;
value->closure.func = func;
value->closure.args = g_ptr_array_new_with_free_func (value_data_unref);
value->closure.user_data = user_data;
value->closure.destroy_user_data = destroy_user_data;
for (guint i = 0; i < n_args; i++)
{
ValueData *value_data = NULL;
value_data = g_hash_table_lookup (self->values, args[i]);
if (value_data == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", args[i]);
return FALSE;
}
if (arg_types != NULL &&
!g_type_is_a (value_data->type, arg_types[i]))
{
if (check_can_coerce_type (arg_types[i], value_data->type))
g_ptr_array_add (value->closure.args,
wrap_coerce_value (self, value_data, arg_types[i]));
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"component %u for type %s "
"must be of type %s, got %s",
i, g_type_name (type),
g_type_name (arg_types[i]),
g_type_name (value_data->type));
return FALSE;
}
}
else
g_ptr_array_add (value->closure.args, value_data_ref (value_data));
}
g_hash_table_replace (self->values, g_strdup (name), value_data_ref (value));
return TRUE;
}
gboolean
bge_wdgt_spec_add_measure_for_size_source_value (BgeWdgtSpec *self,
const char *name,
GError **error)
{
g_autoptr (ValueData) value = NULL;
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
if (g_hash_table_contains (self->values, name))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' already exists", name);
return FALSE;
}
value = value_data_new ();
value->name = g_strdup (name);
value->kind = VALUE_MEASURE_FOR_SIZE;
value->type = G_TYPE_INT;
g_hash_table_replace (self->values, g_strdup (name), value_data_ref (value));
return TRUE;
}
gboolean
bge_wdgt_spec_add_widget_width_source_value (BgeWdgtSpec *self,
const char *name,
GError **error)
{
g_autoptr (ValueData) value = NULL;
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
if (g_hash_table_contains (self->values, name))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' already exists", name);
return FALSE;
}
value = value_data_new ();
value->name = g_strdup (name);
value->kind = VALUE_WIDGET_WIDTH;
value->type = G_TYPE_DOUBLE;
g_hash_table_replace (self->values, g_strdup (name), value_data_ref (value));
return TRUE;
}
gboolean
bge_wdgt_spec_add_widget_height_source_value (BgeWdgtSpec *self,
const char *name,
GError **error)
{
g_autoptr (ValueData) value = NULL;
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
if (g_hash_table_contains (self->values, name))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' already exists", name);
return FALSE;
}
value = value_data_new ();
value->name = g_strdup (name);
value->kind = VALUE_WIDGET_HEIGHT;
value->type = G_TYPE_DOUBLE;
g_hash_table_replace (self->values, g_strdup (name), value_data_ref (value));
return TRUE;
}
gboolean
bge_wdgt_spec_add_tick_time_source_value (BgeWdgtSpec *self,
const char *name,
GError **error)
{
g_autoptr (ValueData) value = NULL;
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
if (g_hash_table_contains (self->values, name))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' already exists", name);
return FALSE;
}
value = value_data_new ();
value->name = g_strdup (name);
value->kind = VALUE_TICK_TIME;
value->type = G_TYPE_DOUBLE;
g_hash_table_replace (self->values, g_strdup (name), value_data_ref (value));
return TRUE;
}
gboolean
bge_wdgt_spec_add_track_transition_source_value (BgeWdgtSpec *self,
const char *name,
const char *src,
const char *damping_ratio,
const char *mass,
const char *stiffness,
GError **error)
{
ValueData *src_value = NULL;
g_autoptr (ValueData) coerced_src_value = NULL;
ValueData *damping_ratio_value = NULL;
g_autoptr (ValueData) coerced_damping_ratio_value = NULL;
ValueData *mass_value = NULL;
g_autoptr (ValueData) coerced_mass_value = NULL;
ValueData *stiffness_value = NULL;
g_autoptr (ValueData) coerced_stiffness_value = NULL;
g_autoptr (ValueData) value = NULL;
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (src != NULL, FALSE);
g_return_val_if_fail (damping_ratio != NULL, FALSE);
g_return_val_if_fail (mass != NULL, FALSE);
g_return_val_if_fail (stiffness != NULL, FALSE);
if (g_hash_table_contains (self->values, name))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' already exists", name);
return FALSE;
}
src_value = g_hash_table_lookup (self->values, src);
if (src_value == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", src);
return FALSE;
}
if (src_value->type != G_TYPE_DOUBLE)
{
if (check_can_coerce_type (G_TYPE_DOUBLE, src_value->type))
coerced_src_value = wrap_coerce_value (self, src_value, src_value->type);
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"source type %s cannot be transitioned",
g_type_name (src_value->type));
return FALSE;
}
}
damping_ratio_value = g_hash_table_lookup (self->values, damping_ratio);
if (damping_ratio_value == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", damping_ratio);
return FALSE;
}
if (damping_ratio_value->type != G_TYPE_DOUBLE)
{
if (check_can_coerce_type (G_TYPE_DOUBLE, damping_ratio_value->type))
coerced_damping_ratio_value = wrap_coerce_value (self, damping_ratio_value, damping_ratio_value->type);
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"damping ratio must be a double, got type %s",
g_type_name (damping_ratio_value->type));
return FALSE;
}
}
mass_value = g_hash_table_lookup (self->values, mass);
if (mass_value == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", mass);
return FALSE;
}
if (mass_value->type != G_TYPE_DOUBLE)
{
if (check_can_coerce_type (G_TYPE_DOUBLE, mass_value->type))
coerced_mass_value = wrap_coerce_value (self, mass_value, mass_value->type);
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"mass must be a double, got type %s",
g_type_name (mass_value->type));
return FALSE;
}
}
stiffness_value = g_hash_table_lookup (self->values, stiffness);
if (stiffness_value == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", stiffness);
return FALSE;
}
if (stiffness_value->type != G_TYPE_DOUBLE)
{
if (check_can_coerce_type (G_TYPE_DOUBLE, stiffness_value->type))
coerced_stiffness_value = wrap_coerce_value (self, stiffness_value, stiffness_value->type);
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"stiffness must be a double, got type %s",
g_type_name (stiffness_value->type));
return FALSE;
}
}
value = value_data_new ();
value->name = g_strdup (name);
value->kind = VALUE_TRACK_TRANSITION;
value->type = G_TYPE_DOUBLE;
value->track_transition.src =
coerced_src_value != NULL
? value_data_ref (coerced_src_value)
: value_data_ref (src_value);
value->track_transition.spring.damping_ratio =
coerced_damping_ratio_value != NULL
? value_data_ref (coerced_damping_ratio_value)
: value_data_ref (damping_ratio_value);
value->track_transition.spring.mass =
coerced_mass_value != NULL
? value_data_ref (coerced_mass_value)
: value_data_ref (mass_value);
value->track_transition.spring.stiffness =
coerced_stiffness_value != NULL
? value_data_ref (coerced_stiffness_value)
: value_data_ref (stiffness_value);
g_hash_table_replace (self->values, g_strdup (name), value_data_ref (value));
g_ptr_array_add (self->track_transition_values, value_data_ref (value));
return TRUE;
}
gboolean
bge_wdgt_spec_add_variable_value (BgeWdgtSpec *self,
GType type,
const char *name,
GError **error)
{
g_autoptr (ValueData) value = NULL;
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
if (!G_TYPE_IS_VALUE (type))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"type '%s' cannot be stored in value '%s'",
g_type_name (type), name);
return FALSE;
}
if (g_hash_table_contains (self->values, name))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' already exists", name);
return FALSE;
}
value = value_data_new ();
value->name = g_strdup (name);
value->type = type;
value->kind = VALUE_VARIABLE;
g_hash_table_replace (self->values, g_strdup (name), value_data_ref (value));
return TRUE;
}
gboolean
bge_wdgt_spec_add_reference_object_value (BgeWdgtSpec *self,
GType type,
const char *name,
GError **error)
{
g_autoptr (ValueData) value = NULL;
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
if (self->reference != NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"an object reference value has already been defined");
return FALSE;
}
if (g_hash_table_contains (self->values, name))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' already exists", name);
return FALSE;
}
if (!g_type_is_a (type, G_TYPE_OBJECT))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"type '%s' cannot be stored in reference object value '%s'",
g_type_name (type), name);
return FALSE;
}
value = value_data_new ();
value->name = g_strdup (name);
value->type = type;
value->kind = VALUE_REFERENCE_OBJECT;
g_hash_table_replace (self->values, g_strdup (name), value_data_ref (value));
self->reference = value_data_ref (value);
return TRUE;
}
gboolean
bge_wdgt_spec_add_property_value (BgeWdgtSpec *self,
const char *name,
const char *object,
const char *property,
GType *type_out,
GError **error)
{
ValueData *object_value = NULL;
ValueData *existing_value = NULL;
g_autoptr (GTypeClass) type_class = NULL;
GParamSpec *pspec = NULL;
g_autoptr (ValueData) value = NULL;
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (object != NULL, FALSE);
g_return_val_if_fail (property != NULL, FALSE);
object_value = g_hash_table_lookup (self->values, object);
if (object_value == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", object);
return FALSE;
}
if (!g_type_is_a (object_value->type, G_TYPE_OBJECT))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is not an object", object);
return FALSE;
}
existing_value = g_hash_table_lookup (self->values, name);
if (existing_value != NULL)
{
if (existing_value->kind == VALUE_PROPERTY &&
existing_value->property.object == object_value &&
g_strcmp0 (existing_value->property.prop_name, property) == 0)
return TRUE;
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' already exists", name);
return FALSE;
}
}
type_class = g_type_class_ref (object_value->type);
pspec = g_object_class_find_property (G_OBJECT_CLASS (type_class), property);
if (pspec == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"property '%s' doesn't exist on type %s",
property, g_type_name (object_value->type));
return FALSE;
}
value = value_data_new ();
value->name = g_strdup (name);
value->type = pspec->value_type;
value->kind = VALUE_PROPERTY;
value->property.object = value_data_ref (object_value);
value->property.prop_name = g_strdup (property);
value->property.pspec_flags = pspec->flags;
g_hash_table_replace (self->values, g_strdup (name), value_data_ref (value));
if (type_out != NULL)
*type_out = pspec->value_type;
return TRUE;
}
gboolean
bge_wdgt_spec_add_allocation_width_value (BgeWdgtSpec *self,
const char *name,
const char *child,
GError **error)
{
ValueData *child_value = NULL;
g_autoptr (ValueData) value = NULL;
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (child != NULL, FALSE);
if (g_hash_table_contains (self->values, name))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' already exists", name);
return FALSE;
}
child_value = g_hash_table_lookup (self->values, child);
if (child_value == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", child);
return FALSE;
}
if (!g_type_is_a (child_value->type, GTK_TYPE_WIDGET))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is not a widget", child);
return FALSE;
}
value = value_data_new ();
value->name = g_strdup (name);
value->type = G_TYPE_INT;
value->kind = VALUE_ALLOCATION_WIDTH;
value->allocation.widget = value_data_ref (child_value);
g_hash_table_replace (self->values, g_strdup (name), value_data_ref (value));
return TRUE;
}
gboolean
bge_wdgt_spec_add_allocation_height_value (BgeWdgtSpec *self,
const char *name,
const char *child,
GError **error)
{
ValueData *child_value = NULL;
g_autoptr (ValueData) value = NULL;
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (child != NULL, FALSE);
if (g_hash_table_contains (self->values, name))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' already exists", name);
return FALSE;
}
child_value = g_hash_table_lookup (self->values, child);
if (child_value == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", child);
return FALSE;
}
if (!g_type_is_a (child_value->type, GTK_TYPE_WIDGET))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is not a widget", child);
return FALSE;
}
value = value_data_new ();
value->name = g_strdup (name);
value->type = G_TYPE_INT;
value->kind = VALUE_ALLOCATION_HEIGHT;
value->allocation.widget = value_data_ref (child_value);
g_hash_table_replace (self->values, g_strdup (name), value_data_ref (value));
return TRUE;
}
gboolean
bge_wdgt_spec_add_allocation_transform_value (BgeWdgtSpec *self,
const char *name,
const char *child,
GError **error)
{
ValueData *child_value = NULL;
g_autoptr (ValueData) value = NULL;
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (child != NULL, FALSE);
if (g_hash_table_contains (self->values, name))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' already exists", name);
return FALSE;
}
child_value = g_hash_table_lookup (self->values, child);
if (child_value == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", child);
return FALSE;
}
if (!g_type_is_a (child_value->type, GTK_TYPE_WIDGET))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is not a widget", child);
return FALSE;
}
value = value_data_new ();
value->name = g_strdup (name);
value->type = GSK_TYPE_TRANSFORM;
value->kind = VALUE_ALLOCATION_TRANSFORM;
value->allocation.widget = value_data_ref (child_value);
g_hash_table_replace (self->values, g_strdup (name), value_data_ref (value));
return TRUE;
}
gboolean
bge_wdgt_spec_add_measure_value (BgeWdgtSpec *self,
const char *name,
BgeWdgtMeasureKind kind,
GError **error)
{
g_autoptr (ValueData) value = NULL;
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
if (g_hash_table_contains (self->values, name))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' already exists", name);
return FALSE;
}
value = value_data_new ();
value->name = g_strdup (name);
value->type = G_TYPE_INT;
switch (kind)
{
case BGE_WDGT_MEASURE_MINIMUM_WIDTH:
value->kind = VALUE_MEASURE_MINIMUM_WIDTH;
break;
case BGE_WDGT_MEASURE_NATURAL_WIDTH:
value->kind = VALUE_MEASURE_NATURAL_WIDTH;
break;
case BGE_WDGT_MEASURE_MINIMUM_HEIGHT:
value->kind = VALUE_MEASURE_MINIMUM_HEIGHT;
break;
case BGE_WDGT_MEASURE_NATURAL_HEIGHT:
value->kind = VALUE_MEASURE_NATURAL_HEIGHT;
break;
default:
{
g_critical ("Passed an invalid measure kind");
return FALSE;
}
}
g_hash_table_replace (self->values, g_strdup (name), value_data_ref (value));
return TRUE;
}
gboolean
bge_wdgt_spec_add_state (BgeWdgtSpec *self,
const char *name,
gboolean default_state,
GError **error)
{
g_autoptr (StateData) state = NULL;
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
if (g_hash_table_contains (self->states, name))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"state '%s' already exists", name);
return FALSE;
}
if (default_state && self->default_state != NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"default state already specified");
return FALSE;
}
state = state_data_new ();
state->name = g_strdup (name);
state->setters = g_hash_table_new_full (
g_direct_hash, g_direct_equal,
value_data_unref, value_data_unref);
state->transitions = g_hash_table_new_full (
g_direct_hash, g_direct_equal,
value_data_unref, transition_data_unref);
state->expressions = g_hash_table_new_full (
g_direct_hash, g_direct_equal,
value_data_unref, (GDestroyNotify) gtk_expression_unref);
state->snapshot_deps = g_ptr_array_new_with_free_func (
(GDestroyNotify) gtk_expression_unref);
state->values_need_tick = g_hash_table_new_full (
g_direct_hash, g_direct_equal,
value_data_unref, NULL);
g_hash_table_replace (self->states, g_strdup (name), state_data_ref (state));
if (default_state)
self->default_state = state_data_ref (state);
gtk_string_list_append (self->state_names, name);
return TRUE;
}
gboolean
bge_wdgt_spec_set_value (BgeWdgtSpec *self,
const char *state,
const char *dest_value,
const char *src_value,
GError **error)
{
StateData *state_data = NULL;
ValueData *dest_data = NULL;
ValueData *src_data = NULL;
g_autoptr (ValueData) coerced_src_data = NULL;
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (dest_value != NULL, FALSE);
g_return_val_if_fail (src_value != NULL, FALSE);
if (state != NULL)
state_data = g_hash_table_lookup (self->states, state);
else
state_data = self->init_state;
dest_data = g_hash_table_lookup (self->values, dest_value);
src_data = g_hash_table_lookup (self->values, src_value);
if (state_data == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"state '%s' is undefined", state);
return FALSE;
}
if (dest_data == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", dest_value);
return FALSE;
}
if (src_data == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", src_value);
return FALSE;
}
if (dest_data == src_data)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"cannot assign '%s' to itself", src_value);
return FALSE;
}
if (!g_type_is_a (src_data->type, dest_data->type))
{
if (check_can_coerce_type (dest_data->type, src_data->type))
coerced_src_data = wrap_coerce_value (self, src_data, dest_data->type);
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"source type %s cannot be assigned to destination type %s",
g_type_name (src_data->type), g_type_name (dest_data->type));
return FALSE;
}
}
if (dest_data->kind == VALUE_CONSTANT)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"cannot assign to a constant source value");
return FALSE;
}
if (dest_data->kind == VALUE_PROPERTY &&
!(dest_data->property.pspec_flags & G_PARAM_WRITABLE))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"property %s on type %s is not writable",
dest_data->property.prop_name,
g_type_name (dest_data->type));
return FALSE;
}
if (src_data->kind == VALUE_PROPERTY &&
!(src_data->property.pspec_flags & G_PARAM_READABLE))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"property %s on type %s is not writable",
src_data->property.prop_name,
g_type_name (src_data->type));
return FALSE;
}
g_hash_table_replace (
state_data->setters,
value_data_ref (dest_data),
coerced_src_data != NULL
? value_data_ref (coerced_src_data)
: value_data_ref (src_data));
return TRUE;
}
gboolean
bge_wdgt_spec_transition_value (BgeWdgtSpec *self,
const char *state,
const char *value,
const char *seconds,
const char *easing,
GError **error)
{
StateData *state_data = NULL;
ValueData *value_data = NULL;
ValueData *seconds_data = NULL;
ValueData *easing_data = NULL;
g_autoptr (TransitionData) transition = NULL;
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (state != NULL, FALSE);
g_return_val_if_fail (value != NULL, FALSE);
g_return_val_if_fail (seconds != NULL, FALSE);
g_return_val_if_fail (easing != NULL, FALSE);
state_data = g_hash_table_lookup (self->states, state);
if (state_data == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"state '%s' is undefined", state);
return FALSE;
}
value_data = g_hash_table_lookup (self->values, value);
seconds_data = g_hash_table_lookup (self->values, seconds);
easing_data = g_hash_table_lookup (self->values, easing);
if (value_data == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", value);
return FALSE;
}
if (value_data->type != G_TYPE_DOUBLE)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"type %s cannot be transitioned",
g_type_name (value_data->type));
return FALSE;
}
if (seconds_data == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", value);
return FALSE;
}
if (seconds_data->type != G_TYPE_DOUBLE)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"transition ease seconds value must be a double, got %s",
g_type_name (seconds_data->type));
return FALSE;
}
if (easing_data == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", value);
return FALSE;
}
if (easing_data->type != BGE_TYPE_EASING)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"transition ease easing value must be of type %s, got %s",
g_type_name (BGE_TYPE_EASING),
g_type_name (easing_data->type));
return FALSE;
}
transition = transition_data_new ();
transition->kind = TRANSITION_EASE;
transition->ease.seconds = value_data_ref (seconds_data);
transition->ease.easing = value_data_ref (easing_data);
g_hash_table_replace (
state_data->transitions,
value_data_ref (value_data),
transition_data_ref (transition));
overlays */
g_hash_table_replace (
self->init_state->transitions,
value_data_ref (value_data),
transition_data_ref (transition));
return TRUE;
}
gboolean
bge_wdgt_spec_transition_value_spring (BgeWdgtSpec *self,
const char *state,
const char *value,
const char *damping_ratio,
const char *mass,
const char *stiffness,
GError **error)
{
StateData *state_data = NULL;
ValueData *value_data = NULL;
ValueData *damping_ratio_data = NULL;
ValueData *mass_data = NULL;
ValueData *stiffness_data = NULL;
g_autoptr (TransitionData) transition = NULL;
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (state != NULL, FALSE);
g_return_val_if_fail (value != NULL, FALSE);
g_return_val_if_fail (damping_ratio != NULL, FALSE);
g_return_val_if_fail (mass != NULL, FALSE);
g_return_val_if_fail (stiffness != NULL, FALSE);
state_data = g_hash_table_lookup (self->states, state);
value_data = g_hash_table_lookup (self->values, value);
damping_ratio_data = g_hash_table_lookup (self->values, damping_ratio);
mass_data = g_hash_table_lookup (self->values, mass);
stiffness_data = g_hash_table_lookup (self->values, stiffness);
if (state_data == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"state '%s' is undefined", state);
return FALSE;
}
if (value_data == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", value);
return FALSE;
}
if (damping_ratio_data == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", damping_ratio);
return FALSE;
}
if (mass_data == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", mass);
return FALSE;
}
if (stiffness_data == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", stiffness);
return FALSE;
}
if (value_data->type != G_TYPE_DOUBLE)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"type %s cannot be transitioned",
g_type_name (value_data->type));
return FALSE;
}
if (damping_ratio_data->type != G_TYPE_DOUBLE)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"transition damping ratio must of type %s, got %s",
g_type_name (G_TYPE_DOUBLE),
g_type_name (damping_ratio_data->type));
return FALSE;
}
if (mass_data->type != G_TYPE_DOUBLE)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"transition mass must of type %s, got %s",
g_type_name (G_TYPE_DOUBLE),
g_type_name (mass_data->type));
return FALSE;
}
if (stiffness_data->type != G_TYPE_DOUBLE)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"transition stiffness must of type %s, got %s",
g_type_name (G_TYPE_DOUBLE),
g_type_name (stiffness_data->type));
return FALSE;
}
transition = transition_data_new ();
transition->kind = TRANSITION_SPRING;
transition->spring.damping_ratio = value_data_ref (damping_ratio_data);
transition->spring.mass = value_data_ref (mass_data);
transition->spring.stiffness = value_data_ref (stiffness_data);
g_hash_table_replace (
state_data->transitions,
value_data_ref (value_data),
transition_data_ref (transition));
overlays */
g_hash_table_replace (
self->init_state->transitions,
value_data_ref (value_data),
transition_data_ref (transition));
return TRUE;
}
gboolean
bge_wdgt_spec_append_snapshot_instr (BgeWdgtSpec *self,
const char *state,
BgeWdgtSnapshotInstrKind kind,
const char *instr,
const char *const *args,
guint n_args,
guint *n_pops_out,
GError **error)
{
gboolean result = FALSE;
StateData *state_data = NULL;
ForeachData *foreach_context = NULL;
SnapshotInstr match = { 0 };
guint match_rest_start = 0;
g_autoptr (SnapshotCallData) call = NULL;
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (instr != NULL, FALSE);
g_return_val_if_fail (n_args == 0 || args != NULL, FALSE);
if (state != NULL)
{
state_data = g_hash_table_lookup (self->states, state);
if (state_data == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"state '%s' is undefined", state);
return FALSE;
}
}
else
state_data = self->init_state;
if (self->foreach_stack->len > 0)
foreach_context = g_ptr_array_index (
self->foreach_stack,
self->foreach_stack->len - 1);
switch (kind)
{
case BGE_WDGT_SNAPSHOT_INSTR_PUSH:
result = lookup_snapshot_push_instr (instr, &match);
if (!result)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"\"%s\" is not a valid snapshot push instruction",
instr);
return FALSE;
}
if (n_pops_out != NULL)
*n_pops_out = match.n_pops;
break;
case BGE_WDGT_SNAPSHOT_INSTR_TRANSFORM:
result = lookup_snapshot_transform_instr (instr, &match);
if (!result)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"\"%s\" is not a valid snapshot transform instruction",
instr);
return FALSE;
}
break;
case BGE_WDGT_SNAPSHOT_INSTR_APPEND:
result = lookup_snapshot_append_instr (instr, &match);
if (!result)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"\"%s\" is not a valid snapshot append instruction",
instr);
return FALSE;
}
break;
case BGE_WDGT_SNAPSHOT_INSTR_POP:
case BGE_WDGT_SNAPSHOT_INSTR_SAVE:
case BGE_WDGT_SNAPSHOT_INSTR_RESTORE:
{
call = snapshot_call_data_new ();
call->kind = kind;
if (foreach_context != NULL)
call->foreach_context = foreach_data_ref (foreach_context);
ensure_state_snapshot (state_data);
g_ptr_array_add (state_data->snapshot->calls, snapshot_call_data_ref (call));
}
return TRUE;
case BGE_WDGT_SNAPSHOT_INSTR_SNAPSHOT_CHILD:
{
ValueData *child = NULL;
if (n_args != 1)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"child snapshot instruction requires "
"a single argument");
return FALSE;
}
child = g_hash_table_lookup (self->values, args[0]);
if (child == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", args[0]);
return FALSE;
}
if (!g_type_is_a (child->type, GTK_TYPE_WIDGET))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is not a child", args[0]);
return FALSE;
}
call = snapshot_call_data_new ();
call->kind = kind;
call->child = value_data_ref (child);
if (foreach_context != NULL)
call->foreach_context = foreach_data_ref (foreach_context);
ensure_state_snapshot (state_data);
g_ptr_array_add (state_data->snapshot->calls, snapshot_call_data_ref (call));
}
return TRUE;
default:
g_critical ("invalid snapshot instruction kind specified");
return FALSE;
}
match_rest_start = match.n_args - match.n_rest;
if (n_args != match.n_args)
{
if (match.n_rest > 0 &&
n_args > match.n_args)
{
if ((n_args - match.n_args) % match.n_rest != 0)
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"snapshot instruction %s cannot handle %u "
"trailing arguments",
match.name, n_args - match.n_args);
}
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"snapshot instruction %s requires %u "
"arguments, got %u",
match.name, match.n_args, n_args);
return FALSE;
}
}
call = snapshot_call_data_new ();
call->kind = kind;
call->func = match.call;
call->args = g_ptr_array_new_with_free_func (value_data_unref);
call->rest = g_ptr_array_new_with_free_func (value_data_unref);
if (foreach_context != NULL)
call->foreach_context = foreach_data_ref (foreach_context);
for (guint i = 0; i < n_args; i++)
{
ValueData *value_data = NULL;
gboolean in_rest = FALSE;
GType expected_type = 0;
value_data = g_hash_table_lookup (self->values, args[i]);
if (value_data == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", args[i]);
return FALSE;
}
in_rest = i >= match_rest_start;
if (in_rest)
expected_type = match.args[match_rest_start +
((i - match_rest_start) %
match.n_rest)];
else
expected_type = match.args[i];
if (!g_type_is_a (value_data->type, expected_type))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"argument %u for snapshot instruction %s "
"must be of type %s, got %s",
i, match.name,
g_type_name (expected_type),
g_type_name (value_data->type));
return FALSE;
}
if (in_rest)
g_ptr_array_add (call->rest, value_data_ref (value_data));
else
g_ptr_array_add (call->args, value_data_ref (value_data));
}
ensure_state_snapshot (state_data);
g_ptr_array_add (state_data->snapshot->calls, snapshot_call_data_ref (call));
return TRUE;
}
gboolean
bge_wdgt_spec_push_foreach (BgeWdgtSpec *self,
const char *model,
const char *value_iterator,
const char *index_iterator,
GType iterator_type,
GError **error)
{
ValueData *model_value = NULL;
g_autoptr (GHashTable) values_copy = NULL;
GHashTableIter iter = { 0 };
g_autoptr (ForeachData) foreach_context = NULL;
g_autoptr (ValueData) value_iterator_value = NULL;
g_autoptr (ValueData) index_iterator_value = NULL;
g_return_val_if_fail (BGE_IS_WDGT_SPEC (self), FALSE);
g_return_val_if_fail (!self->ready, FALSE);
g_return_val_if_fail (model != NULL, FALSE);
g_return_val_if_fail (value_iterator != NULL, FALSE);
g_return_val_if_fail (index_iterator != NULL, FALSE);
model_value = g_hash_table_lookup (self->values, model);
if (model_value == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' is undefined", model);
return FALSE;
}
if (!g_type_is_a (model_value->type, G_TYPE_LIST_MODEL))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' does not implement GListModel", model);
return FALSE;
}
if (g_hash_table_contains (self->values, value_iterator))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' already exists", value_iterator);
return FALSE;
}
if (g_hash_table_contains (self->values, index_iterator))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"value '%s' already exists", index_iterator);
return FALSE;
}
if (!g_type_is_a (iterator_type, G_TYPE_OBJECT))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
"iterator type does not derive from GObject");
return FALSE;
}
values_copy = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, value_data_unref);
g_hash_table_iter_init (&iter, self->values);
for (;;)
{
char *name = NULL;
ValueData *value = NULL;
if (!g_hash_table_iter_next (
&iter,
(gpointer *) &name,
(gpointer *) &value))
break;
g_hash_table_replace (values_copy,
g_strdup (name),
value_data_ref (value));
}
g_ptr_array_add (self->values_stack, g_steal_pointer (&self->values));
self->values = g_hash_table_ref (values_copy);
foreach_context = foreach_data_new ();
foreach_context->model = value_data_ref (model_value);
foreach_context->values = g_hash_table_ref (values_copy);
if (self->foreach_stack->len > 0)
{
ForeachData *parent = NULL;
parent = g_ptr_array_index (
self->foreach_stack,
self->foreach_stack->len - 1);
foreach_context->parent = foreach_data_ref (parent);
}
g_ptr_array_add (self->foreach_stack, foreach_data_ref (foreach_context));
g_ptr_array_add (self->foreaches, foreach_data_ref (foreach_context));
value_iterator_value = value_data_new ();
value_iterator_value->kind = VALUE_ITERATOR;
value_iterator_value->name = g_strdup (value_iterator);
value_iterator_value->type = iterator_type;
value_iterator_value->iterator.context = foreach_data_ref (foreach_context);
g_hash_table_replace (values_copy,
g_strdup (value_iterator),
value_data_ref (value_iterator_value));
index_iterator_value = value_data_new ();
index_iterator_value->kind = VALUE_ITERATOR;
index_iterator_value->name = g_strdup (index_iterator);
index_iterator_value->type = G_TYPE_UINT;
index_iterator_value->iterator.context = foreach_data_ref (foreach_context);
g_hash_table_replace (values_copy,
g_strdup (index_iterator),
value_data_ref (index_iterator_value));
return TRUE;
}
gboolean
bge_wdgt_spec_pop_foreach (BgeWdgtSpec *self)
{
GHashTable *restore_values = NULL;
g_return_val_if_fail (self->foreach_stack->len > 0, FALSE);
g_assert (self->values_stack->len > 0);
g_clear_pointer (&self->values, g_hash_table_unref);
restore_values = g_ptr_array_index (self->values_stack, self->values_stack->len - 1);
self->values = g_hash_table_ref (restore_values);
g_ptr_array_set_size (self->values_stack, self->values_stack->len - 1);
g_ptr_array_set_size (self->foreach_stack, self->foreach_stack->len - 1);
return TRUE;
}
static gboolean
check_can_coerce_type (GType dest,
GType src)
{
const struct
{
GType dest;
GType src;
} valid_pairings[] = {
{ G_TYPE_INT, G_TYPE_UINT },
{ G_TYPE_INT, G_TYPE_FLOAT },
{ G_TYPE_INT, G_TYPE_DOUBLE },
{ G_TYPE_UINT, G_TYPE_INT },
{ G_TYPE_UINT, G_TYPE_FLOAT },
{ G_TYPE_UINT, G_TYPE_DOUBLE },
{ G_TYPE_INT64, G_TYPE_INT },
{ G_TYPE_INT64, G_TYPE_UINT },
{ G_TYPE_INT64, G_TYPE_FLOAT },
{ G_TYPE_INT64, G_TYPE_DOUBLE },
{ G_TYPE_UINT64, G_TYPE_INT },
{ G_TYPE_UINT64, G_TYPE_UINT },
{ G_TYPE_UINT64, G_TYPE_INT64 },
{ G_TYPE_UINT64, G_TYPE_FLOAT },
{ G_TYPE_UINT64, G_TYPE_DOUBLE },
{ G_TYPE_FLOAT, G_TYPE_INT },
{ G_TYPE_FLOAT, G_TYPE_UINT },
{ G_TYPE_FLOAT, G_TYPE_DOUBLE },
{ G_TYPE_DOUBLE, G_TYPE_INT },
{ G_TYPE_DOUBLE, G_TYPE_UINT },
{ G_TYPE_DOUBLE, G_TYPE_FLOAT },
{ G_TYPE_STRING, G_TYPE_INT },
{ G_TYPE_STRING, G_TYPE_UINT },
{ G_TYPE_STRING, G_TYPE_INT64 },
{ G_TYPE_STRING, G_TYPE_UINT64 },
{ G_TYPE_STRING, G_TYPE_FLOAT },
{ G_TYPE_STRING, G_TYPE_DOUBLE },
};
for (guint i = 0; i < G_N_ELEMENTS (valid_pairings); i++)
{
if (dest == valid_pairings[i].dest &&
src == valid_pairings[i].src)
return TRUE;
}
return FALSE;
}
static void
coerce_value (const GValue *in,
GType dest_type,
GValue *out)
{
be non-compile-time constants */
if (dest_type == G_TYPE_INT)
{
int val = 0;
if (in->g_type == G_TYPE_UINT)
val = g_value_get_uint (in);
else if (in->g_type == G_TYPE_FLOAT)
val = round (g_value_get_float (in));
else if (in->g_type == G_TYPE_DOUBLE)
val = round (g_value_get_double (in));
g_value_set_int (out, val);
}
else if (dest_type == G_TYPE_UINT)
{
guint val = 0;
if (in->g_type == G_TYPE_INT)
val = g_value_get_int (in);
else if (in->g_type == G_TYPE_FLOAT)
val = round (g_value_get_float (in));
else if (in->g_type == G_TYPE_DOUBLE)
val = round (g_value_get_double (in));
g_value_set_uint (out, val);
}
else if (dest_type == G_TYPE_INT64)
{
gint64 val = 0;
if (in->g_type == G_TYPE_INT)
val = g_value_get_int (in);
else if (in->g_type == G_TYPE_UINT)
val = g_value_get_uint (in);
else if (in->g_type == G_TYPE_FLOAT)
val = round (g_value_get_float (in));
else if (in->g_type == G_TYPE_DOUBLE)
val = round (g_value_get_double (in));
g_value_set_int64 (out, val);
}
else if (dest_type == G_TYPE_UINT64)
{
guint64 val = 0;
if (in->g_type == G_TYPE_INT)
val = g_value_get_int (in);
else if (in->g_type == G_TYPE_UINT)
val = g_value_get_uint (in);
else if (in->g_type == G_TYPE_INT64)
val = g_value_get_int64 (in);
else if (in->g_type == G_TYPE_UINT64)
val = round (g_value_get_float (in));
else if (in->g_type == G_TYPE_DOUBLE)
val = round (g_value_get_double (in));
g_value_set_uint64 (out, val);
}
else if (dest_type == G_TYPE_FLOAT)
{
float val = 0;
if (in->g_type == G_TYPE_INT)
val = g_value_get_int (in);
else if (in->g_type == G_TYPE_UINT)
val = g_value_get_uint (in);
else if (in->g_type == G_TYPE_DOUBLE)
val = round (g_value_get_double (in));
g_value_set_float (out, val);
}
else if (dest_type == G_TYPE_DOUBLE)
{
double val = 0;
if (in->g_type == G_TYPE_INT)
val = g_value_get_int (in);
else if (in->g_type == G_TYPE_UINT)
val = g_value_get_uint (in);
else if (in->g_type == G_TYPE_FLOAT)
val = round (g_value_get_float (in));
g_value_set_double (out, val);
}
else if (dest_type == G_TYPE_STRING)
{
g_autofree char *val = NULL;
if (in->g_type == G_TYPE_INT)
val = g_strdup_printf ("%d", g_value_get_int (in));
else if (in->g_type == G_TYPE_UINT)
val = g_strdup_printf ("%u", g_value_get_uint (in));
else if (in->g_type == G_TYPE_INT64)
val = g_strdup_printf ("%zd", g_value_get_int64 (in));
else if (in->g_type == G_TYPE_UINT64)
val = g_strdup_printf ("%zu", g_value_get_uint64 (in));
else if (in->g_type == G_TYPE_FLOAT)
val = g_strdup_printf ("%f", g_value_get_float (in));
else if (in->g_type == G_TYPE_DOUBLE)
val = g_strdup_printf ("%f", g_value_get_double (in));
g_value_take_string (out, g_steal_pointer (&val));
}
}
static ValueData *
wrap_coerce_value (BgeWdgtSpec *self,
ValueData *value,
GType dest_type)
{
g_autoptr (ValueData) coerced = NULL;
g_assert (!g_type_is_a (value->type, dest_type));
coerced = value_data_new ();
coerced->type = dest_type;
coerced->kind = VALUE_COERCION;
coerced->coercion.value = value_data_ref (value);
g_ptr_array_add (self->anon_values, value_data_ref (coerced));
return g_steal_pointer (&coerced);
}
static void
ensure_state_snapshot (StateData *state)
{
if (state->snapshot != NULL)
return;
state->snapshot = snapshot_data_new ();
state->snapshot->calls = g_ptr_array_new_with_free_func (
snapshot_call_data_unref);
}
#define BGE_TYPE_WDGT_NOTIFIER (bge_wdgt_notifier_get_type ())
G_DECLARE_FINAL_TYPE (BgeWdgtNotifier, bge_wdgt_notifier, BGE, WDGT_NOTIFIER, GObject)
struct _BgeWdgtNotifier
{
GObject parent_instance;
double value;
};
G_DEFINE_FINAL_TYPE (BgeWdgtNotifier, bge_wdgt_notifier, G_TYPE_OBJECT);
enum
{
NOTIFIER_PROP_0,
NOTIFIER_PROP_VALUE,
LAST_NOTIFIER_PROP
};
static GParamSpec *notifier_props[LAST_NOTIFIER_PROP] = { 0 };
static void
bge_wdgt_notifier_dispose (GObject *object)
{
BgeWdgtNotifier *self = BGE_WDGT_NOTIFIER (object);
(void) self;
G_OBJECT_CLASS (bge_wdgt_notifier_parent_class)->dispose (object);
}
static void
bge_wdgt_notifier_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BgeWdgtNotifier *self = BGE_WDGT_NOTIFIER (object);
switch (prop_id)
{
case NOTIFIER_PROP_VALUE:
g_value_set_double (value, self->value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bge_wdgt_notifier_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BgeWdgtNotifier *self = BGE_WDGT_NOTIFIER (object);
switch (prop_id)
{
case NOTIFIER_PROP_VALUE:
self->value = g_value_get_double (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bge_wdgt_notifier_class_init (BgeWdgtNotifierClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->set_property = bge_wdgt_notifier_set_property;
object_class->get_property = bge_wdgt_notifier_get_property;
object_class->dispose = bge_wdgt_notifier_dispose;
notifier_props[NOTIFIER_PROP_VALUE] =
g_param_spec_double (
"value",
NULL, NULL,
G_MININT, G_MAXINT, 0.0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, LAST_PROP, notifier_props);
}
static void
bge_wdgt_notifier_init (BgeWdgtNotifier *self)
{
}
BGE_DEFINE_DATA (
foreach_instance,
ForeachInstance,
{
guint current_idx;
BgeWdgtNotifier *notifier;
},
BGE_RELEASE_DATA (notifier, g_object_unref))
BGE_DEFINE_DATA (
state_instance,
StateInstance,
{
GHashTable *transitions;
},
BGE_RELEASE_DATA (transitions, g_hash_table_unref));
struct _BgeWdgtRenderer
{
GtkWidget parent_instance;
BgeWdgtSpec *spec;
char *state;
GObject *reference;
GtkWidget *child;
StateInstanceData *init_instance;
GPtrArray *init_watches;
StateData *active_state;
StateInstanceData *active_instance;
SnapshotData *active_snapshot;
StateData *last_state;
StateInstanceData *last_instance;
GTimer *since_last_state;
guint tick;
gboolean had_tick_since_state_switch;
GHashTable *objects;
GPtrArray *nonchildren;
GPtrArray *children;
GHashTable *allocations;
GHashTable *foreach_instances;
GHashTable *state_instances;
GHashTable *transition_instances;
GHashTable *transition_closure_instances;
GHashTable *track_transition_closure_instances;
GPtrArray *track_transitions;
GPtrArray *bindings;
GPtrArray *watches;
BgeWdgtNotifier *reference_notifier;
int measure_minimum_width;
int measure_natural_width;
int measure_minimum_height;
int measure_natural_height;
int current_measure_for_size;
BgeWdgtNotifier *measure_for_size_notifier;
int widget_width;
int widget_height;
BgeWdgtNotifier *widget_width_notifier;
BgeWdgtNotifier *widget_height_notifier;
GTimer *tick_time;
BgeWdgtNotifier *tick_time_notifier;
};
G_DEFINE_FINAL_TYPE (BgeWdgtRenderer, bge_wdgt_renderer, GTK_TYPE_WIDGET);
enum
{
RENDERER_PROP_0,
RENDERER_PROP_SPEC,
RENDERER_PROP_RESOURCE,
RENDERER_PROP_STATE,
RENDERER_PROP_REFERENCE,
RENDERER_PROP_CHILD,
LAST_RENDERER_PROP
};
static GParamSpec *renderer_props[LAST_RENDERER_PROP] = { 0 };
BGE_DEFINE_DATA (
watch_setter,
WatchSetter,
{
BgeWdgtRenderer *self;
StateData *state;
StateInstanceData *instance;
ValueData *dest;
ValueData *src;
},
BGE_RELEASE_DATA (state, state_data_unref);
BGE_RELEASE_DATA (instance, state_instance_data_unref);
BGE_RELEASE_DATA (dest, value_data_unref);
BGE_RELEASE_DATA (src, value_data_unref))
BGE_DEFINE_DATA (
allocation,
Allocation,
{
ValueData *width;
ValueData *height;
ValueData *transform;
},
BGE_RELEASE_DATA (width, value_data_unref);
BGE_RELEASE_DATA (height, value_data_unref);
BGE_RELEASE_DATA (transform, value_data_unref))
typedef struct
{
double velocity;
double est_duration;
double cache_damping_ratio;
double cache_mass;
double cache_stiffness;
} SpringCache;
BGE_DEFINE_DATA (
transition_instance,
TransitionInstance,
{
double value;
double elapsed;
BgeWdgtNotifier *notifier;
struct
{
BgeEasing easing;
double seconds;
} ease;
SpringCache spring;
},
BGE_RELEASE_DATA (notifier, g_object_unref))
BGE_DEFINE_DATA (
transition_closure,
TransitionClosure,
{
StateData *state;
StateInstanceData *instance;
ValueData *value;
},
BGE_RELEASE_DATA (state, state_data_unref);
BGE_RELEASE_DATA (instance, state_instance_data_unref);
BGE_RELEASE_DATA (value, value_data_unref));
BGE_DEFINE_DATA (
track_transition_closure,
TrackTransitionClosure,
{
ValueData *value;
GTimer *timer;
BgeWdgtNotifier *notifier;
double elapsed;
double current;
double target;
double last;
SpringCache spring;
},
BGE_RELEASE_DATA (value, value_data_unref);
BGE_RELEASE_DATA (notifier, g_object_unref);
BGE_RELEASE_DATA (timer, g_timer_destroy));
static void
regenerate (BgeWdgtRenderer *self);
static void
spawn_nonchild (BgeWdgtRenderer *self,
ValueData *value);
static void
spawn_child (BgeWdgtRenderer *self,
ValueData *value,
GtkBuilder *dummy_builder);
static void
apply_state (BgeWdgtRenderer *self);
static GtkExpression *
ensure_expressions (BgeWdgtSpec *self,
ValueData *value,
StateData *state,
gboolean *value_needs_tick);
static void
set_value (BgeWdgtRenderer *self,
StateData *state,
StateInstanceData *instance,
ValueData *dest,
ValueData *src,
GPtrArray *watches);
static int
resolve_value_int (BgeWdgtRenderer *self,
ValueData *value,
StateData *state);
static double
resolve_value_double (BgeWdgtRenderer *self,
ValueData *value,
StateData *state);
static gint
resolve_value_enum (BgeWdgtRenderer *self,
ValueData *value,
StateData *state);
static gpointer
resolve_value_boxed_dup (BgeWdgtRenderer *self,
ValueData *value,
StateData *state);
static gpointer
resolve_value_object_dup (BgeWdgtRenderer *self,
ValueData *value,
StateData *state);
static void
discard_binding (gpointer ptr);
static void
discard_watch (gpointer ptr);
static void
prop_change_queue_draw (BgeWdgtRenderer *self);
static void
reset_setter (WatchSetterData *data);
static void
wdgt_renderer_set_from_resource (BgeWdgtRenderer *self,
const char *resource);
static void
bge_wdgt_renderer_dispose (GObject *object)
{
BgeWdgtRenderer *self = BGE_WDGT_RENDERER (object);
if (self->tick > 0)
{
gtk_widget_remove_tick_callback (GTK_WIDGET (self), self->tick);
self->tick = 0;
}
g_clear_pointer (&self->spec, g_object_unref);
g_clear_pointer (&self->state, g_free);
g_clear_pointer (&self->reference, g_object_unref);
g_clear_pointer (&self->child, gtk_widget_unparent);
g_clear_pointer (&self->init_instance, state_instance_data_unref);
g_clear_pointer (&self->init_watches, g_ptr_array_unref);
g_clear_pointer (&self->active_state, state_data_unref);
g_clear_pointer (&self->active_instance, state_instance_data_unref);
g_clear_pointer (&self->active_snapshot, snapshot_data_unref);
g_clear_pointer (&self->last_state, state_data_unref);
g_clear_pointer (&self->last_instance, state_instance_data_unref);
g_clear_pointer (&self->since_last_state, g_timer_destroy);
g_clear_pointer (&self->objects, g_hash_table_unref);
g_clear_pointer (&self->children, g_ptr_array_unref);
g_clear_pointer (&self->allocations, g_hash_table_unref);
g_clear_pointer (&self->nonchildren, g_ptr_array_unref);
g_clear_pointer (&self->foreach_instances, g_hash_table_unref);
g_clear_pointer (&self->state_instances, g_hash_table_unref);
g_clear_pointer (&self->transition_instances, g_hash_table_unref);
g_clear_pointer (&self->transition_closure_instances, g_hash_table_unref);
g_clear_pointer (&self->track_transition_closure_instances, g_hash_table_unref);
g_clear_pointer (&self->track_transitions, g_ptr_array_unref);
g_clear_pointer (&self->bindings, g_ptr_array_unref);
g_clear_pointer (&self->watches, g_ptr_array_unref);
g_clear_pointer (&self->reference_notifier, g_object_unref);
g_clear_pointer (&self->measure_for_size_notifier, g_object_unref);
g_clear_pointer (&self->widget_width_notifier, g_object_unref);
g_clear_pointer (&self->widget_height_notifier, g_object_unref);
g_clear_pointer (&self->tick_time, g_timer_destroy);
g_clear_pointer (&self->tick_time_notifier, g_object_unref);
G_OBJECT_CLASS (bge_wdgt_renderer_parent_class)->dispose (object);
}
static void
bge_wdgt_renderer_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BgeWdgtRenderer *self = BGE_WDGT_RENDERER (object);
switch (prop_id)
{
case RENDERER_PROP_SPEC:
g_value_set_object (value, bge_wdgt_renderer_get_spec (self));
break;
case RENDERER_PROP_STATE:
g_value_set_string (value, bge_wdgt_renderer_get_state (self));
break;
case RENDERER_PROP_REFERENCE:
g_value_set_object (value, bge_wdgt_renderer_get_reference (self));
break;
case RENDERER_PROP_CHILD:
g_value_set_object (value, bge_wdgt_renderer_get_child (self));
break;
case RENDERER_PROP_RESOURCE:
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bge_wdgt_renderer_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BgeWdgtRenderer *self = BGE_WDGT_RENDERER (object);
switch (prop_id)
{
case RENDERER_PROP_SPEC:
bge_wdgt_renderer_set_spec (self, g_value_get_object (value));
break;
case RENDERER_PROP_RESOURCE:
wdgt_renderer_set_from_resource (self, g_value_get_string (value));
break;
case RENDERER_PROP_STATE:
bge_wdgt_renderer_set_state (self, g_value_get_string (value));
break;
case RENDERER_PROP_REFERENCE:
bge_wdgt_renderer_set_reference (self, g_value_get_object (value));
break;
case RENDERER_PROP_CHILD:
bge_wdgt_renderer_set_child (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bge_wdgt_renderer_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
BgeWdgtRenderer *self = BGE_WDGT_RENDERER (widget);
gboolean wdgt_measured = FALSE;
self->current_measure_for_size = for_size;
g_object_notify_by_pspec (
G_OBJECT (self->measure_for_size_notifier),
notifier_props[NOTIFIER_PROP_VALUE]);
switch (orientation)
{
case GTK_ORIENTATION_HORIZONTAL:
if (self->measure_minimum_width > 0 ||
self->measure_natural_width > 0)
{
*minimum = self->measure_minimum_width;
*natural = self->measure_natural_width;
wdgt_measured = TRUE;
}
break;
case GTK_ORIENTATION_VERTICAL:
if (self->measure_minimum_height > 0 ||
self->measure_natural_height > 0)
{
*minimum = self->measure_minimum_height;
*natural = self->measure_natural_height;
wdgt_measured = TRUE;
}
break;
default:
g_assert_not_reached ();
}
if (!wdgt_measured)
{
GtkWidget *child = NULL;
child = gtk_widget_get_first_child (GTK_WIDGET (self));
if (child != NULL)
gtk_widget_measure (
GTK_WIDGET (child),
orientation, for_size,
minimum, natural,
minimum_baseline, natural_baseline);
}
}
static void
bge_wdgt_renderer_size_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
BgeWdgtRenderer *self = BGE_WDGT_RENDERER (widget);
self->widget_width = width;
self->widget_height = height;
g_object_notify_by_pspec (
G_OBJECT (self->widget_width_notifier),
notifier_props[NOTIFIER_PROP_VALUE]);
g_object_notify_by_pspec (
G_OBJECT (self->widget_height_notifier),
notifier_props[NOTIFIER_PROP_VALUE]);
if (self->child != NULL &&
gtk_widget_should_layout (self->child))
gtk_widget_allocate (self->child, width, height, baseline, NULL);
for (guint i = 0; i < self->children->len; i++)
{
GtkWidget *child = NULL;
AllocationData *allocation = NULL;
int alloc_width = width;
int alloc_height = height;
g_autoptr (GskTransform) transform = NULL;
child = g_ptr_array_index (self->children, i);
allocation = g_hash_table_lookup (self->allocations, child);
alloc_width = resolve_value_int (
self,
allocation->width,
self->active_instance != NULL
? self->active_state
: self->spec->init_state);
alloc_height = resolve_value_int (
self,
allocation->height,
self->active_state != NULL
? self->active_state
: self->spec->init_state);
transform = resolve_value_boxed_dup (
self,
allocation->transform,
self->active_state != NULL
? self->active_state
: self->spec->init_state);
gtk_widget_allocate (
child,
MAX (alloc_width, 0.0),
MAX (alloc_height, 0.0),
baseline,
g_steal_pointer (&transform));
}
}
static gboolean
recurse_snapshot (BgeWdgtRenderer *self,
GtkSnapshot *snapshot,
GPtrArray *calls,
guint *idx,
ForeachData *foreach_context,
ForeachData *parent_foreach,
gboolean skip_foreach)
{
gboolean result = FALSE;
guint start_idx = 0;
guint n_iters = 0;
ForeachData *next_foreach = NULL;
ForeachInstanceData *foreach_instance = NULL;
g_autoptr (GListModel) model = NULL;
start_idx = *idx;
if (foreach_context != NULL)
{
for (ForeachData *data = foreach_context->parent;
data != NULL && data != parent_foreach;
data = data->parent)
{
next_foreach = foreach_context;
foreach_context = data;
}
foreach_instance = g_hash_table_lookup (
self->foreach_instances, foreach_context);
g_assert (foreach_instance != NULL);
model = resolve_value_object_dup (
self,
foreach_context->model,
self->active_state);
if (model != NULL)
n_iters = g_list_model_get_n_items (model);
else
{
n_iters = 1;
skip_foreach = TRUE;
}
}
else
n_iters = 1;
for (guint i = 0; i < n_iters; i++)
{
if (foreach_instance != NULL)
{
foreach_instance->current_idx = i;
g_object_notify_by_pspec (
G_OBJECT (foreach_instance->notifier),
notifier_props[NOTIFIER_PROP_VALUE]);
}
*idx = start_idx;
if (next_foreach != NULL)
{
result = recurse_snapshot (
self,
snapshot,
calls,
idx,
next_foreach,
foreach_context,
skip_foreach);
if (!result)
return FALSE;
}
for (; *idx < calls->len; (*idx)++)
{
SnapshotCallData *call = NULL;
call = g_ptr_array_index (calls, *idx);
if ((call->foreach_context != NULL ||
parent_foreach != NULL) &&
call->foreach_context == parent_foreach)
break;
else if (call->foreach_context != foreach_context)
{
result = recurse_snapshot (
self,
snapshot,
calls,
idx,
call->foreach_context,
foreach_context,
skip_foreach);
if (!result)
return FALSE;
}
else if (!skip_foreach)
{
switch (call->kind)
{
case BGE_WDGT_SNAPSHOT_INSTR_POP:
gtk_snapshot_pop (snapshot);
break;
case BGE_WDGT_SNAPSHOT_INSTR_SAVE:
gtk_snapshot_save (snapshot);
break;
case BGE_WDGT_SNAPSHOT_INSTR_RESTORE:
gtk_snapshot_restore (snapshot);
break;
case BGE_WDGT_SNAPSHOT_INSTR_APPEND:
case BGE_WDGT_SNAPSHOT_INSTR_PUSH:
case BGE_WDGT_SNAPSHOT_INSTR_TRANSFORM:
{
GValue arg_values[ARGBUF_SIZE] = { 0 };
guint n_arg_values = 0;
GValue rest_values[ARGBUF_SIZE] = { 0 };
guint n_rest_values = 0;
n_arg_values = MIN (call->args->len, G_N_ELEMENTS (arg_values));
n_rest_values = MIN (call->rest->len, G_N_ELEMENTS (rest_values));
for (guint j = 0; j < n_arg_values; j++)
{
ValueData *value = NULL;
GtkExpression *expression = NULL;
value = g_ptr_array_index (call->args, j);
expression = g_hash_table_lookup (self->active_state->expressions, value);
result = gtk_expression_evaluate (expression, self, &arg_values[j]);
if (!result)
return FALSE;
}
for (guint j = 0; j < n_rest_values; j++)
{
ValueData *value = NULL;
GtkExpression *expression = NULL;
value = g_ptr_array_index (call->rest, j);
expression = g_hash_table_lookup (self->active_state->expressions, value);
result = gtk_expression_evaluate (expression, self, &rest_values[j]);
if (!result)
return FALSE;
}
call->func (snapshot, arg_values, rest_values, n_rest_values);
for (guint j = 0; j < n_rest_values; j++)
{
g_value_unset (&rest_values[j]);
}
for (guint j = 0; j < n_arg_values; j++)
{
g_value_unset (&arg_values[j]);
}
}
break;
case BGE_WDGT_SNAPSHOT_INSTR_SNAPSHOT_CHILD:
{
GtkExpression *expression = NULL;
GValue child_value = G_VALUE_INIT;
GtkWidget *child = NULL;
expression = g_hash_table_lookup (
self->active_state->expressions,
call->child);
gtk_expression_evaluate (expression, self, &child_value);
child = g_value_get_object (&child_value);
if (child != NULL)
{
if (gtk_widget_get_parent (child) == GTK_WIDGET (self))
gtk_widget_snapshot_child (GTK_WIDGET (self), child, snapshot);
else
g_critical ("Trying to snapshot a widget which is "
"not a direct child of this spec! Skipping");
}
g_value_unset (&child_value);
}
break;
default:
break;
}
}
}
}
return TRUE;
}
static void
bge_wdgt_renderer_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
BgeWdgtRenderer *self = BGE_WDGT_RENDERER (widget);
GPtrArray *calls = NULL;
guint call_idx = 0;
if (self->active_state == NULL ||
self->active_instance == NULL ||
self->active_snapshot == NULL)
goto done;
calls = self->active_snapshot->calls;
recurse_snapshot (self, snapshot, calls, &call_idx, NULL, NULL, FALSE);
done:
if (self->child != NULL)
gtk_widget_snapshot_child (GTK_WIDGET (self), self->child, snapshot);
}
static void
bge_wdgt_renderer_class_init (BgeWdgtRendererClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->set_property = bge_wdgt_renderer_set_property;
object_class->get_property = bge_wdgt_renderer_get_property;
object_class->dispose = bge_wdgt_renderer_dispose;
renderer_props[RENDERER_PROP_SPEC] =
g_param_spec_object (
"spec",
NULL, NULL,
BGE_TYPE_WDGT_SPEC,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
renderer_props[RENDERER_PROP_RESOURCE] =
g_param_spec_string (
"resource",
NULL, NULL, NULL,
G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
renderer_props[RENDERER_PROP_STATE] =
g_param_spec_string (
"state",
NULL, NULL, NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
renderer_props[RENDERER_PROP_REFERENCE] =
g_param_spec_object (
"reference",
NULL, NULL,
G_TYPE_OBJECT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
renderer_props[RENDERER_PROP_CHILD] =
g_param_spec_object (
"child",
NULL, NULL,
GTK_TYPE_WIDGET,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, LAST_RENDERER_PROP, renderer_props);
widget_class->measure = bge_wdgt_renderer_measure;
widget_class->size_allocate = bge_wdgt_renderer_size_allocate;
widget_class->snapshot = bge_wdgt_renderer_snapshot;
g_type_ensure (BGE_TYPE_WDGT_NOTIFIER);
}
static gboolean
tick_cb (BgeWdgtRenderer *self,
GdkFrameClock *frame_clock,
gpointer user_data)
{
gboolean should_animate = FALSE;
GHashTableIter iter = { 0 };
gboolean finished_all_state_transitions = TRUE;
self->had_tick_since_state_switch = TRUE;
g_object_notify_by_pspec (
G_OBJECT (self->tick_time_notifier),
notifier_props[NOTIFIER_PROP_VALUE]);
should_animate = bge_should_animate (GTK_WIDGET (self));
for (guint i = 0; i < self->track_transitions->len;)
{
TrackTransitionClosureData *data = NULL;
double elapsed = 0.0;
data = g_ptr_array_index (self->track_transitions, i);
elapsed = g_timer_elapsed (data->timer, NULL);
g_object_notify_by_pspec (
G_OBJECT (data->notifier),
notifier_props[NOTIFIER_PROP_VALUE]);
if (should_animate &&
elapsed <= data->spring.est_duration)
i++;
else
g_ptr_array_remove_index (self->track_transitions, i);
}
if (should_animate &&
self->spec != NULL &&
self->active_state != NULL &&
self->active_instance != NULL &&
self->last_state != NULL &&
self->last_instance != NULL)
{
double elapsed = 0.0;
elapsed = g_timer_elapsed (self->since_last_state, NULL);
g_hash_table_iter_init (&iter, self->active_state->transitions);
for (;;)
{
ValueData *value = NULL;
TransitionData *transition = NULL;
TransitionInstanceData *transition_instance = NULL;
TransitionInstanceData *init_transition_instance = NULL;
if (!g_hash_table_iter_next (
&iter,
(gpointer *) &value,
(gpointer *) &transition))
break;
transition_instance = g_hash_table_lookup (self->active_instance->transitions, value);
if (transition_instance != NULL)
g_object_notify_by_pspec (
G_OBJECT (transition_instance->notifier),
notifier_props[NOTIFIER_PROP_VALUE]);
init_transition_instance = g_hash_table_lookup (self->init_instance->transitions, value);
if (init_transition_instance != NULL)
g_object_notify_by_pspec (
G_OBJECT (init_transition_instance->notifier),
notifier_props[NOTIFIER_PROP_VALUE]);
switch (transition->kind)
{
case TRANSITION_EASE:
if (elapsed <= transition_instance->ease.seconds)
finished_all_state_transitions = FALSE;
break;
case TRANSITION_SPRING:
if (transition_instance->spring.est_duration < 0.0 ||
elapsed <= transition_instance->spring.est_duration)
finished_all_state_transitions = FALSE;
break;
default:
g_assert_not_reached ();
}
}
}
if (finished_all_state_transitions)
{
g_clear_pointer (&self->last_state, state_data_unref);
g_clear_pointer (&self->last_instance, state_instance_data_unref);
}
if ((self->active_state == NULL ||
!self->active_state->needs_tick) &&
self->track_transitions->len == 0 &&
finished_all_state_transitions)
{
gtk_widget_remove_tick_callback (GTK_WIDGET (self), self->tick);
self->tick = 0;
return G_SOURCE_REMOVE;
}
return G_SOURCE_CONTINUE;
}
static void
bge_wdgt_renderer_init (BgeWdgtRenderer *self)
{
self->init_watches = g_ptr_array_new_with_free_func (discard_watch);
self->objects = g_hash_table_new_full (
g_direct_hash, g_direct_equal,
value_data_unref, g_object_unref);
self->children = g_ptr_array_new_with_free_func (
(GDestroyNotify) gtk_widget_unparent);
self->nonchildren = g_ptr_array_new_with_free_func (
(GDestroyNotify) g_object_unref);
self->allocations = g_hash_table_new_full (
g_direct_hash, g_direct_equal,
g_object_unref, allocation_data_unref);
self->foreach_instances = g_hash_table_new_full (
g_direct_hash, g_direct_equal,
foreach_data_unref, NULL);
self->state_instances = g_hash_table_new_full (
g_direct_hash, g_direct_equal,
state_data_unref, state_instance_data_unref);
self->transition_instances = g_hash_table_new_full (
g_direct_hash, g_direct_equal,
transition_data_unref, transition_instance_data_unref);
self->transition_closure_instances = g_hash_table_new_full (
g_direct_hash, g_direct_equal,
transition_data_unref, transition_closure_data_unref);
self->track_transition_closure_instances = g_hash_table_new_full (
g_direct_hash, g_direct_equal,
value_data_unref, track_transition_closure_data_unref);
self->track_transitions = g_ptr_array_new_with_free_func (
track_transition_closure_data_unref);
self->bindings = g_ptr_array_new_with_free_func (discard_binding);
self->watches = g_ptr_array_new_with_free_func (discard_watch);
self->since_last_state = g_timer_new ();
self->tick_time = g_timer_new ();
self->reference_notifier = g_object_new (BGE_TYPE_WDGT_NOTIFIER, NULL);
self->measure_for_size_notifier = g_object_new (BGE_TYPE_WDGT_NOTIFIER, NULL);
self->widget_width_notifier = g_object_new (BGE_TYPE_WDGT_NOTIFIER, NULL);
self->widget_height_notifier = g_object_new (BGE_TYPE_WDGT_NOTIFIER, NULL);
self->tick_time_notifier = g_object_new (BGE_TYPE_WDGT_NOTIFIER, NULL);
}
BgeWdgtRenderer *
bge_wdgt_renderer_new (void)
{
return g_object_new (BGE_TYPE_WDGT_RENDERER, NULL);
}
BgeWdgtSpec *
bge_wdgt_renderer_get_spec (BgeWdgtRenderer *self)
{
g_return_val_if_fail (BGE_IS_WDGT_RENDERER (self), NULL);
return self->spec;
}
const char *
bge_wdgt_renderer_get_state (BgeWdgtRenderer *self)
{
g_return_val_if_fail (BGE_IS_WDGT_RENDERER (self), NULL);
return self->state;
}
GObject *
bge_wdgt_renderer_get_reference (BgeWdgtRenderer *self)
{
g_return_val_if_fail (BGE_IS_WDGT_RENDERER (self), NULL);
return self->reference;
}
GtkWidget *
bge_wdgt_renderer_get_child (BgeWdgtRenderer *self)
{
g_return_val_if_fail (BGE_IS_WDGT_RENDERER (self), NULL);
return self->child;
}
void
bge_wdgt_renderer_set_spec (BgeWdgtRenderer *self,
BgeWdgtSpec *spec)
{
GBinding *name_binding = NULL;
g_return_if_fail (BGE_IS_WDGT_RENDERER (self));
g_return_if_fail (spec == NULL || BGE_IS_WDGT_SPEC (spec));
if (spec == self->spec)
return;
g_clear_pointer (&self->spec, g_object_unref);
if (spec != NULL)
self->spec = g_object_ref (spec);
regenerate (self);
apply_state (self);
if (spec != NULL)
{
name_binding = g_object_bind_property (spec, "name", self, "name", G_BINDING_SYNC_CREATE);
g_ptr_array_add (self->bindings, g_object_ref (name_binding));
}
else
gtk_widget_set_name (GTK_WIDGET (self), NULL);
g_object_notify_by_pspec (G_OBJECT (self), renderer_props[RENDERER_PROP_SPEC]);
}
void
bge_wdgt_renderer_set_state (BgeWdgtRenderer *self,
const char *state)
{
g_return_if_fail (BGE_IS_WDGT_RENDERER (self));
if (state == self->state || (state != NULL && self->state != NULL && g_strcmp0 (state, self->state) == 0))
return;
g_clear_pointer (&self->state, g_free);
if (state != NULL)
self->state = g_strdup (state);
apply_state (self);
g_object_notify_by_pspec (G_OBJECT (self), renderer_props[RENDERER_PROP_STATE]);
}
void
bge_wdgt_renderer_set_reference (BgeWdgtRenderer *self,
GObject *reference)
{
g_return_if_fail (BGE_IS_WDGT_RENDERER (self));
if (reference == self->reference)
return;
g_clear_pointer (&self->reference, g_object_unref);
if (reference != NULL)
self->reference = g_object_ref (reference);
g_object_notify_by_pspec (G_OBJECT (self), renderer_props[RENDERER_PROP_REFERENCE]);
g_object_notify_by_pspec (G_OBJECT (self->reference_notifier), notifier_props[NOTIFIER_PROP_VALUE]);
}
void
bge_wdgt_renderer_set_child (BgeWdgtRenderer *self,
GtkWidget *child)
{
g_return_if_fail (BGE_IS_WDGT_RENDERER (self));
if (self->child == child)
return;
if (child != NULL)
g_return_if_fail (gtk_widget_get_parent (child) == NULL);
g_clear_pointer (&self->child, gtk_widget_unparent);
self->child = child;
if (child != NULL)
gtk_widget_set_parent (child, GTK_WIDGET (self));
g_object_notify_by_pspec (G_OBJECT (self), renderer_props[RENDERER_PROP_CHILD]);
}
void
bge_wdgt_renderer_set_state_take (BgeWdgtRenderer *self,
char *state)
{
g_return_if_fail (BGE_IS_WDGT_RENDERER (self));
if (state != NULL && self->state != NULL && g_strcmp0 (state, self->state) == 0)
{
g_free (state);
return;
}
g_clear_pointer (&self->state, g_free);
if (state != NULL)
self->state = state;
g_object_notify_by_pspec (G_OBJECT (self), renderer_props[RENDERER_PROP_STATE]);
}
gpointer
bge_wdgt_renderer_lookup_object (BgeWdgtRenderer *self,
const char *name)
{
ValueData *value = NULL;
g_return_val_if_fail (BGE_IS_WDGT_RENDERER (self), NULL);
g_return_val_if_fail (name != NULL, NULL);
if (self->spec == NULL)
return NULL;
value = g_hash_table_lookup (self->spec->values, name);
if (value == NULL)
return NULL;
if (!g_type_is_a (value->type, G_TYPE_OBJECT))
return NULL;
return resolve_value_object_dup (
self, value,
self->active_state != NULL
? self->active_state
: self->spec->init_state);
}
static void
regenerate (BgeWdgtRenderer *self)
{
BgeWdgtSpec *spec = self->spec;
g_autoptr (GtkBuilder) dummy_builder = NULL;
GHashTableIter state_iter = { 0 };
GHashTableIter setters_iter = { 0 };
g_clear_pointer (&self->init_instance, state_instance_data_unref);
g_ptr_array_set_size (self->init_watches, 0);
g_hash_table_remove_all (self->state_instances);
g_hash_table_remove_all (self->objects);
g_ptr_array_set_size (self->children, 0);
g_ptr_array_set_size (self->nonchildren, 0);
g_hash_table_remove_all (self->allocations);
g_hash_table_remove_all (self->foreach_instances);
g_ptr_array_set_size (self->track_transitions, 0);
g_clear_pointer (&self->last_state, state_data_unref);
g_clear_pointer (&self->last_instance, state_instance_data_unref);
g_clear_pointer (&self->active_state, state_data_unref);
g_clear_pointer (&self->active_instance, state_instance_data_unref);
g_clear_pointer (&self->active_snapshot, snapshot_data_unref);
g_ptr_array_set_size (self->bindings, 0);
g_ptr_array_set_size (self->watches, 0);
self->measure_minimum_width = -1;
self->measure_natural_width = -1;
self->measure_minimum_height = -1;
self->measure_natural_height = -1;
if (self->spec == NULL)
return;
for (guint i = 0; i < spec->foreaches->len; i++)
{
ForeachData *data = NULL;
g_autoptr (ForeachInstanceData) foreach_instance = NULL;
data = g_ptr_array_index (spec->foreaches, i);
foreach_instance = foreach_instance_data_new ();
foreach_instance->notifier = g_object_new (BGE_TYPE_WDGT_NOTIFIER, NULL);
g_hash_table_replace (self->foreach_instances,
foreach_data_ref (data),
foreach_instance_data_ref (foreach_instance));
}
for (guint i = 0; i < spec->nonchildren->len; i++)
{
ValueData *value = NULL;
value = g_ptr_array_index (spec->nonchildren, i);
spawn_nonchild (self, value);
}
dummy_builder = gtk_builder_new ();
for (guint i = 0; i < spec->children->len; i++)
{
ValueData *value = NULL;
value = g_ptr_array_index (spec->children, i);
spawn_child (self, value, dummy_builder);
}
g_hash_table_iter_init (&state_iter, spec->states);
for (;;)
{
char *state_name = NULL;
StateData *state = NULL;
g_autoptr (StateInstanceData) instance = NULL;
GHashTableIter transitions_iter = { 0 };
if (!g_hash_table_iter_next (
&state_iter,
(gpointer *) &state_name,
(gpointer *) &state))
break;
instance = state_instance_data_new ();
instance->transitions = g_hash_table_new_full (
g_direct_hash, g_direct_equal,
value_data_unref, transition_instance_data_unref);
g_hash_table_replace (self->state_instances,
state_data_ref (state),
state_instance_data_ref (instance));
g_hash_table_iter_init (&transitions_iter, state->transitions);
for (;;)
{
ValueData *value = NULL;
TransitionData *transition = NULL;
g_autoptr (BgeWdgtNotifier) notifier_object = NULL;
g_autoptr (TransitionInstanceData) instance_data = NULL;
g_autoptr (TransitionClosureData) closure_data = NULL;
if (!g_hash_table_iter_next (
&transitions_iter,
(gpointer *) &value,
(gpointer *) &transition))
break;
notifier_object = g_object_new (BGE_TYPE_WDGT_NOTIFIER, NULL);
instance_data = transition_instance_data_new ();
instance_data->notifier = g_object_ref (notifier_object);
switch (transition->kind)
{
case TRANSITION_EASE:
instance_data->elapsed = G_MAXINT;
break;
case TRANSITION_SPRING:
instance_data->spring.est_duration = -1.0;
break;
default:
g_assert_not_reached ();
}
g_hash_table_replace (instance->transitions,
value_data_ref (value),
transition_instance_data_ref (instance_data));
g_hash_table_replace (self->transition_instances,
transition_data_ref (transition),
transition_instance_data_ref (instance_data));
closure_data = transition_closure_data_new ();
closure_data->state = state_data_ref (state);
closure_data->instance = state_instance_data_ref (instance);
closure_data->value = value_data_ref (value);
g_hash_table_replace (self->transition_closure_instances,
transition_data_ref (transition),
transition_closure_data_ref (closure_data));
}
for (guint i = 0; i < spec->track_transition_values->len; i++)
{
ValueData *value = NULL;
g_autoptr (TrackTransitionClosureData) closure_data = NULL;
value = g_ptr_array_index (spec->track_transition_values, i);
closure_data = track_transition_closure_data_new ();
closure_data->value = value_data_ref (value);
closure_data->timer = g_timer_new ();
closure_data->notifier = g_object_new (BGE_TYPE_WDGT_NOTIFIER, NULL);
g_hash_table_replace (self->track_transition_closure_instances,
value_data_ref (value),
track_transition_closure_data_ref (closure_data));
}
if (state == spec->init_state)
self->init_instance = state_instance_data_ref (instance);
}
if (!spec->ready)
{
g_hash_table_iter_init (&state_iter, spec->states);
for (;;)
{
GHashTableIter value_iter = { 0 };
char *state_name = NULL;
StateData *state = NULL;
if (!g_hash_table_iter_next (
&state_iter,
(gpointer *) &state_name,
(gpointer *) &state))
break;
g_hash_table_iter_init (&value_iter, spec->values);
for (;;)
{
char *value_name = NULL;
ValueData *value = NULL;
g_autoptr (GtkExpression) expression = NULL;
if (!g_hash_table_iter_next (
&value_iter,
(gpointer *) &value_name,
(gpointer *) &value))
break;
expression = ensure_expressions (spec, value, state, NULL);
}
for (guint i = 0; i < spec->anon_values->len; i++)
{
ValueData *value = NULL;
g_autoptr (GtkExpression) expression = NULL;
value = g_ptr_array_index (spec->anon_values, i);
expression = ensure_expressions (spec, value, state, NULL);
}
for (guint i = 0; i < spec->foreaches->len; i++)
{
ForeachData *data = NULL;
data = g_ptr_array_index (spec->foreaches, i);
{
g_autoptr (GtkExpression) expression =
ensure_expressions (spec, data->model, state, NULL);
}
g_hash_table_iter_init (&value_iter, data->values);
for (;;)
{
char *value_name = NULL;
ValueData *value = NULL;
g_autoptr (GtkExpression) expression = NULL;
if (!g_hash_table_iter_next (
&value_iter,
(gpointer *) &value_name,
(gpointer *) &value))
break;
expression = ensure_expressions (spec, value, state, NULL);
}
}
}
g_hash_table_iter_init (&state_iter, spec->states);
for (;;)
{
char *state_name = NULL;
StateData *state = NULL;
StateData *snapshot_state = NULL;
if (!g_hash_table_iter_next (
&state_iter,
(gpointer *) &state_name,
(gpointer *) &state))
break;
g_hash_table_iter_init (
&setters_iter,
state->setters);
for (;;)
{
ValueData *dest = NULL;
ValueData *src = NULL;
if (!g_hash_table_iter_next (
&setters_iter,
(gpointer *) &dest,
(gpointer *) &src))
break;
if (g_hash_table_contains (state->values_need_tick, src))
state->needs_tick = TRUE;
}
if (state->snapshot != NULL)
snapshot_state = state;
else
on using the init state */
snapshot_state = self->spec->init_state;
for (guint i = 0; i < snapshot_state->snapshot->calls->len; i++)
{
SnapshotCallData *call = NULL;
call = g_ptr_array_index (snapshot_state->snapshot->calls, i);
if (call->args == NULL)
continue;
for (guint j = 0; j < call->args->len; j++)
{
ValueData *arg = NULL;
gboolean found = FALSE;
GtkExpression *expression = NULL;
arg = g_ptr_array_index (call->args, j);
expression = g_hash_table_lookup (state->expressions, arg);
g_assert (expression != NULL);
for (guint k = 0; k < state->snapshot_deps->len; k++)
{
GtkExpression *other = NULL;
other = g_ptr_array_index (state->snapshot_deps, k);
if (expression == other)
{
found = TRUE;
break;
}
}
if (found)
continue;
if (g_hash_table_contains (state->values_need_tick, arg))
state->needs_tick = TRUE;
g_ptr_array_add (state->snapshot_deps,
gtk_expression_ref (expression));
}
}
}
spec->ready = TRUE;
}
g_hash_table_iter_init (
&setters_iter,
self->spec->init_state->setters);
for (;;)
{
ValueData *dest = NULL;
ValueData *src = NULL;
if (!g_hash_table_iter_next (
&setters_iter,
(gpointer *) &dest,
(gpointer *) &src))
break;
set_value (
self,
self->spec->init_state,
self->init_instance,
dest,
src,
self->init_watches);
}
gtk_widget_queue_allocate (GTK_WIDGET (self));
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
spawn_nonchild (BgeWdgtRenderer *self,
ValueData *value)
{
g_autoptr (GObject) object = NULL;
g_assert (value->kind == VALUE_OBJECT);
object = g_object_new (value->type, NULL);
if (g_type_is_a (value->type, G_TYPE_INITIALLY_UNOWNED))
g_object_ref_sink (object);
g_hash_table_replace (self->objects,
value_data_ref (value),
g_object_ref (object));
g_ptr_array_add (self->nonchildren, g_object_ref (object));
}
static void
spawn_child (BgeWdgtRenderer *self,
ValueData *value,
GtkBuilder *dummy_builder)
{
GtkWidget *widget = NULL;
g_autoptr (AllocationData) allocation = NULL;
g_assert (value->kind == VALUE_CHILD);
widget = g_object_new (
value->type,
"name", value->name,
NULL);
if (value->child.css_classes != NULL)
{
for (guint j = 0; j < value->child.css_classes->len; j++)
{
const char *class = NULL;
class = g_ptr_array_index (value->child.css_classes, j);
gtk_widget_add_css_class (widget, class);
}
}
if (value->child.parent_widget != NULL)
{
GtkWidget *parent_widget = NULL;
g_autoptr (GtkBuilder) tmp_builder = NULL;
parent_widget = g_hash_table_lookup (
self->objects,
value->child.parent_widget);
g_assert (parent_widget != NULL);
if (dummy_builder == NULL)
tmp_builder = gtk_builder_new ();
GTK_BUILDABLE_GET_IFACE (parent_widget)
->add_child (
GTK_BUILDABLE (parent_widget),
dummy_builder != NULL
? dummy_builder
: tmp_builder,
G_OBJECT (widget),
value->child.builder_type);
}
else
{
gtk_widget_set_parent (widget, GTK_WIDGET (self));
g_ptr_array_add (self->children, g_object_ref (widget));
}
g_hash_table_replace (self->objects,
value_data_ref (value),
g_object_ref (widget));
allocation = allocation_data_new ();
g_hash_table_replace (self->allocations,
g_object_ref (widget),
allocation_data_ref (allocation));
}
static void
apply_state (BgeWdgtRenderer *self)
{
BgeWdgtSpec *spec = self->spec;
StateData *state = NULL;
StateInstanceData *instance = NULL;
GHashTableIter iter = { 0 };
if (self->had_tick_since_state_switch)
{
g_clear_pointer (&self->last_state, state_data_unref);
g_clear_pointer (&self->last_instance, state_instance_data_unref);
if (self->active_state != NULL &&
self->active_instance != NULL)
{
self->last_state = g_steal_pointer (&self->active_state);
self->last_instance = g_steal_pointer (&self->active_instance);
}
g_timer_start (self->since_last_state);
self->had_tick_since_state_switch = FALSE;
}
g_clear_pointer (&self->active_state, state_data_unref);
g_clear_pointer (&self->active_instance, state_instance_data_unref);
g_clear_pointer (&self->active_snapshot, snapshot_data_unref);
g_ptr_array_set_size (self->bindings, 0);
g_ptr_array_set_size (self->watches, 0);
gtk_widget_queue_draw (GTK_WIDGET (self));
if (self->spec == NULL ||
self->state == NULL)
return;
state = g_hash_table_lookup (spec->states, self->state);
if (state == NULL)
{
g_critical ("state \"%s\" doesn't exist on spec \"%s\"",
self->state, spec->name);
return;
}
instance = g_hash_table_lookup (self->state_instances, state);
g_assert (instance != NULL);
self->active_state = state_data_ref (state);
self->active_instance = state_instance_data_ref (instance);
if (state->snapshot != NULL)
self->active_snapshot = snapshot_data_ref (state->snapshot);
else if (self->spec->init_state->snapshot != NULL)
self->active_snapshot = snapshot_data_ref (self->spec->init_state->snapshot);
g_hash_table_iter_init (&iter, state->setters);
for (;;)
{
ValueData *dest = NULL;
ValueData *src = NULL;
TransitionInstanceData *transition_instance = NULL;
if (!g_hash_table_iter_next (
&iter,
(gpointer *) &dest,
(gpointer *) &src))
break;
set_value (
self,
state,
instance,
dest,
src,
self->watches);
transition_instance = g_hash_table_lookup (instance->transitions, dest);
if (transition_instance != NULL)
transition_instance->spring.est_duration = -1.0;
}
g_hash_table_iter_init (&iter, spec->init_state->setters);
for (;;)
{
ValueData *dest = NULL;
ValueData *src = NULL;
if (!g_hash_table_iter_next (
&iter,
(gpointer *) &dest,
(gpointer *) &src))
break;
set_value (
self,
state,
instance,
dest,
src,
self->watches);
}
for (guint i = 0; i < state->snapshot_deps->len; i++)
{
GtkExpression *expression = NULL;
GtkExpressionWatch *watch = NULL;
expression = g_ptr_array_index (state->snapshot_deps, i);
watch = gtk_expression_watch (
expression,
self,
(GtkExpressionNotify) prop_change_queue_draw,
self, NULL);
g_ptr_array_add (self->watches, watch);
}
gtk_widget_queue_allocate (GTK_WIDGET (self));
gtk_widget_queue_draw (GTK_WIDGET (self));
if (self->tick == 0)
self->tick = gtk_widget_add_tick_callback (
GTK_WIDGET (self),
(GtkTickCallback) tick_cb,
NULL, NULL);
}
static graphene_point_t *
expression_create_point (gpointer object,
double x,
double y,
gpointer user_data)
{
graphene_point_t *point = NULL;
point = graphene_point_alloc ();
point->x = x;
point->y = y;
return point;
}
static graphene_size_t *
expression_create_size (gpointer object,
double width,
double height,
gpointer user_data)
{
graphene_size_t *size = NULL;
size = graphene_size_alloc ();
size->width = width;
size->height = height;
return size;
}
static graphene_point3d_t *
expression_create_point3d (gpointer object,
double x,
double y,
double z,
gpointer user_data)
{
graphene_point3d_t *point3d = NULL;
point3d = graphene_point3d_alloc ();
point3d->x = x;
point3d->y = y;
point3d->z = z;
return point3d;
}
static graphene_rect_t *
expression_create_rect (gpointer object,
double x,
double y,
double width,
double height,
gpointer user_data)
{
graphene_rect_t *rect = NULL;
rect = graphene_rect_alloc ();
rect->origin.x = x;
rect->origin.y = y;
rect->size.width = width;
rect->size.height = height;
return rect;
}
static GskStroke *
expression_create_stroke (gpointer object,
double line_width,
GskLineCap line_cap,
GskLineJoin line_join,
double miter_limit,
gpointer user_data)
{
GskStroke *stroke = NULL;
stroke = gsk_stroke_new (line_width);
gsk_stroke_set_line_cap (stroke, line_cap);
gsk_stroke_set_line_join (stroke, line_join);
gsk_stroke_set_miter_limit (stroke, miter_limit);
return stroke;
}
static void
expression_coerce_type (gpointer this,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer dest_type_ptr)
{
GType dest_type = GPOINTER_TO_SIZE (dest_type_ptr);
coerce_value (¶m_values[0], dest_type, return_value);
}
static gpointer
expression_lookup_object (BgeWdgtRenderer *this,
ValueData *value)
{
return g_object_ref (g_hash_table_lookup (this->objects, value));
}
static gpointer
expression_get_reference_notifier (BgeWdgtRenderer *this,
gpointer user_data)
{
return g_object_ref (this->reference_notifier);
}
static gpointer
expression_get_measure_for_size_notifier (BgeWdgtRenderer *this,
gpointer user_data)
{
return g_object_ref (this->measure_for_size_notifier);
}
static gpointer
expression_get_widget_width_notifier (BgeWdgtRenderer *this,
gpointer user_data)
{
return g_object_ref (this->widget_width_notifier);
}
static gpointer
expression_get_widget_height_notifier (BgeWdgtRenderer *this,
gpointer user_data)
{
return g_object_ref (this->widget_height_notifier);
}
static gpointer
expression_get_tick_time_notifier (BgeWdgtRenderer *this,
gpointer user_data)
{
return g_object_ref (this->tick_time_notifier);
}
static gpointer
expression_get_foreach_instance_notifier (BgeWdgtRenderer *this,
ForeachData *data)
{
ForeachInstanceData *instance = NULL;
instance = g_hash_table_lookup (this->foreach_instances, data);
g_assert (instance != NULL);
return g_object_ref (instance->notifier);
}
static gpointer
expression_get_transition_instance_notifier (BgeWdgtRenderer *this,
TransitionData *data)
{
TransitionInstanceData *instance = NULL;
instance = g_hash_table_lookup (this->transition_instances, data);
g_assert (instance != NULL);
return g_object_ref (instance->notifier);
}
static gpointer
expression_get_track_transition_closure_instance_notifier (BgeWdgtRenderer *this,
ValueData *value)
{
TrackTransitionClosureData *instance = NULL;
instance = g_hash_table_lookup (this->track_transition_closure_instances, value);
g_assert (instance != NULL);
return g_object_ref (instance->notifier);
}
static guint
expression_get_foreach_index (BgeWdgtRenderer *this,
double notify,
ForeachData *data)
{
ForeachInstanceData *instance = NULL;
instance = g_hash_table_lookup (this->foreach_instances, data);
g_assert (instance != NULL);
return instance->current_idx;
}
static gpointer
expression_get_model_item (BgeWdgtRenderer *this,
GListModel *model,
guint idx,
double notify,
gpointer user_data)
{
return g_list_model_get_item (model, idx);
}
static int
expression_get_measure_for_size (BgeWdgtRenderer *this,
double notify,
gpointer user_data)
{
return this->current_measure_for_size;
}
static double
expression_get_widget_width (BgeWdgtRenderer *this,
double notify,
gpointer user_data)
{
return (double) this->widget_width;
}
static double
expression_get_widget_height (BgeWdgtRenderer *this,
double notify,
gpointer user_data)
{
return (double) this->widget_height;
}
static double
expression_get_tick_time (BgeWdgtRenderer *this,
double notify,
gpointer user_data)
{
return g_timer_elapsed (this->tick_time, NULL);
}
static gpointer
expression_get_reference_object (BgeWdgtRenderer *this,
double notify,
gpointer user_data)
{
return this->reference != NULL
? g_object_ref (this->reference)
: NULL;
}
static double
expression_adjust_state_transition (BgeWdgtRenderer *this,
double in,
double notifier,
TransitionData *data)
{
TransitionClosureData *closure_data = NULL;
TransitionData *transition = NULL;
TransitionData *last_transition = NULL;
TransitionInstanceData *transition_instance = NULL;
TransitionInstanceData *last_transition_instance = NULL;
ValueData *last_in_value = NULL;
double last_in = 0.0;
double elapsed = 0.0;
double progress = 0.0;
double interpolated_number = 0.0;
closure_data = g_hash_table_lookup (this->transition_closure_instances, data);
g_assert (closure_data != NULL);
g_assert (closure_data->value->type == G_TYPE_DOUBLE);
if (!bge_should_animate (GTK_WIDGET (this)))
return in;
if (this->active_state == NULL ||
this->active_instance == NULL ||
this->last_state == NULL ||
this->last_instance == NULL)
return in;
transition = g_hash_table_lookup (
this->active_state->transitions, closure_data->value);
if (transition == NULL)
return in;
last_transition = g_hash_table_lookup (
this->last_state->transitions, closure_data->value);
transition_instance = g_hash_table_lookup (
this->active_instance->transitions, closure_data->value);
g_assert (transition_instance != NULL);
last_transition_instance = g_hash_table_lookup (
this->last_instance->transitions, closure_data->value);
if (last_transition != NULL &&
last_transition_instance != NULL &&
last_transition->kind == TRANSITION_EASE &&
last_transition_instance->elapsed < last_transition_instance->ease.seconds)
last_in = last_transition_instance->value;
else
{
last_in_value = g_hash_table_lookup (this->last_state->setters, closure_data->value);
if (last_in_value == NULL)
return in;
last_in = resolve_value_double (this, last_in_value, this->last_state);
}
elapsed = g_timer_elapsed (this->since_last_state, NULL);
transition_instance->elapsed = elapsed;
switch (transition->kind)
{
case TRANSITION_EASE:
{
BgeEasing easing = 0;
double seconds = 0.0;
easing = resolve_value_enum (
this,
transition->ease.easing,
this->active_state);
seconds = resolve_value_double (
this,
transition->ease.seconds,
this->active_state);
if (elapsed >= seconds)
interpolated_number = in;
else
{
progress = bge_easing_ease (easing, elapsed / seconds);
interpolated_number = last_in + progress * (in - last_in);
}
transition_instance->ease.easing = easing;
transition_instance->ease.seconds = seconds;
}
break;
case TRANSITION_SPRING:
{
double damping_ratio = 0.0;
double mass = 0.0;
double stiffness = 0.0;
double damping = 0.0;
damping_ratio = resolve_value_double (
this,
transition->spring.damping_ratio,
this->active_state);
mass = resolve_value_double (
this,
transition->spring.mass,
this->active_state);
stiffness = resolve_value_double (
this,
transition->spring.stiffness,
this->active_state);
damping = damping_ratio *
(
2 * sqrt (mass * stiffness));
if (transition_instance->spring.est_duration < 0.0 ||
damping_ratio != transition_instance->spring.cache_damping_ratio ||
mass != transition_instance->spring.cache_mass ||
stiffness != transition_instance->spring.cache_stiffness)
{
if (last_transition != NULL &&
last_transition->kind == TRANSITION_SPRING)
transition_instance->spring.velocity = last_transition_instance->spring.velocity;
else
transition_instance->spring.velocity = 0.0;
transition_instance->spring.est_duration = spring_calculate_duration (
damping,
mass,
stiffness,
last_in,
in,
FALSE);
transition_instance->spring.cache_damping_ratio = damping_ratio;
transition_instance->spring.cache_mass = mass;
transition_instance->spring.cache_stiffness = stiffness;
}
if (elapsed >= transition_instance->spring.est_duration)
interpolated_number = in;
else
interpolated_number = spring_oscillate (
damping,
mass,
stiffness,
last_in,
in,
elapsed,
&transition_instance->spring.velocity);
}
break;
default:
g_assert_not_reached ();
}
transition_instance->value = interpolated_number;
return interpolated_number;
}
static double
expression_adjust_track_transition (BgeWdgtRenderer *this,
double in,
double damping_ratio,
double mass,
double stiffness,
double notifier,
ValueData *value)
{
TrackTransitionClosureData *closure_data = NULL;
guint idx = 0;
gboolean registered = FALSE;
double damping = 0.0;
g_assert (value->type == G_TYPE_DOUBLE);
if (!bge_should_animate (GTK_WIDGET (this)))
return in;
closure_data = g_hash_table_lookup (this->track_transition_closure_instances, value);
g_assert (closure_data != NULL);
registered = g_ptr_array_find (this->track_transitions, closure_data, &idx);
if (!registered)
{
g_ptr_array_add (this->track_transitions,
track_transition_closure_data_ref (closure_data));
if (this->tick == 0)
this->tick = gtk_widget_add_tick_callback (
GTK_WIDGET (this),
(GtkTickCallback) tick_cb,
NULL, NULL);
}
damping = damping_ratio *
(
2 * sqrt (mass * stiffness));
if (!registered ||
closure_data->target != in ||
damping_ratio != closure_data->spring.cache_damping_ratio ||
mass != closure_data->spring.cache_mass ||
stiffness != closure_data->spring.cache_stiffness)
{
closure_data->target = in;
closure_data->last = closure_data->current;
closure_data->elapsed = 0.0;
g_timer_start (closure_data->timer);
closure_data->spring.est_duration = spring_calculate_duration (
damping,
mass,
stiffness,
closure_data->last,
closure_data->target,
FALSE);
closure_data->spring.cache_damping_ratio = damping_ratio;
closure_data->spring.cache_mass = mass;
closure_data->spring.cache_stiffness = stiffness;
}
else
closure_data->elapsed = g_timer_elapsed (closure_data->timer, NULL);
if (closure_data->elapsed >= closure_data->spring.est_duration)
{
closure_data->current = closure_data->target;
closure_data->last = closure_data->target;
}
else
closure_data->current = spring_oscillate (
damping,
mass,
stiffness,
closure_data->last,
closure_data->target,
closure_data->elapsed,
&closure_data->spring.velocity);
return closure_data->current;
}
static GskTransform *
expression_perform_transform (gpointer this,
guint n_param_values,
const GValue *param_values,
TransformCallFunc func)
{
return func (g_value_dup_boxed (¶m_values[0]), param_values + 1);
}
static GskPath *
expression_perform_path_build (gpointer this,
guint n_param_values,
const GValue *param_values,
ValueData *path_value)
{
g_autoptr (GskPathBuilder) builder = NULL;
const GValue *cursor = 0;
g_assert (path_value->kind == VALUE_PATH);
builder = gsk_path_builder_new ();
cursor = param_values;
for (guint i = 0; i < path_value->path.argss->len; i++)
{
PathBuilderCallFunc func = NULL;
GPtrArray *args = NULL;
func = g_ptr_array_index (path_value->path.funcs, i);
args = g_ptr_array_index (path_value->path.argss, i);
func (builder, cursor);
cursor += args->len;
}
return gsk_path_builder_to_path (builder);
}
static GtkExpression *
ensure_expressions (BgeWdgtSpec *self,
ValueData *value,
StateData *state,
gboolean *value_needs_tick)
{
GtkExpression *cached = NULL;
g_autoptr (GtkExpression) expression = NULL;
TransitionData *transition = NULL;
gboolean need_tick = FALSE;
cached = g_hash_table_lookup (state->expressions, value);
if (cached != NULL)
{
if (value_needs_tick != NULL &&
g_hash_table_contains (state->values_need_tick, value))
*value_needs_tick = TRUE;
return gtk_expression_ref (cached);
}
switch (value->kind)
{
case VALUE_CONSTANT:
expression = gtk_constant_expression_new_for_value (&value->constant.value);
break;
case VALUE_OBJECT:
case VALUE_CHILD:
expression = gtk_cclosure_expression_new (
value->type,
bge_marshal_OBJECT__VOID,
0, (GtkExpression *[]){},
G_CALLBACK (expression_lookup_object),
value_data_ref (value),
value_data_unref_closure);
break;
case VALUE_REFERENCE_OBJECT:
{
GtkExpression *notifier_constant = NULL;
GtkExpression *notify_expression = NULL;
notifier_constant = gtk_cclosure_expression_new (
BGE_TYPE_WDGT_NOTIFIER,
bge_marshal_OBJECT__VOID,
0, (GtkExpression *[]){},
G_CALLBACK (expression_get_reference_notifier),
NULL, NULL);
notify_expression = gtk_property_expression_new_for_pspec (
notifier_constant, notifier_props[NOTIFIER_PROP_VALUE]);
expression = gtk_cclosure_expression_new (
value->type,
bge_marshal_OBJECT__DOUBLE,
1, (GtkExpression *[]){ notify_expression },
G_CALLBACK (expression_get_reference_object),
GSIZE_TO_POINTER (value->type),
NULL);
}
break;
case VALUE_ITERATOR:
{
GtkExpression *notifier_constant = NULL;
GtkExpression *notify_expression = NULL;
notifier_constant = gtk_cclosure_expression_new (
BGE_TYPE_WDGT_NOTIFIER,
bge_marshal_OBJECT__VOID,
0, (GtkExpression *[]){},
G_CALLBACK (expression_get_foreach_instance_notifier),
foreach_data_ref (value->iterator.context),
foreach_data_unref_closure);
notify_expression = gtk_property_expression_new_for_pspec (
notifier_constant, notifier_props[NOTIFIER_PROP_VALUE]);
expression = gtk_cclosure_expression_new (
G_TYPE_UINT,
bge_marshal_UINT__DOUBLE,
1, (GtkExpression *[]){ notify_expression },
G_CALLBACK (expression_get_foreach_index),
foreach_data_ref (value->iterator.context),
foreach_data_unref_closure);
if (value->type != G_TYPE_UINT)
{
GtkExpression *model_expression = NULL;
g_assert (g_type_is_a (value->type, G_TYPE_OBJECT));
model_expression = ensure_expressions (
self,
value->iterator.context->model,
state,
&need_tick);
expression = gtk_cclosure_expression_new (
value->type,
bge_marshal_OBJECT__OBJECT_UINT_DOUBLE,
3, (GtkExpression *[]){ model_expression, expression, notify_expression },
G_CALLBACK (expression_get_model_item),
NULL, NULL);
}
}
break;
case VALUE_VARIABLE:
case VALUE_ALLOCATION_WIDTH:
case VALUE_ALLOCATION_HEIGHT:
case VALUE_ALLOCATION_TRANSFORM:
case VALUE_MEASURE_MINIMUM_WIDTH:
case VALUE_MEASURE_NATURAL_WIDTH:
case VALUE_MEASURE_MINIMUM_HEIGHT:
case VALUE_MEASURE_NATURAL_HEIGHT:
{
ValueData *holds = NULL;
holds = g_hash_table_lookup (state->setters, value);
if (holds == NULL)
holds = g_hash_table_lookup (self->init_state->setters, value);
if (holds != NULL)
expression = ensure_expressions (
self, holds, state, &need_tick);
else
{
GValue empty_value = G_VALUE_INIT;
g_value_init (&empty_value, value->type);
expression = gtk_constant_expression_new_for_value (&empty_value);
g_value_unset (&empty_value);
}
}
break;
case VALUE_MEASURE_FOR_SIZE:
{
GtkExpression *notifier_constant = NULL;
GtkExpression *notify_expression = NULL;
notifier_constant = gtk_cclosure_expression_new (
BGE_TYPE_WDGT_NOTIFIER,
bge_marshal_OBJECT__VOID,
0, (GtkExpression *[]){},
G_CALLBACK (expression_get_measure_for_size_notifier),
NULL, NULL);
notify_expression = gtk_property_expression_new_for_pspec (
notifier_constant, notifier_props[NOTIFIER_PROP_VALUE]);
expression = gtk_cclosure_expression_new (
value->type,
bge_marshal_INT__DOUBLE,
1, (GtkExpression *[]){ notify_expression },
G_CALLBACK (expression_get_measure_for_size),
GSIZE_TO_POINTER (value->type),
NULL);
}
break;
case VALUE_WIDGET_WIDTH:
{
GtkExpression *notifier_constant = NULL;
GtkExpression *notify_expression = NULL;
notifier_constant = gtk_cclosure_expression_new (
BGE_TYPE_WDGT_NOTIFIER,
bge_marshal_OBJECT__VOID,
0, (GtkExpression *[]){},
G_CALLBACK (expression_get_widget_width_notifier),
NULL, NULL);
notify_expression = gtk_property_expression_new_for_pspec (
notifier_constant, notifier_props[NOTIFIER_PROP_VALUE]);
expression = gtk_cclosure_expression_new (
value->type,
bge_marshal_DOUBLE__DOUBLE,
1, (GtkExpression *[]){ notify_expression },
G_CALLBACK (expression_get_widget_width),
GSIZE_TO_POINTER (value->type),
NULL);
}
break;
case VALUE_WIDGET_HEIGHT:
{
GtkExpression *notifier_constant = NULL;
GtkExpression *notify_expression = NULL;
notifier_constant = gtk_cclosure_expression_new (
BGE_TYPE_WDGT_NOTIFIER,
bge_marshal_OBJECT__VOID,
0, (GtkExpression *[]){},
G_CALLBACK (expression_get_widget_height_notifier),
NULL, NULL);
notify_expression = gtk_property_expression_new_for_pspec (
notifier_constant, notifier_props[NOTIFIER_PROP_VALUE]);
expression = gtk_cclosure_expression_new (
value->type,
bge_marshal_DOUBLE__DOUBLE,
1, (GtkExpression *[]){ notify_expression },
G_CALLBACK (expression_get_widget_height),
GSIZE_TO_POINTER (value->type),
NULL);
}
break;
case VALUE_TICK_TIME:
{
GtkExpression *notifier_constant = NULL;
GtkExpression *notify_expression = NULL;
notifier_constant = gtk_cclosure_expression_new (
BGE_TYPE_WDGT_NOTIFIER,
bge_marshal_OBJECT__VOID,
0, (GtkExpression *[]){},
G_CALLBACK (expression_get_tick_time_notifier),
NULL, NULL);
notify_expression = gtk_property_expression_new_for_pspec (
notifier_constant, notifier_props[NOTIFIER_PROP_VALUE]);
expression = gtk_cclosure_expression_new (
value->type,
bge_marshal_DOUBLE__DOUBLE,
1, (GtkExpression *[]){ notify_expression },
G_CALLBACK (expression_get_tick_time),
GSIZE_TO_POINTER (value->type),
NULL);
need_tick = TRUE;
}
break;
case VALUE_COERCION:
{
expression = ensure_expressions (
self, value->coercion.value, state, &need_tick);
expression = gtk_cclosure_expression_new (
value->type,
_marshal_DIRECT__ARGS_DIRECT,
1, (GtkExpression *[]){ expression },
G_CALLBACK (expression_coerce_type),
GSIZE_TO_POINTER (value->type),
NULL);
}
break;
case VALUE_TRACK_TRANSITION:
{
g_autoptr (BgeWdgtNotifier) notifier_object = NULL;
GtkExpression *notifier_constant = NULL;
GtkExpression *notify_expression = NULL;
GtkExpression *damping_ratio_expression = NULL;
GtkExpression *mass_expression = NULL;
GtkExpression *stiffness_expression = NULL;
notifier_constant = gtk_cclosure_expression_new (
BGE_TYPE_WDGT_NOTIFIER,
bge_marshal_OBJECT__VOID,
0, (GtkExpression *[]){},
G_CALLBACK (expression_get_track_transition_closure_instance_notifier),
value_data_ref (value), value_data_unref_closure);
notify_expression = gtk_property_expression_new_for_pspec (
notifier_constant, notifier_props[NOTIFIER_PROP_VALUE]);
damping_ratio_expression = ensure_expressions (
self,
value->track_transition.spring.damping_ratio,
state,
&need_tick);
mass_expression = ensure_expressions (
self,
value->track_transition.spring.mass,
state,
&need_tick);
stiffness_expression = ensure_expressions (
self,
value->track_transition.spring.stiffness,
state,
&need_tick);
expression = ensure_expressions (
self,
value->track_transition.src,
state,
&need_tick);
expression = gtk_cclosure_expression_new (
value->type,
bge_marshal_DOUBLE__DOUBLE_DOUBLE_DOUBLE_DOUBLE_DOUBLE,
5, (GtkExpression *[]){ expression, damping_ratio_expression, mass_expression, stiffness_expression, notify_expression },
G_CALLBACK (expression_adjust_track_transition),
value_data_ref (value),
value_data_unref_closure);
}
break;
case VALUE_COMPONENT:
{
g_autoptr (GPtrArray) params = NULL;
GClosureMarshal marshal = NULL;
GCallback callback = NULL;
params = g_ptr_array_new ();
for (guint i = 0; i < value->component.params->len; i++)
{
ValueData *member = NULL;
g_autoptr (GtkExpression) member_expr = NULL;
member = g_ptr_array_index (value->component.params, i);
member_expr = ensure_expressions (self, member, state, &need_tick);
g_ptr_array_add (params, g_steal_pointer (&member_expr));
}
if (value->type == GRAPHENE_TYPE_POINT)
{
marshal = bge_marshal_BOXED__DOUBLE_DOUBLE;
callback = G_CALLBACK (expression_create_point);
}
else if (value->type == GRAPHENE_TYPE_SIZE)
{
marshal = bge_marshal_BOXED__DOUBLE_DOUBLE;
callback = G_CALLBACK (expression_create_size);
}
else if (value->type == GRAPHENE_TYPE_POINT3D)
{
marshal = bge_marshal_BOXED__DOUBLE_DOUBLE_DOUBLE;
callback = G_CALLBACK (expression_create_point3d);
}
else if (value->type == GRAPHENE_TYPE_RECT)
{
marshal = bge_marshal_BOXED__DOUBLE_DOUBLE_DOUBLE_DOUBLE;
callback = G_CALLBACK (expression_create_rect);
}
else if (value->type == GSK_TYPE_STROKE)
{
marshal = bge_marshal_BOXED__DOUBLE_ENUM_ENUM_DOUBLE;
callback = G_CALLBACK (expression_create_stroke);
}
else
g_assert_not_reached ();
expression = gtk_cclosure_expression_new (
value->type,
marshal,
params->len,
(GtkExpression **) params->pdata,
callback,
self,
NULL);
}
break;
case VALUE_TRANSFORM:
{
g_autoptr (GPtrArray) params = NULL;
g_autoptr (GtkExpression) next_expr = NULL;
params = g_ptr_array_new ();
next_expr = ensure_expressions (self, value->transform.next, state, &need_tick);
g_ptr_array_add (params, g_steal_pointer (&next_expr));
for (guint i = 0; i < value->transform.args->len; i++)
{
ValueData *arg = NULL;
g_autoptr (GtkExpression) arg_expr = NULL;
arg = g_ptr_array_index (value->transform.args, i);
arg_expr = ensure_expressions (self, arg, state, &need_tick);
g_ptr_array_add (params, g_steal_pointer (&arg_expr));
}
expression = gtk_cclosure_expression_new (
value->type,
_marshal_BOXED__ARGS_DIRECT,
params->len,
(GtkExpression **) params->pdata,
G_CALLBACK (expression_perform_transform),
value->transform.func,
NULL);
}
break;
case VALUE_PATH:
{
g_autoptr (GPtrArray) params = NULL;
g_autoptr (GtkExpression) next_expr = NULL;
params = g_ptr_array_new ();
g_assert (value->path.funcs->len == value->path.argss->len);
for (guint i = 0; i < value->path.argss->len; i++)
{
GPtrArray *args = NULL;
args = g_ptr_array_index (value->path.argss, i);
for (guint j = 0; j < args->len; j++)
{
ValueData *arg = NULL;
g_autoptr (GtkExpression) arg_expr = NULL;
arg = g_ptr_array_index (args, j);
arg_expr = ensure_expressions (self, arg, state, &need_tick);
g_ptr_array_add (params, g_steal_pointer (&arg_expr));
}
}
expression = gtk_cclosure_expression_new (
value->type,
_marshal_BOXED__ARGS_DIRECT,
params->len,
(GtkExpression **) params->pdata,
G_CALLBACK (expression_perform_path_build),
value_data_ref (value),
value_data_unref_closure);
}
break;
case VALUE_CLOSURE:
{
g_autoptr (GPtrArray) params = NULL;
params = g_ptr_array_new ();
for (guint i = 0; i < value->closure.args->len; i++)
{
ValueData *arg = NULL;
g_autoptr (GtkExpression) arg_expr = NULL;
arg = g_ptr_array_index (value->closure.args, i);
arg_expr = ensure_expressions (self, arg, state, &need_tick);
g_ptr_array_add (params, g_steal_pointer (&arg_expr));
}
expression = gtk_cclosure_expression_new (
value->type,
value->closure.marshal,
params->len,
(GtkExpression **) params->pdata,
value->closure.func,
value->closure.user_data,
NULL);
}
break;
case VALUE_PROPERTY:
{
g_autoptr (GtkExpression) object_expression = NULL;
object_expression = ensure_expressions (
self, value->property.object, state, &need_tick);
expression = gtk_property_expression_new (
value->property.object->type,
g_steal_pointer (&object_expression),
value->property.prop_name);
}
break;
default:
g_assert_not_reached ();
break;
}
transition = g_hash_table_lookup (state->transitions, value);
if (value->type == G_TYPE_DOUBLE &&
transition != NULL)
{
GtkExpression *notifier_constant = NULL;
GtkExpression *notify_expression = NULL;
notifier_constant = gtk_cclosure_expression_new (
BGE_TYPE_WDGT_NOTIFIER,
bge_marshal_OBJECT__VOID,
0, (GtkExpression *[]){},
G_CALLBACK (expression_get_transition_instance_notifier),
transition_data_ref (transition),
transition_data_unref_closure);
notify_expression = gtk_property_expression_new_for_pspec (
notifier_constant, notifier_props[NOTIFIER_PROP_VALUE]);
expression = gtk_cclosure_expression_new (
value->type,
bge_marshal_DOUBLE__DOUBLE_DOUBLE,
2, (GtkExpression *[]){ expression, notify_expression },
G_CALLBACK (expression_adjust_state_transition),
transition_data_ref (transition),
transition_data_unref_closure);
}
g_hash_table_replace (
state->expressions,
value_data_ref (value),
gtk_expression_ref (expression));
if (need_tick)
{
callback when transitions are not active */
g_hash_table_replace (
state->values_need_tick,
value_data_ref (value),
NULL);
if (value_needs_tick != NULL)
*value_needs_tick = TRUE;
}
return gtk_expression_ref (expression);
}
static void
set_value (BgeWdgtRenderer *self,
StateData *state,
StateInstanceData *instance,
ValueData *dest,
ValueData *src,
GPtrArray *watches)
{
GtkExpression *src_expression = NULL;
GtkExpression *dest_expression = NULL;
src_expression = g_hash_table_lookup (state->expressions, src);
g_assert (src_expression != NULL);
dest_expression = g_hash_table_lookup (state->expressions, dest);
g_assert (dest_expression != NULL);
switch (dest->kind)
{
case VALUE_PROPERTY:
{
GtkExpression *dest_obj_expression = NULL;
GValue dest_obj_resolved = G_VALUE_INIT;
GObject *dest_obj = NULL;
dest_obj_expression = g_hash_table_lookup (state->expressions, dest->property.object);
g_assert (dest_obj_expression != NULL);
gtk_expression_evaluate (
dest_obj_expression,
self,
&dest_obj_resolved);
dest_obj = g_value_get_object (&dest_obj_resolved);
if (dest_obj != NULL)
{
gboolean resolved = FALSE;
GValue src_resolved = G_VALUE_INIT;
resolved = gtk_expression_evaluate (
src_expression,
self,
&src_resolved);
if (resolved)
g_object_set_property (
dest_obj,
dest->property.prop_name,
&src_resolved);
g_value_unset (&src_resolved);
}
g_value_unset (&dest_obj_resolved);
}
break;
case VALUE_ALLOCATION_WIDTH:
case VALUE_ALLOCATION_HEIGHT:
case VALUE_ALLOCATION_TRANSFORM:
{
GtkExpression *dest_widget_expression = NULL;
GValue dest_widget_resolved = G_VALUE_INIT;
GtkWidget *dest_widget = NULL;
dest_widget_expression = g_hash_table_lookup (state->expressions, dest->allocation.widget);
g_assert (dest_widget_expression != NULL);
gtk_expression_evaluate (
dest_widget_expression,
self,
&dest_widget_resolved);
dest_widget = g_value_get_object (&dest_widget_resolved);
if (dest_widget != NULL)
{
AllocationData *allocation = NULL;
allocation = g_hash_table_lookup (self->allocations, dest_widget);
if (allocation != NULL)
{
switch (dest->kind)
{
case VALUE_ALLOCATION_WIDTH:
g_clear_pointer (&allocation->width, value_data_unref);
allocation->width = value_data_ref (src);
break;
case VALUE_ALLOCATION_HEIGHT:
g_clear_pointer (&allocation->height, value_data_unref);
allocation->height = value_data_ref (src);
break;
case VALUE_ALLOCATION_TRANSFORM:
g_clear_pointer (&allocation->transform, value_data_unref);
allocation->transform = value_data_ref (src);
break;
case VALUE_CHILD:
case VALUE_CLOSURE:
case VALUE_COERCION:
case VALUE_COMPONENT:
case VALUE_CONSTANT:
case VALUE_ITERATOR:
case VALUE_MEASURE_FOR_SIZE:
case VALUE_MEASURE_MINIMUM_HEIGHT:
case VALUE_MEASURE_MINIMUM_WIDTH:
case VALUE_MEASURE_NATURAL_HEIGHT:
case VALUE_MEASURE_NATURAL_WIDTH:
case VALUE_OBJECT:
case VALUE_PATH:
case VALUE_PROPERTY:
case VALUE_REFERENCE_OBJECT:
case VALUE_TICK_TIME:
case VALUE_TRACK_TRANSITION:
case VALUE_TRANSFORM:
case VALUE_VARIABLE:
case VALUE_WIDGET_HEIGHT:
case VALUE_WIDGET_WIDTH:
default:
g_assert_not_reached ();
}
gtk_widget_queue_allocate (GTK_WIDGET (self));
}
}
g_value_unset (&dest_widget_resolved);
}
break;
case VALUE_MEASURE_MINIMUM_WIDTH:
case VALUE_MEASURE_NATURAL_WIDTH:
case VALUE_MEASURE_MINIMUM_HEIGHT:
case VALUE_MEASURE_NATURAL_HEIGHT:
{
GValue src_resolved = G_VALUE_INIT;
gtk_expression_evaluate (
src_expression,
self,
&src_resolved);
switch (dest->kind)
{
case VALUE_MEASURE_MINIMUM_WIDTH:
self->measure_minimum_width = g_value_get_int (&src_resolved);
break;
case VALUE_MEASURE_NATURAL_WIDTH:
self->measure_natural_width = g_value_get_int (&src_resolved);
break;
case VALUE_MEASURE_MINIMUM_HEIGHT:
self->measure_minimum_height = g_value_get_int (&src_resolved);
break;
case VALUE_MEASURE_NATURAL_HEIGHT:
self->measure_natural_height = g_value_get_int (&src_resolved);
break;
case VALUE_ALLOCATION_HEIGHT:
case VALUE_ALLOCATION_TRANSFORM:
case VALUE_ALLOCATION_WIDTH:
case VALUE_CHILD:
case VALUE_CLOSURE:
case VALUE_COERCION:
case VALUE_COMPONENT:
case VALUE_CONSTANT:
case VALUE_ITERATOR:
case VALUE_MEASURE_FOR_SIZE:
case VALUE_OBJECT:
case VALUE_PATH:
case VALUE_PROPERTY:
case VALUE_REFERENCE_OBJECT:
case VALUE_TICK_TIME:
case VALUE_TRACK_TRANSITION:
case VALUE_TRANSFORM:
case VALUE_VARIABLE:
case VALUE_WIDGET_HEIGHT:
case VALUE_WIDGET_WIDTH:
default:
g_assert_not_reached ();
}
g_value_unset (&src_resolved);
gtk_widget_queue_resize (GTK_WIDGET (self));
}
break;
case VALUE_CHILD:
case VALUE_CLOSURE:
case VALUE_COERCION:
case VALUE_COMPONENT:
case VALUE_CONSTANT:
case VALUE_ITERATOR:
case VALUE_MEASURE_FOR_SIZE:
case VALUE_OBJECT:
case VALUE_PATH:
case VALUE_REFERENCE_OBJECT:
case VALUE_TICK_TIME:
case VALUE_TRACK_TRANSITION:
case VALUE_TRANSFORM:
case VALUE_VARIABLE:
case VALUE_WIDGET_HEIGHT:
case VALUE_WIDGET_WIDTH:
break;
default:
g_assert_not_reached ();
}
if (watches != NULL)
{
g_autoptr (WatchSetterData) watch_data = NULL;
GtkExpressionWatch *watch = NULL;
watch_data = watch_setter_data_new ();
watch_data->self = self;
watch_data->state = state_data_ref (state);
watch_data->instance = state_instance_data_ref (instance);
watch_data->dest = value_data_ref (dest);
watch_data->src = value_data_ref (src);
watch = gtk_expression_watch (
src_expression,
self,
(GtkExpressionNotify) reset_setter,
watch_setter_data_ref (watch_data),
watch_setter_data_unref);
g_ptr_array_add (watches, watch);
}
}
static int
resolve_value_int (BgeWdgtRenderer *self,
ValueData *value,
StateData *state)
{
GtkExpression *expression = NULL;
GValue resolved = G_VALUE_INIT;
int ret = 0.0;
expression = g_hash_table_lookup (state->expressions, value);
g_assert (expression != NULL);
gtk_expression_evaluate (
expression,
self,
&resolved);
ret = g_value_get_int (&resolved);
g_value_unset (&resolved);
return ret;
}
static double
resolve_value_double (BgeWdgtRenderer *self,
ValueData *value,
StateData *state)
{
GtkExpression *expression = NULL;
GValue resolved = G_VALUE_INIT;
double ret = 0.0;
expression = g_hash_table_lookup (state->expressions, value);
g_assert (expression != NULL);
gtk_expression_evaluate (
expression,
self,
&resolved);
ret = g_value_get_double (&resolved);
g_value_unset (&resolved);
return ret;
}
static gint
resolve_value_enum (BgeWdgtRenderer *self,
ValueData *value,
StateData *state)
{
GtkExpression *expression = NULL;
GValue resolved = G_VALUE_INIT;
gint ret = 0;
expression = g_hash_table_lookup (state->expressions, value);
g_assert (expression != NULL);
gtk_expression_evaluate (
expression,
self,
&resolved);
ret = g_value_get_enum (&resolved);
g_value_unset (&resolved);
return ret;
}
static gpointer
resolve_value_boxed_dup (BgeWdgtRenderer *self,
ValueData *value,
StateData *state)
{
GtkExpression *expression = NULL;
GValue resolved = G_VALUE_INIT;
gpointer ret = NULL;
expression = g_hash_table_lookup (state->expressions, value);
g_assert (expression != NULL);
gtk_expression_evaluate (
expression,
self,
&resolved);
ret = g_value_dup_boxed (&resolved);
g_value_unset (&resolved);
return ret;
}
static gpointer
resolve_value_object_dup (BgeWdgtRenderer *self,
ValueData *value,
StateData *state)
{
GtkExpression *expression = NULL;
GValue resolved = G_VALUE_INIT;
gpointer ret = NULL;
expression = g_hash_table_lookup (state->expressions, value);
g_assert (expression != NULL);
gtk_expression_evaluate (
expression,
self,
&resolved);
ret = g_value_dup_object (&resolved);
g_value_unset (&resolved);
return ret;
}
static void
discard_binding (gpointer ptr)
{
GBinding *binding = ptr;
g_binding_unbind (binding);
g_object_unref (binding);
}
static void
discard_watch (gpointer ptr)
{
GtkExpressionWatch *watch = ptr;
gtk_expression_watch_unwatch (watch);
}
static void
prop_change_queue_draw (BgeWdgtRenderer *self)
{
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
reset_setter (WatchSetterData *data)
{
BgeWdgtRenderer *self = data->self;
if this is the init state and the active state also sets this value, we
should avoid touching it */
if (self->active_state != NULL &&
data->state == self->spec->init_state &&
g_hash_table_contains (
self->active_state->setters,
data->dest))
return;
set_value (
data->self,
data->state,
data->instance,
data->dest,
data->src,
NULL);
}
static GHashTable *renderer_resource_cache = NULL;
static void
wdgt_renderer_set_from_resource (BgeWdgtRenderer *self,
const char *resource)
{
g_autoptr (GError) local_error = NULL;
g_autoptr (GBytes) bytes = NULL;
BgeWdgtSpec *cached = NULL;
gsize buffer_size = 0;
gconstpointer buffer = NULL;
g_autoptr (BgeWdgtSpec) spec = NULL;
if (g_once_init_enter_pointer (&renderer_resource_cache))
{
GHashTable *tmp = NULL;
tmp = g_hash_table_new_full (
g_str_hash,
g_str_equal,
g_free,
g_object_unref);
g_once_init_leave_pointer (&renderer_resource_cache, tmp);
}
cached = g_hash_table_lookup (
renderer_resource_cache,
resource);
if (cached == NULL)
{
bytes = g_resources_lookup_data (
resource,
G_RESOURCE_LOOKUP_FLAGS_NONE,
&local_error);
if (bytes == NULL)
{
g_critical ("failed to set renderer spec from resource: %s",
local_error->message);
bge_wdgt_renderer_set_spec (self, NULL);
return;
}
buffer = g_bytes_get_data (bytes, &buffer_size);
spec = bge_wdgt_spec_new_for_string (buffer, &local_error);
if (spec == NULL)
{
g_critical ("failed to set renderer spec from resource %s: %s",
resource, local_error->message);
bge_wdgt_renderer_set_spec (self, NULL);
return;
}
g_hash_table_replace (
renderer_resource_cache,
g_strdup (resource),
g_object_ref (spec));
cached = spec;
}
bge_wdgt_renderer_set_spec (self, cached);
}