Commit 87466892 authored by Xan Lopez's avatar Xan Lopez

Port floating statusbar to gedit's overlay widget

Works correctly in framed pages, and fixes a few bugs along the way
(not to mention it should be more efficient since it does not redraw
needlessly).

The gedit code has been modified to get rid of the animation stuff we
don't really need atm; we have coordinated with the gedit developers
and hopefully both versions will be in sync again really soon.
parent cfff1c9b
......@@ -36,7 +36,7 @@
G_BEGIN_DECLS
#define EPHY_GET_WEBKIT_WEB_VIEW_FROM_EMBED(embed) (WEBKIT_WEB_VIEW (ephy_embed_get_web_view (embed)))
#define EPHY_GET_EMBED_FROM_EPHY_WEB_VIEW(view) (EPHY_EMBED (gtk_widget_get_parent (gtk_widget_get_parent (gtk_widget_get_parent (GTK_WIDGET (view))))))
#define EPHY_GET_EMBED_FROM_EPHY_WEB_VIEW(view) (EPHY_EMBED (gtk_widget_get_parent (gtk_widget_get_parent (gtk_widget_get_parent (gtk_widget_get_parent ((GTK_WIDGET (view))))))))
#define EPHY_WEBKIT_BACK_FORWARD_LIMIT 100
......
......@@ -43,6 +43,7 @@
#include "ephy-stock-icons.h"
#include "ephy-string.h"
#include "ephy-web-view.h"
#include "gedit-overlay.h"
#include <errno.h>
#include <glib/gi18n.h>
......@@ -72,6 +73,7 @@ struct _EphyEmbedPrivate
gboolean inspector_attached;
guint is_setting_zoom : 1;
GSList *destroy_on_transition_list;
GtkWidget *statusbar_label;
};
G_DEFINE_TYPE (EphyEmbed, ephy_embed, GTK_TYPE_VBOX)
......@@ -840,25 +842,52 @@ download_requested_cb (WebKitWebView *web_view,
return TRUE;
}
/* FIXME: it probably makes sense to move this stuff completely into
* EphyEmbed now, since it's not an integral part of EphyWebView
* anymore. */
void
_ephy_embed_set_statusbar_label (EphyEmbed *embed, const char *label)
{
EphyEmbedPrivate *priv = embed->priv;
gtk_label_set_label (GTK_LABEL (priv->statusbar_label), label);
if (label == NULL || label[0] == '\0')
gtk_widget_hide (priv->statusbar_label);
else
gtk_widget_show (priv->statusbar_label);
}
static void
ephy_embed_constructed (GObject *object)
{
EphyEmbed *embed = (EphyEmbed*)object;
EphyEmbedPrivate *priv = embed->priv;
GtkWidget *scrolled_window;
GtkWidget *paned;
WebKitWebView *web_view;
WebKitWebInspector *inspector;
GtkWidget *overlay;
GtkWidget *frame;
/* Skeleton */
web_view = WEBKIT_WEB_VIEW (ephy_web_view_new ());
scrolled_window = GTK_WIDGET (embed->priv->scrolled_window);
overlay = gedit_overlay_new (scrolled_window);
/* statusbar is hidden by default */
priv->statusbar_label = gtk_label_new (NULL);
frame = gtk_frame_new (NULL);
gtk_widget_show (frame);
gtk_container_add (GTK_CONTAINER (frame), priv->statusbar_label);
gedit_overlay_add (GEDIT_OVERLAY (overlay), frame, GTK_ORIENTATION_HORIZONTAL, GDK_GRAVITY_SOUTH_WEST, 0, TRUE);
paned = GTK_WIDGET (embed->priv->paned);
embed->priv->web_view = web_view;
gtk_container_add (GTK_CONTAINER (embed->priv->scrolled_window),
GTK_WIDGET (web_view));
gtk_paned_pack1 (GTK_PANED (paned), GTK_WIDGET (scrolled_window),
gtk_paned_pack1 (GTK_PANED (paned), GTK_WIDGET (overlay),
TRUE, FALSE);
gtk_box_pack_start (GTK_BOX (embed),
......
......@@ -56,6 +56,7 @@ EphyWebView* ephy_embed_get_web_view (EphyEmbed *embed);
void ephy_embed_add_top_widget (EphyEmbed *embed, GtkWidget *widget, gboolean destroy_on_transition);
void ephy_embed_remove_top_widget (EphyEmbed *embed, GtkWidget *widget);
void ephy_embed_auto_download_url (EphyEmbed *embed, const char *url);
void _ephy_embed_set_statusbar_label (EphyEmbed *embed, const char *label);
G_END_DECLS
......
......@@ -1155,76 +1155,6 @@ ephy_web_view_constructed (GObject *object)
webkit_web_view_set_full_content_zoom (WEBKIT_WEB_VIEW (object), TRUE);
}
static void
_ephy_web_view_draw_statusbar (GtkWidget *widget, cairo_t *cr)
{
gint width, height;
guint border_width, statusbar_border_width;
PangoLayout *layout;
GtkAllocation allocation;
GtkStyleContext *context;
EphyWebViewPrivate *priv;
priv = EPHY_WEB_VIEW (widget)->priv;
gtk_widget_get_allocation (widget, &allocation);
layout = gtk_widget_create_pango_layout (widget, priv->text);
pango_layout_set_width (layout, PANGO_SCALE * (allocation.width * 0.9));
pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
pango_layout_get_pixel_size (layout, &width, &height);
border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
statusbar_border_width = 4; /* FIXME: what should we use here? */
priv->text_rectangle.x = border_width;
priv->text_rectangle.y = allocation.height - height - border_width - (statusbar_border_width * 2);
priv->text_rectangle.width = width + (statusbar_border_width * 2);
priv->text_rectangle.height = height + (statusbar_border_width * 2);
context = gtk_widget_get_style_context (widget);
gtk_style_context_save (context);
gtk_style_context_set_state (context, GTK_STATE_FLAG_NORMAL);
gtk_render_background (context, cr,
priv->text_rectangle.x,
priv->text_rectangle.y,
priv->text_rectangle.width,
priv->text_rectangle.height);
gtk_render_frame (context, cr,
priv->text_rectangle.x,
priv->text_rectangle.y,
priv->text_rectangle.width,
priv->text_rectangle.height);
gtk_style_context_set_state (context, 0);
gtk_render_layout (context, cr,
priv->text_rectangle.x + statusbar_border_width,
priv->text_rectangle.y + statusbar_border_width,
layout);
gtk_style_context_restore (context);
g_object_unref (layout);
}
static gboolean
ephy_web_view_draw (GtkWidget *widget, cairo_t *cr)
{
EphyWebViewPrivate *priv;
GTK_WIDGET_CLASS (ephy_web_view_parent_class)->draw (widget, cr);
priv = EPHY_WEB_VIEW (widget)->priv;
if (priv->text && priv->text[0] != '\0')
_ephy_web_view_draw_statusbar (widget, cr);
return FALSE;
}
static void
ephy_web_view_class_init (EphyWebViewClass *klass)
{
......@@ -1239,7 +1169,6 @@ ephy_web_view_class_init (EphyWebViewClass *klass)
widget_class->button_press_event = ephy_web_view_button_press_event;
widget_class->key_press_event = ephy_web_view_key_press_event;
widget_class->draw = ephy_web_view_draw;
/**
* EphyWebView:address:
......@@ -3752,32 +3681,12 @@ ephy_web_view_load_homepage (EphyWebView *view)
static void
ephy_web_view_statusbar_update (EphyWebView *view, const char *text)
{
EphyWebViewPrivate *priv;
GdkWindow *window;
GdkRectangle rect;
priv = view->priv;
if (priv->text)
g_free (priv->text);
priv->text = g_strdup (text);
/* FIXME: we should invalidate the union of the sizes of the
* rectangles of the previous and next statusbar text */
window = gtk_widget_get_window (GTK_WIDGET (view));
if (window) {
GtkAllocation allocation;
EphyEmbed *embed;
gtk_widget_get_allocation (GTK_WIDGET (view), &allocation);
g_return_if_fail (EPHY_IS_WEB_VIEW (view));
rect = priv->text_rectangle;
rect.width = allocation.width;
if (rect.height == 0)
rect.height = allocation.height;
gdk_window_invalidate_rect (window, &rect, TRUE);
}
embed = EPHY_GET_EMBED_FROM_EPHY_WEB_VIEW (view);
_ephy_embed_set_statusbar_label (embed, text);
}
/* Portions of the following code based on GTK+.
......
......@@ -14,7 +14,11 @@ libephywidgets_la_SOURCES = \
ephy-zoom-action.h \
ephy-zoom-action.c \
ephy-zoom-control.c \
ephy-zoom-control.h
ephy-zoom-control.h \
gedit-overlay.c \
gedit-overlay.h \
gedit-theatrics-animated-widget.c \
gedit-theatrics-animated-widget.h
libephywidgets_la_CPPFLAGS = \
-I$(top_builddir)/lib \
......
/*
* gedit-overlay.c
* This file is part of gedit
*
* Copyright (C) 2010 - Ignacio Casal Quinteiro
*
* Based on Mike Krüger <mkrueger@novell.com> work.
*
* gedit 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 2 of the License, or
* (at your option) any later version.
*
* gedit 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 gedit; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include "gedit-overlay.h"
#include "gedit-theatrics-animated-widget.h"
#define GEDIT_OVERLAY_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GEDIT_TYPE_OVERLAY, GeditOverlayPrivate))
typedef struct _OverlayChild
{
GtkWidget *child;
GdkGravity gravity;
guint offset;
guint fixed_position : 1;
guint is_animated : 1;
} OverlayChild;
struct _GeditOverlayPrivate
{
GtkWidget *main_widget;
GSList *children;
GtkAllocation main_alloc;
GtkAdjustment *hadjustment;
GtkAdjustment *vadjustment;
glong hadjustment_signal_id;
glong vadjustment_signal_id;
/* GtkScrollablePolicy needs to be checked when
* driving the scrollable adjustment values */
guint hscroll_policy : 1;
guint vscroll_policy : 1;
};
enum
{
PROP_0,
PROP_MAIN_WIDGET,
PROP_HADJUSTMENT,
PROP_VADJUSTMENT,
PROP_HSCROLL_POLICY,
PROP_VSCROLL_POLICY
};
static void gedit_overlay_set_hadjustment (GeditOverlay *overlay,
GtkAdjustment *adjustment);
static void gedit_overlay_set_vadjustment (GeditOverlay *overlay,
GtkAdjustment *adjustment);
G_DEFINE_TYPE_WITH_CODE (GeditOverlay, gedit_overlay, GTK_TYPE_CONTAINER,
G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
static void
free_container_child (OverlayChild *child)
{
g_slice_free (OverlayChild, child);
}
static void
add_toplevel_widget (GeditOverlay *overlay,
GtkWidget *widget,
gboolean fixed_position,
gboolean is_animated,
GdkGravity gravity,
guint offset)
{
OverlayChild *child = g_slice_new (OverlayChild);
child->child = widget;
child->gravity = gravity;
child->fixed_position = fixed_position;
child->is_animated = is_animated;
child->offset = offset;
gtk_widget_set_parent (widget, GTK_WIDGET (overlay));
overlay->priv->children = g_slist_append (overlay->priv->children,
child);
}
static void
gedit_overlay_finalize (GObject *object)
{
GeditOverlay *overlay = GEDIT_OVERLAY (object);
g_slist_free (overlay->priv->children);
G_OBJECT_CLASS (gedit_overlay_parent_class)->finalize (object);
}
static void
gedit_overlay_dispose (GObject *object)
{
GeditOverlay *overlay = GEDIT_OVERLAY (object);
if (overlay->priv->hadjustment != NULL)
{
g_signal_handler_disconnect (overlay->priv->hadjustment,
overlay->priv->hadjustment_signal_id);
overlay->priv->hadjustment = NULL;
}
if (overlay->priv->vadjustment != NULL)
{
g_signal_handler_disconnect (overlay->priv->vadjustment,
overlay->priv->vadjustment_signal_id);
overlay->priv->vadjustment = NULL;
}
G_OBJECT_CLASS (gedit_overlay_parent_class)->dispose (object);
}
static void
gedit_overlay_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GeditOverlay *overlay = GEDIT_OVERLAY (object);
GeditOverlayPrivate *priv = overlay->priv;
switch (prop_id)
{
case PROP_MAIN_WIDGET:
g_value_set_object (value, priv->main_widget);
break;
case PROP_HADJUSTMENT:
g_value_set_object (value, priv->hadjustment);
break;
case PROP_VADJUSTMENT:
g_value_set_object (value, priv->vadjustment);
break;
case PROP_HSCROLL_POLICY:
if (GTK_IS_SCROLLABLE (priv->main_widget))
{
g_value_set_enum (value,
gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (priv->main_widget)));
}
else
{
g_value_set_enum (value, priv->hscroll_policy);
}
break;
case PROP_VSCROLL_POLICY:
if (GTK_IS_SCROLLABLE (priv->main_widget))
{
g_value_set_enum (value,
gtk_scrollable_get_vscroll_policy (GTK_SCROLLABLE (priv->main_widget)));
}
else
{
g_value_set_enum (value, priv->vscroll_policy);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gedit_overlay_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GeditOverlay *overlay = GEDIT_OVERLAY (object);
GeditOverlayPrivate *priv = overlay->priv;
switch (prop_id)
{
case PROP_MAIN_WIDGET:
overlay->priv->main_widget = g_value_get_object (value);
add_toplevel_widget (overlay,
overlay->priv->main_widget,
TRUE, FALSE, GDK_GRAVITY_STATIC,
0);
break;
case PROP_HADJUSTMENT:
gedit_overlay_set_hadjustment (overlay,
g_value_get_object (value));
break;
case PROP_VADJUSTMENT:
gedit_overlay_set_vadjustment (overlay,
g_value_get_object (value));
break;
case PROP_HSCROLL_POLICY:
if (GTK_IS_SCROLLABLE (priv->main_widget))
{
gtk_scrollable_set_hscroll_policy (GTK_SCROLLABLE (priv->main_widget),
g_value_get_enum (value));
}
else
{
priv->hscroll_policy = g_value_get_enum (value);
gtk_widget_queue_resize (GTK_WIDGET (overlay));
}
break;
case PROP_VSCROLL_POLICY:
if (GTK_IS_SCROLLABLE (priv->main_widget))
{
gtk_scrollable_set_vscroll_policy (GTK_SCROLLABLE (priv->main_widget),
g_value_get_enum (value));
}
else
{
priv->vscroll_policy = g_value_get_enum (value);
gtk_widget_queue_resize (GTK_WIDGET (overlay));
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gedit_overlay_realize (GtkWidget *widget)
{
GtkAllocation allocation;
GdkWindow *window;
GdkWindowAttr attributes;
gint attributes_mask;
GtkStyleContext *context;
gtk_widget_set_realized (widget, TRUE);
gtk_widget_get_allocation (widget, &allocation);
attributes.window_type = GDK_WINDOW_CHILD;
attributes.x = allocation.x;
attributes.y = allocation.y;
attributes.width = allocation.width;
attributes.height = allocation.height;
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.visual = gtk_widget_get_visual (widget);
attributes.event_mask = gtk_widget_get_events (widget);
attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK;
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
window = gdk_window_new (gtk_widget_get_parent_window (widget),
&attributes, attributes_mask);
gtk_widget_set_window (widget, window);
gdk_window_set_user_data (window, widget);
context = gtk_widget_get_style_context (widget);
gtk_style_context_set_state (context, GTK_STATE_FLAG_NORMAL);
gtk_style_context_set_background (context, window);
}
static void
gedit_overlay_get_preferred_width (GtkWidget *widget,
gint *minimum,
gint *natural)
{
GeditOverlayPrivate *priv = GEDIT_OVERLAY (widget)->priv;
OverlayChild *child;
GSList *children;
gint child_min, child_nat;
*minimum = 0;
*natural = 0;
for (children = priv->children; children; children = children->next)
{
child = children->data;
if (!gtk_widget_get_visible (child->child))
continue;
gtk_widget_get_preferred_width (child->child, &child_min, &child_nat);
*minimum = MAX (*minimum, child_min);
*natural = MAX (*natural, child_nat);
}
}
static void
gedit_overlay_get_preferred_height (GtkWidget *widget,
gint *minimum,
gint *natural)
{
GeditOverlayPrivate *priv = GEDIT_OVERLAY (widget)->priv;
OverlayChild *child;
GSList *children;
gint child_min, child_nat;
*minimum = 0;
*natural = 0;
for (children = priv->children; children; children = children->next)
{
child = children->data;
if (!gtk_widget_get_visible (child->child))
continue;
gtk_widget_get_preferred_height (child->child, &child_min, &child_nat);
*minimum = MAX (*minimum, child_min);
*natural = MAX (*natural, child_nat);
}
}
static void
set_children_positions (GeditOverlay *overlay)
{
GSList *l;
for (l = overlay->priv->children; l != NULL; l = g_slist_next (l))
{
GeditOverlayPrivate *priv = overlay->priv;
OverlayChild *child = (OverlayChild *)l->data;
GtkRequisition req;
GtkAllocation alloc;
if (child->child == priv->main_widget)
continue;
gtk_widget_get_preferred_size (child->child, &req, NULL);
/* FIXME: Add all the gravities here */
switch (child->gravity)
{
/* The gravity is treated as position and not as a gravity */
case GDK_GRAVITY_NORTH_EAST:
alloc.x = priv->main_alloc.width - req.width - child->offset;
alloc.y = 0;
break;
case GDK_GRAVITY_NORTH_WEST:
alloc.x = child->offset;
alloc.y = 0;
break;
case GDK_GRAVITY_SOUTH_WEST:
alloc.x = child->offset;
alloc.y = priv->main_alloc.height - req.height;
break;
default:
alloc.x = 0;
alloc.y = 0;
}
if (!child->fixed_position)
{
alloc.x *= gtk_adjustment_get_value (priv->hadjustment);
alloc.y *= gtk_adjustment_get_value (priv->vadjustment);
}
alloc.width = req.width;
alloc.height = req.height;
gtk_widget_size_allocate (child->child, &alloc);
}
}
static void
gedit_overlay_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
GeditOverlay *overlay = GEDIT_OVERLAY (widget);
GTK_WIDGET_CLASS (gedit_overlay_parent_class)->size_allocate (widget, allocation);
overlay->priv->main_alloc.x = 0;
overlay->priv->main_alloc.y = 0;
overlay->priv->main_alloc.width = allocation->width;
overlay->priv->main_alloc.height = allocation->height;
gtk_widget_size_allocate (overlay->priv->main_widget,
&overlay->priv->main_alloc);
set_children_positions (overlay);
}
static void
overlay_add (GtkContainer *overlay,
GtkWidget *widget)
{
add_toplevel_widget (GEDIT_OVERLAY (overlay), widget,
FALSE, FALSE, GDK_GRAVITY_STATIC, 0);
}
static void
gedit_overlay_remove (GtkContainer *overlay,
GtkWidget *widget)
{
GeditOverlay *goverlay = GEDIT_OVERLAY (overlay);
GSList *l;
for (l = goverlay->priv->children; l != NULL; l = g_slist_next (l))
{
OverlayChild *child = (OverlayChild *)l->data;
if (child->child == widget)
{
gtk_widget_unparent (widget);
goverlay->priv->children = g_slist_remove_link (goverlay->priv->children,
l);
free_container_child (child);
break;
}
}
}
static void
gedit_overlay_forall (GtkContainer *overlay,
gboolean include_internals,
GtkCallback callback,
gpointer callback_data)
{
GeditOverlay *goverlay = GEDIT_OVERLAY (overlay);
GSList *l;
for (l = goverlay->priv->children; l != NULL; l = g_slist_next (l))
{
OverlayChild *child = (OverlayChild *)l->data;
(* callback) (child->child, callback_data);
}
}
static GType
gedit_overlay_child_type (GtkContainer *overlay)
{
return GTK_TYPE_WIDGET;
}
static void
adjustment_value_changed (GtkAdjustment *adjustment,
GeditOverlay *overlay)
{
set_children_positions (overlay);
}
static void
gedit_overlay_set_hadjustment (GeditOverlay *overlay,
GtkAdjustment *adjustment)
{
GeditOverlayPrivate *priv = overlay->priv;
if (adjustment && priv->vadjustment == adjustment)
return;
if (priv->hadjustment != NULL)
{
g_signal_handler_disconnect (priv->hadjustment,
priv->hadjustment_signal_id);
g_object_unref (priv->hadjustment);
}
if (adjustment == NULL)
{
adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
0.0, 0.0, 0.0);
}
priv->hadjustment_signal_id =
g_signal_connect (adjustment,
"value-changed",
G_CALLBACK (adjustment_value_changed),
overlay);
priv->hadjustment = g_object_ref_sink (adjustment);
if (GTK_IS_SCROLLABLE (priv->main_widget))
{
g_object_set (priv->main_widget,
"hadjustment", adjustment,
NULL);
}
g_object_notify (G_OBJECT (overlay), "hadjustment");
}
static void
gedit_overlay_set_vadjustment (GeditOverlay *overlay,
GtkAdjustment *adjustment)
{
GeditOverlayPrivate *priv = overlay->priv;
if (adjustment && priv->vadjustment == adjustment)
return;
if (priv->vadjustment != NULL)