summaryrefslogtreecommitdiff
path: root/src/shell-embedded-window.c
blob: 6fe54c6725cf48d55efd0047a7b767b65ad9f523 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */

#include "config.h"

#include <gdk/gdkx.h>
#include <clutter/x11/clutter-x11.h>

#include "shell-embedded-window-private.h"

/* This type is a subclass of GtkWindow that ties the window to a
 * ShellGtkEmbed; the resizing logic is bound to the clutter logic.
 *
 * The typical usage we might expect is
 *
 *  - ShellEmbeddedWindow is created and filled with content
 *  - ShellEmbeddedWindow is shown with gtk_widget_show_all()
 *  - ShellGtkEmbed is created for the ShellEmbeddedWindow
 *  - actor is added to a stage
 *
 * The way it works is that the GtkWindow is mapped if and only if both:
 *
 * - gtk_widget_visible (window) [widget has been shown]
 * - Actor is mapped [actor and all parents visible, actor in stage]
 */

G_DEFINE_TYPE (ShellEmbeddedWindow, shell_embedded_window, GTK_TYPE_WINDOW);

enum {
   PROP_0
};

struct _ShellEmbeddedWindowPrivate {
  ShellGtkEmbed *actor;

  GdkRectangle position;
};

/*
 * The normal gtk_window_show() starts all of the complicated asynchronous
 * window resizing code running; we don't want or need any of that.
 * Bypassing the normal code does mean that the extra geometry management
 * available on GtkWindow: gridding, maximum sizes, etc, is ignored; we
 * don't really want that anyways - we just want a way of embedding a
 * GtkWidget into a Clutter stage.
 */
static void
shell_embedded_window_show (GtkWidget *widget)
{
  ShellEmbeddedWindow *window = SHELL_EMBEDDED_WINDOW (widget);
  GtkWidgetClass *widget_class;

  /* Skip GtkWindow, but run the default GtkWidget handling which
   * marks the widget visible */
  widget_class = g_type_class_peek (GTK_TYPE_WIDGET);
  widget_class->show (widget);

  if (window->priv->actor)
    {
      /* Size is 0x0 if the GtkWindow is not shown */
      clutter_actor_queue_relayout (CLUTTER_ACTOR (window->priv->actor));

      if (CLUTTER_ACTOR_IS_REALIZED (window->priv->actor))
        gtk_widget_map (widget);
    }
}

static void
shell_embedded_window_hide (GtkWidget *widget)
{
  ShellEmbeddedWindow *window = SHELL_EMBEDDED_WINDOW (widget);

  if (window->priv->actor)
    clutter_actor_queue_relayout (CLUTTER_ACTOR (window->priv->actor));

  GTK_WIDGET_CLASS (shell_embedded_window_parent_class)->hide (widget);
}

static gboolean
shell_embedded_window_configure_event (GtkWidget         *widget,
                                       GdkEventConfigure *event)
{
  /* Normally a configure event coming back from X triggers the
   * resizing logic inside GtkWindow; we just ignore them
   * since we are handling the resizing logic separately.
   */
  return FALSE;
}

static void
shell_embedded_window_check_resize (GtkContainer *container)
{
  ShellEmbeddedWindow *window = SHELL_EMBEDDED_WINDOW (container);

  /* Check resize is called when a resize is queued on something
   * inside the GtkWindow; we need to make sure that in response
   * to this gtk_widget_size_request() and then
   * gtk_widget_size_allocate() are called; we defer to the Clutter
   * logic and assume it will do the right thing.
   */
  if (window->priv->actor)
    clutter_actor_queue_relayout (CLUTTER_ACTOR (window->priv->actor));
}

static GObject *
shell_embedded_window_constructor (GType                  gtype,
                                   guint                  n_properties,
                                   GObjectConstructParam *properties)
{
  GObject *object;
  GObjectClass *parent_class;

  parent_class = G_OBJECT_CLASS (shell_embedded_window_parent_class);
  object = parent_class->constructor (gtype, n_properties, properties);

  /* Setting the resize mode to immediate means that calling queue_resize()
   * on a widget within the window will immmediately call check_resize()
   * to be called, instead of having it queued to an idle. From our perspective,
   * this is ideal since we just are going to queue a resize to Clutter's
   * idle resize anyways.
   */
  g_object_set (object,
                "app-paintable", TRUE,
                "resize-mode", GTK_RESIZE_IMMEDIATE,
                "type", GTK_WINDOW_POPUP,
                NULL);

  return object;
}

static void
shell_embedded_window_class_init (ShellEmbeddedWindowClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);

  g_type_class_add_private (klass, sizeof (ShellEmbeddedWindowPrivate));

  object_class->constructor     = shell_embedded_window_constructor;

  widget_class->show            = shell_embedded_window_show;
  widget_class->hide            = shell_embedded_window_hide;
  widget_class->configure_event = shell_embedded_window_configure_event;

  container_class->check_resize    = shell_embedded_window_check_resize;
}

static void
shell_embedded_window_init (ShellEmbeddedWindow *window)
{
  window->priv = G_TYPE_INSTANCE_GET_PRIVATE (window, SHELL_TYPE_EMBEDDED_WINDOW,
                                              ShellEmbeddedWindowPrivate);
}

/*
 * Private routines called by ShellGtkEmbed
 */

void
_shell_embedded_window_set_actor (ShellEmbeddedWindow  *window,
                                  ShellGtkEmbed        *actor)

{
  g_return_if_fail (SHELL_IS_EMBEDDED_WINDOW (window));

  window->priv->actor = actor;

  if (actor &&
      CLUTTER_ACTOR_IS_MAPPED (actor) &&
      gtk_widget_get_visible (GTK_WIDGET (window)))
    gtk_widget_map (GTK_WIDGET (window));
}

void
_shell_embedded_window_allocate (ShellEmbeddedWindow *window,
                                 int                  x,
                                 int                  y,
                                 int                  width,
                                 int                  height)
{
  GtkAllocation allocation;

  g_return_if_fail (SHELL_IS_EMBEDDED_WINDOW (window));

  if (window->priv->position.x == x &&
      window->priv->position.y == y &&
      window->priv->position.width == width &&
      window->priv->position.height == height)
    return;

  window->priv->position.x = x;
  window->priv->position.y = y;
  window->priv->position.width = width;
  window->priv->position.height = height;

  if (gtk_widget_get_realized (GTK_WIDGET (window)))
    gdk_window_move_resize (gtk_widget_get_window (GTK_WIDGET (window)),
                            x, y, width, height);

  allocation.x = 0;
  allocation.y = 0;
  allocation.width = width;
  allocation.height = height;

  gtk_widget_size_allocate (GTK_WIDGET (window), &allocation);
}

void
_shell_embedded_window_map (ShellEmbeddedWindow *window)
{
  g_return_if_fail (SHELL_IS_EMBEDDED_WINDOW (window));

  if (gtk_widget_get_visible (GTK_WIDGET (window)))
    gtk_widget_map (GTK_WIDGET (window));
}

void
_shell_embedded_window_unmap (ShellEmbeddedWindow *window)
{
  g_return_if_fail (SHELL_IS_EMBEDDED_WINDOW (window));

  gtk_widget_unmap (GTK_WIDGET (window));
}

/*
 * Public API
 */
GtkWidget *
shell_embedded_window_new (void)
{
  return g_object_new (SHELL_TYPE_EMBEDDED_WINDOW,
                       NULL);
}