[gnome-builder] minimap: animate the minimap in/out of view
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] minimap: animate the minimap in/out of view
- Date: Tue, 28 Apr 2015 23:36:24 +0000 (UTC)
commit fd8aea6c9d0ffe9c948b09d977f37cc2eb68c346
Author: Christian Hergert <christian hergert me>
Date: Tue Apr 28 16:34:30 2015 -0700
minimap: animate the minimap in/out of view
When the mouse is placed over an editor widget (such as the source map
or the source view), animate the source map in. When it leaves, animate
it out after a short delay.
I assume we want to add to this the ability to hide the map once typing
begins similar to the scrolled window.
data/ui/gb-editor-frame.ui | 21 +++--
libide/ide-source-map.c | 163 ++++++++++++++++++++++++++++++++--
src/editor/gb-editor-frame-private.h | 7 ++-
src/editor/gb-editor-frame.c | 118 ++++++++++++++++++++++++-
4 files changed, 291 insertions(+), 18 deletions(-)
---
diff --git a/data/ui/gb-editor-frame.ui b/data/ui/gb-editor-frame.ui
index 91a9a73..00f1912 100644
--- a/data/ui/gb-editor-frame.ui
+++ b/data/ui/gb-editor-frame.ui
@@ -3,7 +3,7 @@
<!-- interface-requires gtk+ 3.15 -->
<template class="GbEditorFrame" parent="GtkBin">
<child>
- <object class="GtkOverlay">
+ <object class="GtkOverlay" id="frame_overlay">
<property name="expand">true</property>
<property name="visible">true</property>
<child type="overlay">
@@ -42,10 +42,16 @@
<property name="orientation">horizontal</property>
<property name="visible">true</property>
<child>
- <object class="GtkOverlay">
+ <object class="GtkOverlay" id="source_overlay">
<property name="expand">true</property>
<property name="visible">true</property>
<child type="overlay">
+ <object class="GbEditorMapBin" id="source_map_container">
+ <property name="floating-bar">floating_bar</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ <child type="overlay">
<object class="GtkRevealer" id="search_revealer">
<property name="halign">end</property>
<property name="valign">start</property>
@@ -146,15 +152,14 @@
</child>
</object>
</child>
- <child>
- <object class="GbEditorMapBin" id="source_map_container">
- <property name="floating-bar">floating_bar</property>
- <property name="visible">true</property>
- </object>
- </child>
</object>
</child>
</object>
</child>
</template>
+ <object class="GtkAdjustment" id="overlay_adj">
+ <property name="lower">0.0</property>
+ <property name="upper">1.0</property>
+ <property name="value">1.0</property>
+ </object>
</interface>
diff --git a/libide/ide-source-map.c b/libide/ide-source-map.c
index 50b0187..b516b00 100644
--- a/libide/ide-source-map.c
+++ b/libide/ide-source-map.c
@@ -27,8 +27,9 @@
#include "ide-source-map.h"
#include "ide-source-view.h"
-#define DEFAULT_WIDTH 100
-#define DELAYED_DRAW_TIMEOUT_MSEC 34
+#define DEFAULT_WIDTH 100
+#define DELAYED_HIDE_TIMEOUT 1000
+#define DELAYED_SHOW_TIMEOUT 50
struct _IdeSourceMap
{
@@ -44,7 +45,11 @@ struct _IdeSourceMap
GtkSourceView *view;
GtkSourceGutterRenderer *line_renderer;
+ guint delayed_reveal_timeout;
+
guint in_press : 1;
+ guint is_hiding : 1;
+ guint show_map : 1;
};
struct _IdeSourceMapClass
@@ -61,7 +66,79 @@ enum {
LAST_PROP
};
+enum {
+ SHOW_MAP,
+ HIDE_MAP,
+ LAST_SIGNAL
+};
+
static GParamSpec *gParamSpecs [LAST_PROP];
+static guint gSignals [LAST_SIGNAL];
+
+static gboolean
+ide_source_map_do_reveal (gpointer data)
+{
+ IdeSourceMap *self = data;
+
+ g_assert (IDE_IS_SOURCE_MAP (self));
+
+ self->delayed_reveal_timeout = 0;
+
+ /* ignore if we are already at this state */
+ if ((!self->is_hiding) == self->show_map)
+ return G_SOURCE_REMOVE;
+
+ self->show_map = !self->is_hiding;
+
+ if (self->is_hiding)
+ g_signal_emit (self, gSignals [HIDE_MAP], 0);
+ else
+ g_signal_emit (self, gSignals [SHOW_MAP], 0);
+
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+ide_source_map__enter_notify_event (IdeSourceMap *self,
+ GdkEventCrossing *event,
+ GtkWidget *widget)
+{
+ g_assert (IDE_IS_SOURCE_MAP (self));
+ g_assert (event != NULL);
+ g_assert (GTK_IS_WIDGET (widget));
+
+ self->is_hiding = FALSE;
+
+ if (self->delayed_reveal_timeout != 0)
+ g_source_remove (self->delayed_reveal_timeout);
+
+ self->delayed_reveal_timeout = g_timeout_add (DELAYED_SHOW_TIMEOUT,
+ ide_source_map_do_reveal,
+ self);
+
+ return GDK_EVENT_PROPAGATE;
+}
+
+static gboolean
+ide_source_map__leave_notify_event (IdeSourceMap *self,
+ GdkEventCrossing *event,
+ GtkWidget *widget)
+{
+ g_assert (IDE_IS_SOURCE_MAP (self));
+ g_assert (event != NULL);
+ g_assert (GTK_IS_WIDGET (widget));
+
+ self->is_hiding = TRUE;
+
+ if (self->delayed_reveal_timeout != 0)
+ g_source_remove (self->delayed_reveal_timeout);
+
+ self->delayed_reveal_timeout = g_timeout_add (DELAYED_HIDE_TIMEOUT,
+ ide_source_map_do_reveal,
+ self);
+
+ return GDK_EVENT_PROPAGATE;
+}
static void
ide_source_map_rebuild_css (IdeSourceMap *self)
@@ -334,6 +411,18 @@ ide_source_map_set_view (IdeSourceMap *self,
self,
G_CONNECT_SWAPPED);
+ g_signal_connect_object (view,
+ "enter-notify-event",
+ G_CALLBACK (ide_source_map__enter_notify_event),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (view,
+ "leave-notify-event",
+ G_CALLBACK (ide_source_map__leave_notify_event),
+ self,
+ G_CONNECT_SWAPPED);
+
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
ide_source_map__buffer_notify_style_scheme (self, NULL, buffer);
@@ -361,6 +450,12 @@ ide_source_map_set_view (IdeSourceMap *self,
self,
G_CONNECT_SWAPPED);
+ if ((gtk_widget_get_events (GTK_WIDGET (self->view)) & GDK_ENTER_NOTIFY_MASK) == 0)
+ gtk_widget_add_events (GTK_WIDGET (self->view), GDK_ENTER_NOTIFY_MASK);
+
+ if ((gtk_widget_get_events (GTK_WIDGET (self->view)) & GDK_LEAVE_NOTIFY_MASK) == 0)
+ gtk_widget_add_events (GTK_WIDGET (self->view), GDK_LEAVE_NOTIFY_MASK);
+
ide_source_map_rebuild_css (self);
}
@@ -688,16 +783,22 @@ ide_source_map_do_scroll_event (IdeSourceMap *self,
}
static void
-ide_source_map_finalize (GObject *object)
+ide_source_map_destroy (GtkWidget *widget)
{
- IdeSourceMap *self = (IdeSourceMap *)object;
+ IdeSourceMap *self = (IdeSourceMap *)widget;
+
+ if (self->delayed_reveal_timeout)
+ {
+ g_source_remove (self->delayed_reveal_timeout);
+ self->delayed_reveal_timeout = 0;
+ }
g_clear_object (&self->box_css_provider);
g_clear_object (&self->view_css_provider);
g_clear_pointer (&self->font_desc, pango_font_description_free);
ide_clear_weak_pointer (&self->view);
- G_OBJECT_CLASS (ide_source_map_parent_class)->finalize (object);
+ GTK_WIDGET_CLASS (ide_source_map_parent_class)->destroy (widget);
}
static void
@@ -749,10 +850,10 @@ ide_source_map_class_init (IdeSourceMapClass *klass)
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GtkOverlayClass *overlay_class = GTK_OVERLAY_CLASS (klass);
- object_class->finalize = ide_source_map_finalize;
object_class->get_property = ide_source_map_get_property;
object_class->set_property = ide_source_map_set_property;
+ widget_class->destroy = ide_source_map_destroy;
widget_class->get_preferred_height = ide_source_map_get_preferred_height;
widget_class->get_preferred_width = ide_source_map_get_preferred_width;
widget_class->size_allocate = ide_source_map_size_allocate;
@@ -774,6 +875,26 @@ ide_source_map_class_init (IdeSourceMapClass *klass)
PANGO_TYPE_FONT_DESCRIPTION,
(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_FONT_DESC, gParamSpecs [PROP_FONT_DESC]);
+
+ gSignals [HIDE_MAP] =
+ g_signal_new ("hide-map",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ 0,
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE,
+ 0);
+
+ gSignals [SHOW_MAP] =
+ g_signal_new ("show-map",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ 0,
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE,
+ 0);
}
static void
@@ -834,7 +955,7 @@ ide_source_map_init (IdeSourceMap *self)
*/
gutter = gtk_source_view_get_gutter (self->child_view, GTK_TEXT_WINDOW_LEFT);
self->line_renderer = g_object_new (IDE_TYPE_LINE_CHANGE_GUTTER_RENDERER,
- "size", 3,
+ "size", 2,
"visible", TRUE,
NULL);
gtk_source_gutter_insert (gutter, self->line_renderer, 0);
@@ -850,7 +971,6 @@ ide_source_map_init (IdeSourceMap *self)
G_CALLBACK (ide_source_map__overlay_box_button_press_event),
self,
G_CONNECT_SWAPPED);
- gtk_widget_add_events (GTK_WIDGET (self->overlay_box), GDK_SCROLL_MASK);
g_signal_connect_object (self->overlay_box,
"scroll-event",
G_CALLBACK (ide_source_map_do_scroll_event),
@@ -878,4 +998,31 @@ ide_source_map_init (IdeSourceMap *self)
gtk_source_completion_block_interactive (completion);
ide_source_map_set_font_name (self, "Monospace 1");
+
+ gtk_widget_add_events (GTK_WIDGET (self->overlay_box),
+ (GDK_SCROLL_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK));
+ gtk_widget_add_events (GTK_WIDGET (self->child_view),
+ (GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK));
+
+ g_signal_connect_object (self->overlay_box,
+ "enter-notify-event",
+ G_CALLBACK (ide_source_map__enter_notify_event),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (self->overlay_box,
+ "leave-notify-event",
+ G_CALLBACK (ide_source_map__leave_notify_event),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (self->child_view,
+ "enter-notify-event",
+ G_CALLBACK (ide_source_map__enter_notify_event),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (self->child_view,
+ "leave-notify-event",
+ G_CALLBACK (ide_source_map__leave_notify_event),
+ self,
+ G_CONNECT_SWAPPED);
}
diff --git a/src/editor/gb-editor-frame-private.h b/src/editor/gb-editor-frame-private.h
index 6bd1055..7044b58 100644
--- a/src/editor/gb-editor-frame-private.h
+++ b/src/editor/gb-editor-frame-private.h
@@ -22,6 +22,7 @@
#include <gtk/gtk.h>
#include <ide.h>
+#include "gb-editor-map-bin.h"
#include "gd-tagged-entry.h"
#include "nautilus-floating-bar.h"
@@ -33,14 +34,18 @@ struct _GbEditorFrame
NautilusFloatingBar *floating_bar;
GtkLabel *mode_name_label;
+ GtkAdjustment *overlay_adj;
GtkLabel *overwrite_label;
GtkScrolledWindow *scrolled_window;
GtkRevealer *search_revealer;
GdTaggedEntry *search_entry;
GdTaggedEntryTag *search_entry_tag;
IdeSourceView *source_view;
- GtkBox *source_map_container;
+ GbEditorMapBin *source_map_container;
IdeSourceMap *source_map;
+ GtkOverlay *source_overlay;
+
+ IdeAnimation *map_animation;
gulong cursor_moved_handler;
};
diff --git a/src/editor/gb-editor-frame.c b/src/editor/gb-editor-frame.c
index 79645a6..deea3e9 100644
--- a/src/editor/gb-editor-frame.c
+++ b/src/editor/gb-editor-frame.c
@@ -29,6 +29,9 @@
#include "gb-view-stack.h"
#include "gb-widget.h"
+#define MINIMAP_HIDE_DURATION 500
+#define MINIMAP_SHOW_DURATION 250
+
G_DEFINE_TYPE (GbEditorFrame, gb_editor_frame, GTK_TYPE_BIN)
enum {
@@ -46,6 +49,57 @@ enum {
static GParamSpec *gParamSpecs [LAST_PROP];
static void
+gb_editor_frame_animate_map (GbEditorFrame *self,
+ gboolean visible)
+{
+ IdeAnimation *animation;
+ GdkFrameClock *frame_clock;
+ gdouble value;
+ guint duration;
+
+ g_assert (GB_IS_EDITOR_FRAME (self));
+
+ if (self->map_animation)
+ {
+ animation = self->map_animation;
+ ide_clear_weak_pointer (&self->map_animation);
+ ide_animation_stop (animation);
+ }
+
+ frame_clock = gtk_widget_get_frame_clock (GTK_WIDGET (self->source_map_container));
+ duration = visible ? MINIMAP_SHOW_DURATION : MINIMAP_HIDE_DURATION;
+ value = visible ? 0.0 : 1.0;
+
+ animation = ide_object_animate (self->overlay_adj,
+ IDE_ANIMATION_EASE_IN_OUT_QUAD,
+ duration,
+ frame_clock,
+ "value", value,
+ NULL);
+ ide_set_weak_pointer (&self->map_animation, animation);
+}
+
+static void
+gb_editor_frame_show_map (GbEditorFrame *self,
+ IdeSourceMap *source_map)
+{
+ g_assert (GB_IS_EDITOR_FRAME (self));
+ g_assert (IDE_IS_SOURCE_MAP (source_map));
+
+ gb_editor_frame_animate_map (self, TRUE);
+}
+
+static void
+gb_editor_frame_hide_map (GbEditorFrame *self,
+ IdeSourceMap *source_map)
+{
+ g_assert (GB_IS_EDITOR_FRAME (self));
+ g_assert (IDE_IS_SOURCE_MAP (source_map));
+
+ gb_editor_frame_animate_map (self, FALSE);
+}
+
+static void
gb_editor_frame_set_position_label (GbEditorFrame *self,
const gchar *text)
{
@@ -485,6 +539,16 @@ gb_editor_frame_set_show_map (GbEditorFrame *self,
"view", self->source_view,
"visible", TRUE,
NULL);
+ g_signal_connect_object (self->source_map,
+ "show-map",
+ G_CALLBACK (gb_editor_frame_show_map),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (self->source_map,
+ "hide-map",
+ G_CALLBACK (gb_editor_frame_hide_map),
+ self,
+ G_CONNECT_SWAPPED);
gtk_container_add (GTK_CONTAINER (self->source_map_container),
GTK_WIDGET (self->source_map));
}
@@ -522,6 +586,42 @@ gb_editor_frame__source_view_populate_popup (GbEditorFrame *self,
}
}
+static gboolean
+gb_editor_frame__source_overlay_get_child_position (GbEditorFrame *self,
+ GtkWidget *widget,
+ GtkAllocation *alloc,
+ GtkOverlay *overlay)
+{
+ GtkAllocation main_alloc;
+ GtkRequisition req;
+
+ g_assert (GTK_IS_OVERLAY (overlay));
+ g_assert (GB_IS_EDITOR_FRAME (self));
+ g_assert (GTK_IS_WIDGET (widget));
+ g_assert (alloc != NULL);
+
+ if (widget == (GtkWidget *)self->source_map_container)
+ {
+ gdouble value;
+
+ gtk_widget_get_allocation (GTK_WIDGET (self), &main_alloc);
+ gtk_widget_get_preferred_size (widget, &req, NULL);
+
+ alloc->x = main_alloc.x + main_alloc.width - req.width;
+ alloc->width = req.width;
+ alloc->y = main_alloc.y;
+ alloc->height = main_alloc.height;
+
+ /* adjust for animation */
+ value = gtk_adjustment_get_value (self->overlay_adj);
+ alloc->x += (value * alloc->width);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static void
gb_editor_frame_constructed (GObject *object)
{
@@ -571,6 +671,8 @@ gb_editor_frame_dispose (GObject *object)
{
GbEditorFrame *self = (GbEditorFrame *)object;
+ ide_clear_weak_pointer (&self->map_animation);
+
if (self->source_view && self->cursor_moved_handler)
{
GtkTextBuffer *buffer;
@@ -677,11 +779,13 @@ gb_editor_frame_class_init (GbEditorFrameClass *klass)
GB_WIDGET_CLASS_BIND (klass, GbEditorFrame, floating_bar);
GB_WIDGET_CLASS_BIND (klass, GbEditorFrame, mode_name_label);
+ GB_WIDGET_CLASS_BIND (klass, GbEditorFrame, overlay_adj);
GB_WIDGET_CLASS_BIND (klass, GbEditorFrame, overwrite_label);
GB_WIDGET_CLASS_BIND (klass, GbEditorFrame, scrolled_window);
GB_WIDGET_CLASS_BIND (klass, GbEditorFrame, search_entry);
- GB_WIDGET_CLASS_BIND (klass, GbEditorFrame, source_map_container);
GB_WIDGET_CLASS_BIND (klass, GbEditorFrame, search_revealer);
+ GB_WIDGET_CLASS_BIND (klass, GbEditorFrame, source_map_container);
+ GB_WIDGET_CLASS_BIND (klass, GbEditorFrame, source_overlay);
GB_WIDGET_CLASS_BIND (klass, GbEditorFrame, source_view);
g_type_ensure (NAUTILUS_TYPE_FLOATING_BAR);
@@ -715,6 +819,18 @@ gb_editor_frame_init (GbEditorFrame *self)
g_object_bind_property (self->source_view, "overwrite", self->overwrite_label, "visible",
G_BINDING_SYNC_CREATE);
+ g_signal_connect_object (self->source_overlay,
+ "get-child-position",
+ G_CALLBACK (gb_editor_frame__source_overlay_get_child_position),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (self->overlay_adj,
+ "value-changed",
+ G_CALLBACK (gtk_widget_queue_resize),
+ self->source_map_container,
+ G_CONNECT_SWAPPED);
+
/*
* we want to rubberbanding search until enter has been pressed or next/previous actions
* have been activated.
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]