summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--applets/wncklet/org.mate.panel.applet.window-list.gschema.xml.in10
-rw-r--r--applets/wncklet/window-list.c270
-rw-r--r--applets/wncklet/window-list.ui144
3 files changed, 417 insertions, 7 deletions
diff --git a/applets/wncklet/org.mate.panel.applet.window-list.gschema.xml.in b/applets/wncklet/org.mate.panel.applet.window-list.gschema.xml.in
index 0f2d871d..08647c21 100644
--- a/applets/wncklet/org.mate.panel.applet.window-list.gschema.xml.in
+++ b/applets/wncklet/org.mate.panel.applet.window-list.gschema.xml.in
@@ -10,6 +10,16 @@
<summary>Show windows from all workspaces</summary>
<description>If true, the window list will show windows from all workspaces. Otherwise it will only display windows from the current workspace.</description>
</key>
+ <key name="show-window-thumbnails" type="b">
+ <default>true</default>
+ <summary>Display window thumbnails on hover</summary>
+ <description>If true, then when hovering over a taskbar item, a thumbnail of the window will appear. It will go away as soon as the mouse leaves the item.</description>
+ </key>
+ <key name="thumbnail-window-size" type="i">
+ <default>200</default>
+ <summary>Size of window thumbnails</summary>
+ <description>Size in pixels of the window preview thumbnail. The largest between width and height will use this value, the other one will be calculated to maintain the correct aspect ratio.</description>
+ </key>
<key name="group-windows" enum="org.mate.panel.applet.window-list.GroupingType">
<default>'never'</default>
<summary>When to group windows</summary>
diff --git a/applets/wncklet/window-list.c b/applets/wncklet/window-list.c
index 3cdcd834..833f0cce 100644
--- a/applets/wncklet/window-list.c
+++ b/applets/wncklet/window-list.c
@@ -19,6 +19,7 @@
#include <glib/gi18n.h>
#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
#define WNCK_I_KNOW_THIS_IS_UNSTABLE
#include <libwnck/libwnck.h>
#include <gio/gio.h>
@@ -35,8 +36,11 @@
typedef struct {
GtkWidget* applet;
GtkWidget* tasklist;
+ GtkWidget* preview;
gboolean include_all_workspaces;
+ gboolean show_window_thumbnails;
+ gint thumbnail_size;
WnckTasklistGroupingType grouping;
gboolean move_unminimized_windows;
@@ -52,6 +56,9 @@ typedef struct {
GtkWidget* properties_dialog;
GtkWidget* show_current_radio;
GtkWidget* show_all_radio;
+ GtkWidget* show_thumbnails_radio;
+ GtkWidget* hide_thumbnails_radio;
+ GtkWidget* thumbnail_size_spin;
GtkWidget* never_group_radio;
GtkWidget* auto_group_radio;
GtkWidget* always_group_radio;
@@ -139,6 +146,171 @@ static void applet_change_background(MatePanelApplet* applet, MatePanelAppletBac
}
}
+static GdkPixbuf *preview_window_thumbnail (WnckWindow *wnck_window, TasklistData *tasklist)
+{
+ GdkWindow *window;
+ GdkPixbuf *screenshot;
+ GdkPixbuf *thumbnail;
+ guchar *pixels;
+ double ratio;
+ int width, height;
+ int scale;
+
+ window = gdk_x11_window_foreign_new_for_display (gdk_display_get_default (), wnck_window_get_xid (wnck_window));
+
+ if (window == NULL)
+ return NULL;
+
+ scale = gdk_window_get_scale_factor (window);
+ width = gdk_window_get_width (window) * scale;
+ height = gdk_window_get_height (window) * scale;
+
+ /* Generate window screenshot for preview */
+ screenshot = gdk_pixbuf_get_from_window (window, 0, 0, width / scale, height / scale);
+ g_object_unref (window);
+
+ if (screenshot == NULL)
+ return NULL;
+
+ /* Determine whether the contents of the screenshot are empty */
+ pixels = gdk_pixbuf_get_pixels (screenshot);
+ if (!g_strcmp0 ((const char *)pixels, ""))
+ {
+ g_object_unref (screenshot);
+ return NULL;
+ }
+
+ /* Scale to configured size while maintaining aspect ratio */
+ if (width > height)
+ {
+ ratio = (double) height / (double) width;
+ width = MIN(width, tasklist->thumbnail_size);
+ height = width * ratio;
+ }
+ else
+ {
+ ratio = (double) width / (double) height;
+ height = MIN(height, tasklist->thumbnail_size);
+ width = height * ratio;
+ }
+
+ thumbnail = gdk_pixbuf_scale_simple (screenshot, width, height, GDK_INTERP_BILINEAR);
+ g_object_unref (screenshot);
+
+ return thumbnail;
+}
+
+#define PREVIEW_PADDING 5
+static void preview_window_reposition (TasklistData *tasklist, GdkPixbuf *thumbnail)
+{
+ GdkMonitor *monitor;
+ GdkRectangle monitor_geom;
+ int x_pos, y_pos;
+ int width, height;
+
+ width = gdk_pixbuf_get_width (thumbnail);
+ height = gdk_pixbuf_get_height (thumbnail);
+
+ /* Resize window to fit thumbnail */
+ gtk_window_resize (GTK_WINDOW (tasklist->preview), width, height);
+
+ /* Set position at pointer, then re-adjust from there to just outside of the pointer */
+ gtk_window_set_position (GTK_WINDOW (tasklist->preview), GTK_WIN_POS_MOUSE);
+ gtk_window_get_position (GTK_WINDOW (tasklist->preview), &x_pos, &y_pos);
+
+ /* Get geometry of monitor where tasklist is located to calculate correct position of preview */
+ monitor = gdk_display_get_monitor_at_point (gdk_display_get_default (), x_pos, y_pos);
+ gdk_monitor_get_geometry (monitor, &monitor_geom);
+
+ /* Add padding to clear the panel */
+ switch (mate_panel_applet_get_orient (MATE_PANEL_APPLET (tasklist->applet)))
+ {
+ case MATE_PANEL_APPLET_ORIENT_LEFT:
+ x_pos = monitor_geom.width + monitor_geom.x - (width + tasklist->size) - PREVIEW_PADDING;
+ break;
+ case MATE_PANEL_APPLET_ORIENT_RIGHT:
+ x_pos = tasklist->size + PREVIEW_PADDING;
+ break;
+ case MATE_PANEL_APPLET_ORIENT_UP:
+ y_pos = monitor_geom.height + monitor_geom.y - (height + tasklist->size) - PREVIEW_PADDING;
+ break;
+ case MATE_PANEL_APPLET_ORIENT_DOWN:
+ default:
+ y_pos = tasklist->size + PREVIEW_PADDING;
+ break;
+ }
+
+ gtk_window_move (GTK_WINDOW (tasklist->preview), x_pos, y_pos);
+}
+
+static gboolean preview_window_draw (GtkWidget *widget, cairo_t *cr, GdkPixbuf *thumbnail)
+{
+ GtkStyleContext *context;
+
+ context = gtk_widget_get_style_context (widget);
+ gtk_render_icon (context, cr, thumbnail, 0, 0);
+
+ return FALSE;
+}
+
+static gboolean applet_enter_notify_event (WnckTasklist *tl, GList *wnck_windows, TasklistData *tasklist)
+{
+ GdkPixbuf *thumbnail;
+ WnckWindow *wnck_window = NULL;
+ int n_windows;
+
+ if (tasklist->preview != NULL)
+ {
+ gtk_widget_destroy (tasklist->preview);
+ tasklist->preview = NULL;
+ }
+
+ if (!tasklist->show_window_thumbnails || wnck_windows == NULL)
+ return FALSE;
+
+ n_windows = g_list_length (wnck_windows);
+ /* TODO: Display a list of stacked thumbnails for grouped windows. */
+ if (n_windows == 1)
+ {
+ GList* l = wnck_windows;
+ if (l != NULL)
+ wnck_window = (WnckWindow*)l->data;
+ }
+
+ if (wnck_window == NULL)
+ return FALSE;
+
+ thumbnail = preview_window_thumbnail (wnck_window, tasklist);
+
+ if (thumbnail == NULL)
+ return FALSE;
+
+ /* Create window to display preview */
+ tasklist->preview = gtk_window_new (GTK_WINDOW_POPUP);
+
+ gtk_widget_set_app_paintable (tasklist->preview, TRUE);
+ gtk_window_set_resizable (GTK_WINDOW (tasklist->preview), TRUE);
+
+ preview_window_reposition (tasklist, thumbnail);
+
+ gtk_widget_show (tasklist->preview);
+
+ g_signal_connect_data (G_OBJECT (tasklist->preview), "draw", G_CALLBACK (preview_window_draw), thumbnail, (GClosureNotify) g_object_unref, 0);
+
+ return FALSE;
+}
+
+static gboolean applet_leave_notify_event (WnckTasklist *tl, GList *wnck_windows, TasklistData *tasklist)
+{
+ if (tasklist->preview != NULL)
+ {
+ gtk_widget_destroy (tasklist->preview);
+ tasklist->preview = NULL;
+ }
+
+ return FALSE;
+}
+
static void applet_change_pixel_size(MatePanelApplet* applet, gint size, TasklistData* tasklist)
{
if (tasklist->size == size)
@@ -227,6 +399,55 @@ static void display_all_workspaces_changed(GSettings* settings, gchar* key, Task
tasklist_properties_update_content_radio(tasklist);
}
+static void tasklist_update_thumbnails_radio(TasklistData* tasklist)
+{
+ GtkWidget* button;
+
+ if (tasklist->show_thumbnails_radio == NULL || tasklist->hide_thumbnails_radio == NULL)
+ return;
+
+ if (tasklist->show_window_thumbnails)
+ {
+ button = tasklist->show_thumbnails_radio;
+ }
+ else
+ {
+ button = tasklist->hide_thumbnails_radio;
+ }
+
+ if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+}
+
+static void window_thumbnails_changed(GSettings *settings, gchar* key, TasklistData* tasklist)
+{
+ gboolean value;
+
+ value = g_settings_get_boolean(settings, key);
+
+ tasklist->show_window_thumbnails = (value != 0);
+
+ tasklist_update_thumbnails_radio(tasklist);
+}
+
+static void tasklist_update_thumbnail_size_spin(TasklistData* tasklist)
+{
+ GtkWidget* button;
+
+ if (!tasklist->thumbnail_size)
+ return;
+
+ button = tasklist->thumbnail_size_spin;
+
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), (gdouble)tasklist->thumbnail_size);
+}
+
+static void thumbnail_size_changed(GSettings *settings, gchar* key, TasklistData* tasklist)
+{
+ tasklist->thumbnail_size = g_settings_get_int(settings, key);
+ tasklist_update_thumbnail_size_spin(tasklist);
+}
+
static GtkWidget* get_grouping_button(TasklistData* tasklist, WnckTasklistGroupingType type)
{
switch (type)
@@ -304,6 +525,14 @@ static void setup_gsettings(TasklistData* tasklist)
G_CALLBACK (display_all_workspaces_changed),
tasklist);
g_signal_connect (tasklist->settings,
+ "changed::show-window-thumbnails",
+ G_CALLBACK (window_thumbnails_changed),
+ tasklist);
+ g_signal_connect (tasklist->settings,
+ "changed::thumbnail-window-size",
+ G_CALLBACK (thumbnail_size_changed),
+ tasklist);
+ g_signal_connect (tasklist->settings,
"changed::group-windows",
G_CALLBACK (group_windows_changed),
tasklist);
@@ -421,6 +650,10 @@ gboolean window_list_applet_fill(MatePanelApplet* applet)
tasklist->include_all_workspaces = g_settings_get_boolean (tasklist->settings, "display-all-workspaces");
+ tasklist->show_window_thumbnails = g_settings_get_boolean (tasklist->settings, "show-window-thumbnails");
+
+ tasklist->thumbnail_size = g_settings_get_int (tasklist->settings, "thumbnail-window-size");
+
tasklist->grouping = g_settings_get_enum (tasklist->settings, "group-windows");
tasklist->move_unminimized_windows = g_settings_get_boolean (tasklist->settings, "move-unminimized-windows");
@@ -451,6 +684,8 @@ gboolean window_list_applet_fill(MatePanelApplet* applet)
wnck_tasklist_set_icon_loader(WNCK_TASKLIST(tasklist->tasklist), icon_loader_func, tasklist, NULL);
g_signal_connect(G_OBJECT(tasklist->tasklist), "destroy", G_CALLBACK(destroy_tasklist), tasklist);
+ g_signal_connect(G_OBJECT(tasklist->tasklist), "task_enter_notify", G_CALLBACK(applet_enter_notify_event), tasklist);
+ g_signal_connect(G_OBJECT(tasklist->tasklist), "task_leave_notify", G_CALLBACK(applet_leave_notify_event), tasklist);
g_signal_connect(G_OBJECT(tasklist->applet), "size_allocate", G_CALLBACK(applet_size_allocate), tasklist);
@@ -584,6 +819,16 @@ static void group_windows_toggled(GtkToggleButton* button, TasklistData* tasklis
}
}
+static void show_thumbnails_toggled(GtkToggleButton* button, TasklistData* tasklist)
+{
+ g_settings_set_boolean(tasklist->settings, "show-window-thumbnails", gtk_toggle_button_get_active(button));
+}
+
+static void thumbnail_size_spin_changed(GtkSpinButton* button, TasklistData* tasklist)
+{
+ g_settings_set_int(tasklist->settings, "thumbnail-window-size", gtk_spin_button_get_value_as_int(button));
+}
+
static void move_minimized_toggled(GtkToggleButton* button, TasklistData* tasklist)
{
g_settings_set_boolean(tasklist->settings, "move-unminimized-windows", gtk_toggle_button_get_active(button));
@@ -627,6 +872,7 @@ static void setup_sensitivity(TasklistData* tasklist, GtkBuilder* builder, const
static void setup_dialog(GtkBuilder* builder, TasklistData* tasklist)
{
GtkWidget* button;
+ GtkAdjustment *adjustment;
tasklist->show_current_radio = WID("show_current_radio");
tasklist->show_all_radio = WID("show_all_radio");
@@ -639,6 +885,17 @@ static void setup_dialog(GtkBuilder* builder, TasklistData* tasklist)
setup_sensitivity(tasklist, builder, "never_group_radio", "auto_group_radio", "always_group_radio", "group-windows" /* key */);
+ tasklist->show_thumbnails_radio = WID("show_thumbnails_radio");
+ tasklist->hide_thumbnails_radio = WID("hide_thumbnails_radio");
+ tasklist->thumbnail_size_spin = WID("thumbnail_size_spin");
+
+ setup_sensitivity(tasklist, builder, "show_thumbnails_radio", "hide_thumbnails_radio", NULL, "show-window-thumbnails" /* key */);
+ gtk_widget_set_sensitive(tasklist->thumbnail_size_spin, TRUE);
+ adjustment = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(tasklist->thumbnail_size_spin));
+ gtk_adjustment_set_lower (adjustment, 0);
+ gtk_adjustment_set_upper (adjustment, 999);
+ gtk_adjustment_set_step_increment (adjustment, 1);
+
tasklist->minimized_windows_label = WID("minimized_windows_label");
tasklist->move_minimized_radio = WID("move_minimized_radio");
tasklist->change_workspace_radio = WID("change_workspace_radio");
@@ -656,6 +913,13 @@ static void setup_dialog(GtkBuilder* builder, TasklistData* tasklist)
g_signal_connect(G_OBJECT(tasklist->auto_group_radio), "toggled", (GCallback) group_windows_toggled, tasklist);
g_signal_connect(G_OBJECT(tasklist->always_group_radio), "toggled", (GCallback) group_windows_toggled, tasklist);
+ /* show thumbnails on hover: */
+ tasklist_update_thumbnails_radio(tasklist);
+ g_signal_connect(G_OBJECT(tasklist->show_thumbnails_radio), "toggled", (GCallback) show_thumbnails_toggled, tasklist);
+ /* change thumbnail size: */
+ tasklist_update_thumbnail_size_spin(tasklist);
+ g_signal_connect(G_OBJECT(tasklist->thumbnail_size_spin), "value-changed", (GCallback) thumbnail_size_spin_changed, tasklist);
+
/* move window when unminimizing: */
tasklist_update_unminimization_radio(tasklist);
g_signal_connect(G_OBJECT(tasklist->move_minimized_radio), "toggled", (GCallback) move_minimized_toggled, tasklist);
@@ -698,6 +962,8 @@ static void destroy_tasklist(GtkWidget* widget, TasklistData* tasklist)
{
g_signal_handlers_disconnect_by_data (G_OBJECT (tasklist->applet), tasklist);
+ g_signal_handlers_disconnect_by_data (G_OBJECT (tasklist->tasklist), tasklist);
+
g_signal_handlers_disconnect_by_data (tasklist->settings, tasklist);
g_object_unref(tasklist->settings);
@@ -705,6 +971,8 @@ static void destroy_tasklist(GtkWidget* widget, TasklistData* tasklist)
if (tasklist->properties_dialog)
gtk_widget_destroy(tasklist->properties_dialog);
- g_free(tasklist);
+ if (tasklist->preview)
+ gtk_widget_destroy(tasklist->preview);
+ g_free(tasklist);
}
diff --git a/applets/wncklet/window-list.ui b/applets/wncklet/window-list.ui
index 851a72a0..174285a0 100644
--- a/applets/wncklet/window-list.ui
+++ b/applets/wncklet/window-list.ui
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.22.0 -->
+<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.22"/>
<object class="GtkImage" id="image1">
@@ -17,6 +17,9 @@
<property name="border_width">5</property>
<property name="title" translatable="yes">Window List Preferences</property>
<property name="type_hint">normal</property>
+ <child>
+ <placeholder/>
+ </child>
<child internal-child="vbox">
<object class="GtkBox" id="dialog-vbox2">
<property name="visible">True</property>
@@ -158,6 +161,138 @@
</packing>
</child>
<child>
+ <object class="GtkBox" id="vbox15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Window Thumbnails</property>
+ <property name="xalign">0</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkBox" id="vbox16">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkRadioButton" id="show_thumbnails_radio">
+ <property name="label" translatable="yes">Show _thumbnails on hover</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="hide_thumbnails_radio">
+ <property name="label" translatable="yes">_Hide thumbnails on hover</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">show_thumbnails_radio</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Thumbnail width in pixels. Window aspect ratio will be maintained.</property>
+ <property name="label" translatable="yes">Thumbnail width:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="thumbnail_size_spin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">3</property>
+ <property name="caps_lock_warning">False</property>
+ <property name="placeholder_text" translatable="yes">px</property>
+ <property name="input_purpose">number</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <property name="value">200</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkBox" id="vbox11">
<property name="visible">True</property>
<property name="can_focus">False</property>
@@ -251,7 +386,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">2</property>
+ <property name="position">3</property>
</packing>
</child>
<child>
@@ -332,7 +467,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">3</property>
+ <property name="position">4</property>
</packing>
</child>
</object>
@@ -348,8 +483,5 @@
<action-widget response="-11">help_button</action-widget>
<action-widget response="0">done_button</action-widget>
</action-widgets>
- <child>
- <placeholder/>
- </child>
</object>
</interface>