Add ShellGenericContainer, which makes it possible to write containers in JS
Subclass ClutterGroup (to avoid having to implement all of dispose, raise, lower, add, etc.), and have it proxy the allocation requests out into signals. We have to group up the two out parameters into a struct unfortunately. Included example code in the C file source for now.
This commit is contained in:
parent
31851cbc32
commit
91353c6d60
@ -66,6 +66,8 @@ libgnome_shell_la_SOURCES = \
|
||||
shell-embedded-window-private.h \
|
||||
shell-gconf.c \
|
||||
shell-gconf.h \
|
||||
shell-generic-container.c \
|
||||
shell-generic-container.h \
|
||||
shell-gtk-embed.c \
|
||||
shell-gtk-embed.h \
|
||||
shell-overflow-list.c \
|
||||
|
240
src/shell-generic-container.c
Normal file
240
src/shell-generic-container.c
Normal file
@ -0,0 +1,240 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
/**
|
||||
* SECTION:shell-generic-container
|
||||
* @short_description: A container class with signals for allocation
|
||||
*
|
||||
* #ShellGenericContainer is mainly a workaround for the current
|
||||
* lack of GObject subclassing + vfunc overrides in gjs. We
|
||||
* implement the container interface, but proxy the virtual functions
|
||||
* into signals, which gjs can catch.
|
||||
*/
|
||||
|
||||
/* Example implementation of a horzontal box with PACK_EXPAND for all,
|
||||
vertically and horizontally centering.
|
||||
|
||||
function TestFixedBox() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
TestFixedBox.prototype = {
|
||||
_init : function () {
|
||||
this.actor = new Shell.GenericContainer();
|
||||
this.spacing = 4;
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, function (actor, for_height, alloc) {
|
||||
let children = this.actor.get_children();
|
||||
let max_child_min = 0;
|
||||
let max_child_nat = 0;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let spacing = i > 0 && i < children.length-1 ? this.spacing : 0;
|
||||
let [child_min, child_nat] = children[i].get_preferred_width(for_height);
|
||||
if (child_min > max_child_min)
|
||||
max_child_min = child_min;
|
||||
if (child_nat > max_child_nat)
|
||||
max_child_nat = child_nat;
|
||||
}
|
||||
let totalSpacing = this.spacing * Math.abs(children.length - 1);
|
||||
alloc.min_size = children.length * max_child_min + totalSpacing;
|
||||
alloc.nat_size = children.length * max_child_nat + totalSpacing;
|
||||
}));
|
||||
this.actor.connect('get-preferred-height', Lang.bind(this, function (actor, for_width, alloc) {
|
||||
let children = this.actor.get_children();
|
||||
let max_child_min = 0;
|
||||
let max_child_nat = 0;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let [child_min, child_nat] = children[i].get_preferred_height(for_width);
|
||||
if (child_min > max_child_min)
|
||||
max_child_min = child_min;
|
||||
if (child_nat > max_child_nat)
|
||||
max_child_nat = child_nat;
|
||||
}
|
||||
alloc.min_size = max_child_min;
|
||||
alloc.nat_size = max_child_nat;
|
||||
}));
|
||||
this.actor.connect('allocate', Lang.bind(this, function (actor, box, flags) {
|
||||
let children = this.actor.get_children();
|
||||
let totalSpacing = (this.spacing * Math.abs(children.length - 1));
|
||||
let child_width = (box.x2 - box.x1 - totalSpacing) / (children.length);
|
||||
let child_height = box.y2 - box.y1;
|
||||
|
||||
let x = box.x1;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let [child_min, child_nat] = children[i].get_preferred_height(child_width);
|
||||
let vSpacing = Math.abs(child_height - child_nat) / 2;
|
||||
let childBox = new Clutter.ActorBox();
|
||||
childBox.x1 = x;
|
||||
childBox.y1 = vSpacing;
|
||||
childBox.x2 = x+child_width;
|
||||
childBox.y2 = child_height - vSpacing;
|
||||
children[i].allocate(childBox, flags);
|
||||
x += this.spacing + child_width;
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
function runTestFixedBox() {
|
||||
let testBox = new TestFixedBox();
|
||||
let c = new Clutter.Color();
|
||||
c.from_pixel(0xff0000a0);
|
||||
let r = new Clutter.Rectangle({ width: 50, height: 100, color: c });
|
||||
testBox.actor.add_actor(r);
|
||||
r = new Clutter.Rectangle({ width: 90, height: 70, color: c });
|
||||
testBox.actor.add_actor(r);
|
||||
r = new Clutter.Rectangle({ width: 90, height: 70, color: c });
|
||||
testBox.actor.add_actor(r);
|
||||
r = new Clutter.Rectangle({ width: 30, height: 10, color: c });
|
||||
testBox.actor.add_actor(r);
|
||||
|
||||
c.from_pixel(0x00ff00a0);
|
||||
let borderBox = new Big.Box({ border: 1, border_color: c });
|
||||
borderBox.set_position(100, 100);
|
||||
borderBox.append(testBox.actor, Big.BoxPackFlags.NONE);
|
||||
Shell.Global.get().stage.add_actor(borderBox);
|
||||
}
|
||||
*/
|
||||
|
||||
#include "shell-generic-container.h"
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <girepository.h>
|
||||
|
||||
G_DEFINE_TYPE(ShellGenericContainer, shell_generic_container, CLUTTER_TYPE_GROUP);
|
||||
|
||||
struct _ShellGenericContainerPrivate {
|
||||
gpointer dummy;
|
||||
};
|
||||
|
||||
/* Signals */
|
||||
enum
|
||||
{
|
||||
GET_PREFERRED_WIDTH,
|
||||
GET_PREFERRED_HEIGHT,
|
||||
ALLOCATE,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint shell_generic_container_signals [LAST_SIGNAL] = { 0 };
|
||||
|
||||
|
||||
static gpointer
|
||||
shell_generic_container_allocation_ref (ShellGenericContainerAllocation *alloc)
|
||||
{
|
||||
alloc->_refcount++;
|
||||
return alloc;
|
||||
}
|
||||
|
||||
static void
|
||||
shell_generic_container_allocation_unref (ShellGenericContainerAllocation *alloc)
|
||||
{
|
||||
if (--alloc->_refcount == 0)
|
||||
{
|
||||
g_slice_free1 (sizeof (ShellGenericContainerAllocation), alloc);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
shell_generic_container_allocate (ClutterActor *self,
|
||||
const ClutterActorBox *box,
|
||||
ClutterAllocationFlags flags)
|
||||
{
|
||||
/* chain up to set actor->allocation */
|
||||
(CLUTTER_ACTOR_CLASS (g_type_class_peek (clutter_actor_get_type ())))->allocate (self, box, flags);
|
||||
|
||||
g_signal_emit (G_OBJECT (self), shell_generic_container_signals[ALLOCATE], 0,
|
||||
box, flags);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_generic_container_get_preferred_width (ClutterActor *actor,
|
||||
gfloat for_height,
|
||||
gfloat *min_width_p,
|
||||
gfloat *natural_width_p)
|
||||
{
|
||||
ShellGenericContainerAllocation *alloc = g_slice_alloc0 (sizeof (ShellGenericContainerAllocation));
|
||||
alloc->_refcount = 1;
|
||||
g_signal_emit (G_OBJECT (actor), shell_generic_container_signals[GET_PREFERRED_WIDTH], 0,
|
||||
for_height, alloc);
|
||||
if (min_width_p)
|
||||
*min_width_p = alloc->min_size;
|
||||
if (natural_width_p)
|
||||
*natural_width_p = alloc->natural_size;
|
||||
shell_generic_container_allocation_unref (alloc);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_generic_container_get_preferred_height (ClutterActor *actor,
|
||||
gfloat for_width,
|
||||
gfloat *min_height_p,
|
||||
gfloat *natural_height_p)
|
||||
{
|
||||
ShellGenericContainerAllocation *alloc = g_slice_alloc0 (sizeof (ShellGenericContainerAllocation));
|
||||
alloc->_refcount = 1;
|
||||
g_signal_emit (G_OBJECT (actor), shell_generic_container_signals[GET_PREFERRED_HEIGHT], 0,
|
||||
for_width, alloc);
|
||||
if (min_height_p)
|
||||
*min_height_p = alloc->min_size;
|
||||
if (natural_height_p)
|
||||
*natural_height_p = alloc->natural_size;
|
||||
shell_generic_container_allocation_unref (alloc);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_generic_container_class_init (ShellGenericContainerClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||
|
||||
actor_class->get_preferred_width = shell_generic_container_get_preferred_width;
|
||||
actor_class->get_preferred_height = shell_generic_container_get_preferred_height;
|
||||
actor_class->allocate = shell_generic_container_allocate;
|
||||
|
||||
shell_generic_container_signals[GET_PREFERRED_WIDTH] =
|
||||
g_signal_new ("get-preferred-width",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
gi_cclosure_marshal_generic,
|
||||
G_TYPE_NONE, 2, G_TYPE_FLOAT, SHELL_TYPE_GENERIC_CONTAINER_ALLOCATION);
|
||||
|
||||
shell_generic_container_signals[GET_PREFERRED_HEIGHT] =
|
||||
g_signal_new ("get-preferred-height",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
gi_cclosure_marshal_generic,
|
||||
G_TYPE_NONE, 2, G_TYPE_FLOAT, SHELL_TYPE_GENERIC_CONTAINER_ALLOCATION);
|
||||
|
||||
shell_generic_container_signals[ALLOCATE] =
|
||||
g_signal_new ("allocate",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
gi_cclosure_marshal_generic,
|
||||
G_TYPE_NONE, 2, CLUTTER_TYPE_ACTOR_BOX, CLUTTER_TYPE_ALLOCATION_FLAGS);
|
||||
|
||||
g_type_class_add_private (gobject_class, sizeof (ShellGenericContainerPrivate));
|
||||
}
|
||||
|
||||
static void
|
||||
shell_generic_container_init (ShellGenericContainer *area)
|
||||
{
|
||||
area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area, SHELL_TYPE_GENERIC_CONTAINER,
|
||||
ShellGenericContainerPrivate);
|
||||
}
|
||||
|
||||
GType shell_generic_container_allocation_get_type (void)
|
||||
{
|
||||
static GType gtype = G_TYPE_INVALID;
|
||||
if (gtype == G_TYPE_INVALID)
|
||||
{
|
||||
gtype = g_boxed_type_register_static ("ShellGenericContainerAllocation",
|
||||
(GBoxedCopyFunc)shell_generic_container_allocation_ref,
|
||||
(GBoxedFreeFunc)shell_generic_container_allocation_unref);
|
||||
}
|
||||
return gtype;
|
||||
}
|
44
src/shell-generic-container.h
Normal file
44
src/shell-generic-container.h
Normal file
@ -0,0 +1,44 @@
|
||||
#ifndef __SHELL_GENERIC_CONTAINER_H__
|
||||
#define __SHELL_GENERIC_CONTAINER_H__
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define SHELL_TYPE_GENERIC_CONTAINER (shell_generic_container_get_type ())
|
||||
#define SHELL_GENERIC_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_GENERIC_CONTAINER, ShellGenericContainer))
|
||||
#define SHELL_GENERIC_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_GENERIC_CONTAINER, ShellGenericContainerClass))
|
||||
#define SHELL_IS_GENERIC_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_GENERIC_CONTAINER))
|
||||
#define SHELL_IS_GENERIC_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_GENERIC_CONTAINER))
|
||||
#define SHELL_GENERIC_CONTAINER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_GENERIC_CONTAINER, ShellGenericContainerClass))
|
||||
|
||||
typedef struct {
|
||||
float min_size;
|
||||
float natural_size;
|
||||
|
||||
/* <private> */
|
||||
guint _refcount;
|
||||
} ShellGenericContainerAllocation;
|
||||
|
||||
#define SHELL_TYPE_GENERIC_CONTAINER_ALLOCATION (shell_generic_container_allocation_get_type ())
|
||||
GType shell_generic_container_allocation_get_type (void);
|
||||
|
||||
typedef struct _ShellGenericContainer ShellGenericContainer;
|
||||
typedef struct _ShellGenericContainerClass ShellGenericContainerClass;
|
||||
|
||||
typedef struct _ShellGenericContainerPrivate ShellGenericContainerPrivate;
|
||||
|
||||
struct _ShellGenericContainer
|
||||
{
|
||||
ClutterGroup parent;
|
||||
|
||||
ShellGenericContainerPrivate *priv;
|
||||
};
|
||||
|
||||
struct _ShellGenericContainerClass
|
||||
{
|
||||
ClutterGroupClass parent_class;
|
||||
};
|
||||
|
||||
GType shell_generic_container_get_type (void) G_GNUC_CONST;
|
||||
|
||||
#endif /* __SHELL_GENERIC_CONTAINER_H__ */
|
Loading…
Reference in New Issue
Block a user