A small patch for workspace.js



Hello folks,

I recently tried gnome-shell on my ubuntu system and liked the overlay design concept very much. While using the shell, I felt that it would be better to have window action menu appeared in overview mode also. (when user right clicks on a window). Later, this menu can be extend to support app specific menus.

So with this idea, I went ahead and opened source and tried changing it. I'm attaching the patch to this mail. This is not yet completed, just looking for your suggestions. I couldn't find mutter api to get current state of window i.e. is_maximized/is_minimized etc., to hide irrelevant options (we shouldn't show maximize option when the window is already maximized). similarly, there is no api function to keep window on all workspaces :( If you ppl know alternative way of doing these things, please let me know.

Similary I've another idea too. When user maximizes a window, how about merging titlebar of the window with top-panel ? we are already showing window title in top-panel, why not buttons (I mean minimize, maximize buttons). That way we can save some screen real estate. For this we may need to work with mutter folks, not sure.

--
Naagas.
0__
_> /__...
(_) \(_) Burn fat, not fuel
diff --git a/js/ui/workspaces.js b/js/ui/workspaces.js
index 1cde8ac..a985c7d 100644
--- a/js/ui/workspaces.js
+++ b/js/ui/workspaces.js
@@ -10,6 +10,9 @@ const Meta = imports.gi.Meta;
 const Pango = imports.gi.Pango;
 const Shell = imports.gi.Shell;
 const Signals = imports.signals;
+const Gettext = imports.gettext.domain('gnome-shell');
+const _ = Gettext.gettext;
+
 
 const DND = imports.ui.dnd;
 const Lightbox = imports.ui.lightbox;
@@ -135,6 +138,13 @@ WindowClone.prototype = {
         this._zooming = false;
     },
 
+    updateClone: function() {
+        this.actor.set_source(this.realWindow.get_texture());
+	this.origX = this.realWindow.x;
+	this.origY = this.realWindow.y;
+        this.emit('clone-source-changed');
+    },
+
     setVisibleWithChrome: function(visible) {
         if (visible) {
             this.actor.show();
@@ -268,7 +278,15 @@ WindowClone.prototype = {
     },
 
     _onButtonRelease : function (actor, event) {
-        this.emit('selected', event.get_time());
+	let button = event.get_button();
+	if (button == 1) {
+        	this.emit('selected', event.get_time());
+	} else if(button == 3) {
+	    if (!this._menu) {
+                this._menu = new AppWindowMenu(this);
+            }
+            this._menu.popup(event.get_coords());
+	}
     },
 
     _onDragBegin : function (draggable, time) {
@@ -1258,7 +1276,10 @@ Workspace.prototype = {
                       Lang.bind(this, function() {
                           icon.show();
                       }));
-
+        clone.connect('clone-source-changed',
+                      Lang.bind(this, function() {
+                          this.positionWindows(false);
+                      }));
         this.actor.add_actor(clone.actor);
 
         this._windows.push(clone);
@@ -1728,3 +1749,195 @@ function _workspaceRelativeGet(begin, end, time, params) {
     // Return the workspace coordinates.
     return (screen - curOverviewPos) / curOverviewScale - params.workspacePos;
 }
+
+
+const APPWINDOW_DEFAULT_BORDER_COLOR = new Clutter.Color();
+APPWINDOW_DEFAULT_BORDER_COLOR.from_pixel(0x787878ff);
+const APPWINDOW_MENU_BACKGROUND_COLOR = new Clutter.Color();
+APPWINDOW_MENU_BACKGROUND_COLOR.from_pixel(0x292929ff);
+const APPWINDOW_MENU_FONT = 'Sans 14px';
+const APPWINDOW_MENU_COLOR = new Clutter.Color();
+APPWINDOW_MENU_COLOR.from_pixel(0xffffffff);
+const APPWINDOW_MENU_SELECTED_COLOR = new Clutter.Color();
+APPWINDOW_MENU_SELECTED_COLOR.from_pixel(0x005b97ff);
+const APPWINDOW_MENU_SEPARATOR_COLOR = new Clutter.Color();
+APPWINDOW_MENU_SEPARATOR_COLOR.from_pixel(0x787878ff);
+const APPWINDOW_MENU_BORDER_WIDTH = 1;
+const APPWINDOW_MENU_ARROW_SIZE = 12;
+const APPWINDOW_MENU_CORNER_RADIUS = 4;
+const APPWINDOW_MENU_PADDING = 4;
+
+const TRANSPARENT_COLOR = new Clutter.Color();
+TRANSPARENT_COLOR.from_pixel(0x00000000);
+
+const MenuType = { NONE: 0, ON_RIGHT: 1, BELOW: 2 };
+const MenuAction = { NONE: 0, CLOSE: 1, MAXIMIZE: 2, MINIMIZE: 3, UNMAXIMIZE: 4, UNMINIMIZE: 5, SHADE: 6, UNSHADE: 7 };
+
+
+function AppWindowMenu(source) {
+    this._init(source);
+}
+
+AppWindowMenu.prototype = {
+    _init: function(source) {
+        this._source = source;
+
+        this.actor = new Shell.GenericContainer({ reactive: true });
+        this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
+        this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
+        this.actor.connect('allocate', Lang.bind(this, this._allocate));
+
+        this._windowContainer = new Shell.Menu({ orientation: Big.BoxOrientation.VERTICAL,
+                                                 border_color: APPWINDOW_DEFAULT_BORDER_COLOR,
+                                                 border: APPWINDOW_MENU_BORDER_WIDTH,
+                                                 background_color: APPWINDOW_MENU_BACKGROUND_COLOR,
+                                                 padding: 4,
+                                                 corner_radius: APPWINDOW_MENU_CORNER_RADIUS,
+                                                 width: Main.overview._dash.actor.width * 0.75 });
+        this._windowContainer.connect('unselected', Lang.bind(this, this._onItemUnselected));
+        this._windowContainer.connect('selected', Lang.bind(this, this._onItemSelected));
+        this._windowContainer.connect('cancelled', Lang.bind(this, this._onWindowSelectionCancelled));
+        this._windowContainer.connect('activate', Lang.bind(this, this._onItemActivate));
+        this.actor.add_actor(this._windowContainer);
+
+        // Stay popped up on release over application icon
+        this._windowContainer.set_persistent_source(this._source.actor);
+        this._windowContainer.connect('button-release-event', Lang.bind(this, this._onMenuButtonRelease));
+
+        // Chain our visibility and lifecycle to that of the source
+        source.actor.connect('notify::mapped', Lang.bind(this, function () {
+            if (!source.actor.mapped)
+                this._windowContainer.popdown();
+        }));
+        source.actor.connect('destroy', Lang.bind(this, function () { this.actor.destroy(); }));
+
+        global.stage.add_actor(this.actor);
+    },
+
+    _getPreferredWidth: function(actor, forHeight, alloc) {
+        let [min, natural] = this._windowContainer.get_preferred_width(forHeight);
+        alloc.min_size = min;
+        alloc.natural_size = natural;
+    },
+
+    _getPreferredHeight: function(actor, forWidth, alloc) {
+        let [min, natural] = this._windowContainer.get_preferred_height(forWidth);
+        alloc.min_size = min;
+        alloc.natural_size = natural;
+    },
+
+    _allocate: function(actor, box, flags) {
+        let childBox = new Clutter.ActorBox();
+        let width = box.x2 - box.x1;
+	let height = box.y2 - box.y1;
+	childBox.x1 = APPWINDOW_MENU_BORDER_WIDTH;
+	childBox.x2 = width;
+	childBox.y1 = 0;
+	childBox.y2 = height;
+	this._windowContainer.allocate(childBox, flags);
+    },
+
+    _redisplay: function() {
+        this._windowContainer.remove_all();
+        this._windowContainer.show();
+	
+        this._appendMenuItem(_("New Window"))._actionType = MenuAction.NONE; 
+        this._appendMenuItem(_("Save"))._actionType = MenuAction.NONE; 
+        this._appendSeparator();
+        this._appendMenuItem(_("Maximize"))._actionType = MenuAction.MAXIMIZE;
+        this._appendMenuItem(_("Unmaximize"))._actionType = MenuAction.UNMAXIMIZE;
+        this._appendMenuItem(_("Minimize"))._actionType = MenuAction.MINIMIZE;
+        this._appendMenuItem(_("Unminimize"))._actionType = MenuAction.UNMINIMIZE;
+        this._appendMenuItem(_("Close"))._actionType = MenuAction.CLOSE;
+        this._highlightedItem = null;
+    },
+
+    _appendSeparator: function () {
+        let box = new Big.Box({ padding_top: 2, padding_bottom: 2 });
+        box.append(new Clutter.Rectangle({ height: 1,
+                                           color: APPWINDOW_MENU_SEPARATOR_COLOR }),
+                   Big.BoxPackFlags.EXPAND);
+        this._windowContainer.append_separator(box, Big.BoxPackFlags.NONE);
+    },
+
+    _appendMenuItem: function(labelText) {
+        let box = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
+                                padding_top: 4,
+                                padding_bottom: 4,
+                                spacing: 4,
+                                reactive: true });
+        let label = new Clutter.Text({ text: labelText,
+                                       font_name: APPWINDOW_MENU_FONT,
+                                       ellipsize: Pango.EllipsizeMode.END,
+                                       color: APPWINDOW_MENU_COLOR });
+        box.append(label, Big.BoxPackFlags.NONE);
+        this._windowContainer.append(box, Big.BoxPackFlags.NONE);
+        return box;
+    },
+
+    popup: function(coords) {
+        this._redisplay();
+        this._windowContainer.popup(1, Main.currentTime());
+        this.actor.set_position(coords[0], coords[1]);
+        this.actor.show();
+    },
+
+    popdown: function() {
+        this._windowContainer.popdown();
+        this.actor.hide();
+    },
+
+    _onMenuButtonRelease: function (actor, event) {
+	if (this._highlightedItem == null) {
+		this.popdown();
+		return;
+	}
+	switch (this._highlightedItem._actionType) {
+		case MenuAction.MAXIMIZE:
+			this._source.metaWindow.maximize(3);
+			break;
+		case MenuAction.UNMAXIMIZE:
+			this._source.metaWindow.unmaximize(3);
+			break;
+		case MenuAction.MINIMIZE:
+			this._source.metaWindow.minimize();
+			break;
+		case MenuAction.UNMINIMIZE:
+			this._source.metaWindow.unminimize();
+			break;
+		case MenuAction.CLOSE:
+			this._source.metaWindow.delete(event.get_time());
+			break;
+	}
+	this.popdown();
+	this._source.updateClone();
+    },
+
+    _updateHighlight: function (item) {
+        if (this._highlightedItem) {
+            this._highlightedItem.background_color = TRANSPARENT_COLOR;
+        }
+        this._highlightedItem = item;
+        if (this._highlightedItem) {
+            this._highlightedItem.background_color = APPWINDOW_MENU_SELECTED_COLOR;
+        }
+    },
+
+    _onItemUnselected: function (actor, child) {
+        this._updateHighlight(null);
+    },
+
+    _onItemSelected: function (actor, child) {
+        this._updateHighlight(child);
+    },
+
+    _onItemActivate: function (actor, child) {
+        this.popdown();
+    },
+
+    _onWindowSelectionCancelled: function () {
+        this.popdown();
+    }
+};
+
+Signals.addSignalMethods(AppWindowMenu.prototype);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]