diff options
-rw-r--r-- | applets/wncklet/org.mate.panel.applet.window-list.gschema.xml.in | 10 | ||||
-rw-r--r-- | applets/wncklet/window-list.c | 270 | ||||
-rw-r--r-- | applets/wncklet/window-list.ui | 144 |
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> |