[lookingGlass] Remove Properties tab, replace with popup object inspector
Make inspecting objects more dynamic by turning them into links which pops up a dialog, rather than the more clunky tab interaction.
This commit is contained in:
parent
42e9b21b24
commit
016ad69550
@ -636,7 +636,7 @@ StTooltip {
|
|||||||
background-color: rgba(0,0,0,0.85);
|
background-color: rgba(0,0,0,0.85);
|
||||||
spacing: 4px;
|
spacing: 4px;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
border: 1px solid rgba(0,0,172,0.85);
|
border: 2px solid grey;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|
||||||
color: #88ff66;
|
color: #88ff66;
|
||||||
@ -656,21 +656,58 @@ StTooltip {
|
|||||||
padding: 2px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#LookingGlassDialog .notebook-tab:hover {
|
||||||
|
color: #00ff00;
|
||||||
|
}
|
||||||
|
|
||||||
#LookingGlassDialog .notebook-tab:selected {
|
#LookingGlassDialog .notebook-tab:selected {
|
||||||
border: 1px solid #88ff66;
|
border: 1px solid #88ff66;
|
||||||
padding: 1px;
|
border-radius: 4px;
|
||||||
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#LookingGlassDialog StLabel
|
#LookingGlassDialog .lg-inspector-title {
|
||||||
|
font-weight: bold;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lg-dialog StLabel
|
||||||
{
|
{
|
||||||
color: #88ff66;
|
color: #88ff66;
|
||||||
}
|
}
|
||||||
|
|
||||||
#LookingGlassDialog StEntry
|
.lg-dialog StEntry
|
||||||
{
|
{
|
||||||
color: #88ff66;
|
color: #88ff66;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.lg-obj-inspector-title
|
||||||
|
{
|
||||||
|
spacing: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lg-obj-inspector-button
|
||||||
|
{
|
||||||
|
border: 1px solid #88ff66;
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lg-obj-inspector-button:hover
|
||||||
|
{
|
||||||
|
border: 1px solid #00ff00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lg-dialog .shell-link
|
||||||
|
{
|
||||||
|
color: #88ff66;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lg-dialog .shell-link:hover
|
||||||
|
{
|
||||||
|
color: #00ff00;
|
||||||
|
}
|
||||||
|
|
||||||
#LookingGlassDialog StBoxLayout#EvalBox
|
#LookingGlassDialog StBoxLayout#EvalBox
|
||||||
{
|
{
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
@ -705,6 +742,14 @@ StTooltip {
|
|||||||
spacing: 6px;
|
spacing: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#LookingGlassPropertyInspector {
|
||||||
|
background: rgba(0, 0, 0, 0.9);
|
||||||
|
border: 2px solid grey;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 6px;
|
||||||
|
color: #88ff66;
|
||||||
|
}
|
||||||
|
|
||||||
/* Calendar popup */
|
/* Calendar popup */
|
||||||
|
|
||||||
#calendarPopup {
|
#calendarPopup {
|
||||||
|
@ -12,6 +12,7 @@ function Link(props) {
|
|||||||
Link.prototype = {
|
Link.prototype = {
|
||||||
_init : function(props) {
|
_init : function(props) {
|
||||||
let realProps = { reactive: true,
|
let realProps = { reactive: true,
|
||||||
|
track_hover: true,
|
||||||
style_class: 'shell-link' };
|
style_class: 'shell-link' };
|
||||||
// The user can pass in reactive: false to override the above and get
|
// The user can pass in reactive: false to override the above and get
|
||||||
// a non-reactive link (a link to the current page, perhaps)
|
// a non-reactive link (a link to the current page, perhaps)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const Pango = imports.gi.Pango;
|
const Pango = imports.gi.Pango;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
@ -49,7 +50,9 @@ Notebook.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
appendPage: function(name, child) {
|
appendPage: function(name, child) {
|
||||||
let labelBox = new St.BoxLayout({ style_class: 'notebook-tab' });
|
let labelBox = new St.BoxLayout({ style_class: 'notebook-tab',
|
||||||
|
reactive: true,
|
||||||
|
track_hover: true });
|
||||||
let label = new St.Button({ label: name });
|
let label = new St.Button({ label: name });
|
||||||
label.connect('clicked', Lang.bind(this, function () {
|
label.connect('clicked', Lang.bind(this, function () {
|
||||||
this.selectChild(child);
|
this.selectChild(child);
|
||||||
@ -138,6 +141,40 @@ Notebook.prototype = {
|
|||||||
};
|
};
|
||||||
Signals.addSignalMethods(Notebook.prototype);
|
Signals.addSignalMethods(Notebook.prototype);
|
||||||
|
|
||||||
|
function objectToString(o) {
|
||||||
|
if (typeof(o) == typeof(objectToString)) {
|
||||||
|
// special case this since the default is way, way too verbose
|
||||||
|
return "<js function>";
|
||||||
|
} else {
|
||||||
|
return "" + o;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ObjLink(o, title) {
|
||||||
|
this._init(o, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjLink.prototype = {
|
||||||
|
__proto__: Link.Link,
|
||||||
|
|
||||||
|
_init: function(o, title) {
|
||||||
|
let text;
|
||||||
|
if (title)
|
||||||
|
text = title;
|
||||||
|
else
|
||||||
|
text = objectToString(o);
|
||||||
|
text = GLib.markup_escape_text(text, -1);
|
||||||
|
this._obj = o;
|
||||||
|
Link.Link.prototype._init.call(this, { label: text });
|
||||||
|
this.actor.get_child().single_line_mode = true;
|
||||||
|
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onClicked: function (link) {
|
||||||
|
Main.lookingGlass.inspectObject(this._obj, this.actor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function Result(command, o, index) {
|
function Result(command, o, index) {
|
||||||
this._init(command, o, index);
|
this._init(command, o, index);
|
||||||
}
|
}
|
||||||
@ -150,15 +187,16 @@ Result.prototype = {
|
|||||||
this.actor = new St.BoxLayout({ vertical: true });
|
this.actor = new St.BoxLayout({ vertical: true });
|
||||||
|
|
||||||
let cmdTxt = new St.Label({ text: command });
|
let cmdTxt = new St.Label({ text: command });
|
||||||
cmdTxt.ellipsize = Pango.EllipsizeMode.END;
|
cmdTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END;
|
||||||
|
|
||||||
this.actor.add(cmdTxt);
|
this.actor.add(cmdTxt);
|
||||||
let resultTxt = new St.Label({ text: 'r(' + index + ') = ' + o });
|
let box = new St.BoxLayout({});
|
||||||
resultTxt.ellipsize = Pango.EllipsizeMode.END;
|
this.actor.add(box);
|
||||||
|
let resultTxt = new St.Label({ text: 'r(' + index + ') = ' });
|
||||||
this.actor.add(resultTxt);
|
resultTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END;
|
||||||
let line = new Clutter.Rectangle({ name: 'Separator',
|
box.add(resultTxt);
|
||||||
height: 1 });
|
let objLink = new ObjLink(o);
|
||||||
|
box.add(objLink.actor);
|
||||||
|
let line = new Clutter.Rectangle({ name: 'Separator' });
|
||||||
let padBin = new St.Bin({ name: 'Separator', x_fill: true, y_fill: true });
|
let padBin = new St.Bin({ name: 'Separator', x_fill: true, y_fill: true });
|
||||||
padBin.add_actor(line);
|
padBin.add_actor(line);
|
||||||
this.actor.add(padBin);
|
this.actor.add(padBin);
|
||||||
@ -188,9 +226,8 @@ WindowList.prototype = {
|
|||||||
metaWindow.connect('unmanaged', Lang.bind(this, this._updateWindowList));
|
metaWindow.connect('unmanaged', Lang.bind(this, this._updateWindowList));
|
||||||
let box = new St.BoxLayout({ vertical: true });
|
let box = new St.BoxLayout({ vertical: true });
|
||||||
this.actor.add(box);
|
this.actor.add(box);
|
||||||
let label = new Link.Link({ label: metaWindow.title, x_align: St.Align.START });
|
let windowLink = new ObjLink(metaWindow, metaWindow.title);
|
||||||
label.actor.connect('clicked', Lang.bind(this, function () { this.emit('selected', metaWindow); }));
|
box.add(windowLink.actor, { x_align: St.Align.START, x_fill: false });
|
||||||
box.add(label.actor);
|
|
||||||
let propsBox = new St.BoxLayout({ vertical: true, style: 'padding-left: 6px;' });
|
let propsBox = new St.BoxLayout({ vertical: true, style: 'padding-left: 6px;' });
|
||||||
box.add(propsBox);
|
box.add(propsBox);
|
||||||
propsBox.add(new St.Label({ text: 'wmclass: ' + metaWindow.get_wm_class() }));
|
propsBox.add(new St.Label({ text: 'wmclass: ' + metaWindow.get_wm_class() }));
|
||||||
@ -199,8 +236,10 @@ WindowList.prototype = {
|
|||||||
let icon = app.create_icon_texture(22);
|
let icon = app.create_icon_texture(22);
|
||||||
let propBox = new St.BoxLayout({ style: 'spacing: 6px; ' });
|
let propBox = new St.BoxLayout({ style: 'spacing: 6px; ' });
|
||||||
propsBox.add(propBox);
|
propsBox.add(propBox);
|
||||||
propBox.add(new St.Label({ text: 'app: ' + app.get_id() }), { y_align: St.Align.MIDDLE });
|
propBox.add(new St.Label({ text: 'app: ' }), { y_fill: false });
|
||||||
propBox.add(icon, { y_align: St.Align.MIDDLE });
|
let appLink = new ObjLink(app, app.get_id());
|
||||||
|
propBox.add(appLink.actor, { y_fill: false });
|
||||||
|
propBox.add(icon, { y_fill: false });
|
||||||
} else {
|
} else {
|
||||||
propsBox.add(new St.Label({ text: '<untracked>' }));
|
propsBox.add(new St.Label({ text: '<untracked>' }));
|
||||||
}
|
}
|
||||||
@ -209,36 +248,109 @@ WindowList.prototype = {
|
|||||||
};
|
};
|
||||||
Signals.addSignalMethods(WindowList.prototype);
|
Signals.addSignalMethods(WindowList.prototype);
|
||||||
|
|
||||||
function PropertyInspector() {
|
function ObjInspector() {
|
||||||
this._init();
|
this._init();
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyInspector.prototype = {
|
ObjInspector.prototype = {
|
||||||
_init : function () {
|
_init : function () {
|
||||||
this._target = null;
|
this._obj = null;
|
||||||
|
this._previousObj = null;
|
||||||
|
|
||||||
this._parentList = [];
|
this._parentList = [];
|
||||||
|
|
||||||
this.actor = new St.BoxLayout({ name: 'PropertyInspector', vertical: true });
|
this.actor = new St.ScrollView({ x_fill: true, y_fill: true });
|
||||||
|
this.actor.get_hscroll_bar().hide();
|
||||||
|
this._container = new St.BoxLayout({ name: 'LookingGlassPropertyInspector',
|
||||||
|
style_class: 'lg-dialog',
|
||||||
|
vertical: true });
|
||||||
|
this.actor.add_actor(this._container);
|
||||||
},
|
},
|
||||||
|
|
||||||
setTarget: function(actor) {
|
selectObject: function(obj, skipPrevious) {
|
||||||
this.target = actor;
|
if (!skipPrevious)
|
||||||
|
this._previousObj = this._obj;
|
||||||
|
else
|
||||||
|
this._previousObj = null;
|
||||||
|
this._obj = obj;
|
||||||
|
|
||||||
this.actor.get_children().forEach(function (child) { child.destroy(); });
|
this._container.get_children().forEach(function (child) { child.destroy(); });
|
||||||
|
|
||||||
for (let propName in actor) {
|
let hbox = new St.BoxLayout({ style_class: 'lg-obj-inspector-title' });
|
||||||
let valueStr;
|
this._container.add_actor(hbox);
|
||||||
try {
|
let label = new St.Label({ text: 'Inspecting: %s: %s'.format(typeof(obj),
|
||||||
valueStr = '' + actor[propName];
|
objectToString(obj)) });
|
||||||
} catch (e) {
|
label.single_line_mode = true;
|
||||||
valueStr = '<error>';
|
hbox.add(label, { expand: true, y_fill: false });
|
||||||
}
|
let button = new St.Button({ label: 'Insert', style_class: 'lg-obj-inspector-button' });
|
||||||
let propText = propName + ': ' + valueStr;
|
button.connect('clicked', Lang.bind(this, this._onInsert));
|
||||||
let propDisplay = new St.Label({ reactive: true,
|
hbox.add(button);
|
||||||
text: propText });
|
|
||||||
this.actor.add_actor(propDisplay);
|
if (this._previousObj != null) {
|
||||||
|
button = new St.Button({ label: 'Back', style_class: 'lg-obj-inspector-button' });
|
||||||
|
button.connect('clicked', Lang.bind(this, this._onBack));
|
||||||
|
hbox.add(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button = new St.Button({ style_class: 'window-close' });
|
||||||
|
button.connect('clicked', Lang.bind(this, this.close));
|
||||||
|
hbox.add(button);
|
||||||
|
if (typeof(obj) == typeof({})) {
|
||||||
|
for (let propName in obj) {
|
||||||
|
let valueStr;
|
||||||
|
let link;
|
||||||
|
try {
|
||||||
|
let prop = obj[propName];
|
||||||
|
link = new ObjLink(prop).actor;
|
||||||
|
} catch (e) {
|
||||||
|
link = new St.Label({ text: '<error>' });
|
||||||
|
}
|
||||||
|
let hbox = new St.BoxLayout();
|
||||||
|
let propText = propName + ": " + valueStr;
|
||||||
|
hbox.add(new St.Label({ text: propName + ': ' }));
|
||||||
|
hbox.add(link);
|
||||||
|
this._container.add_actor(hbox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
open: function(sourceActor) {
|
||||||
|
if (this._open)
|
||||||
|
return;
|
||||||
|
this._previousObj = null;
|
||||||
|
this._open = true;
|
||||||
|
this.actor.show();
|
||||||
|
if (sourceActor) {
|
||||||
|
this.actor.set_scale(0, 0);
|
||||||
|
let [sourceX, sourceY] = sourceActor.get_transformed_position();
|
||||||
|
let [sourceWidth, sourceHeight] = sourceActor.get_transformed_size();
|
||||||
|
this.actor.move_anchor_point(Math.floor(sourceX + sourceWidth / 2),
|
||||||
|
Math.floor(sourceY + sourceHeight / 2));
|
||||||
|
Tweener.addTween(this.actor, { scale_x: 1, scale_y: 1,
|
||||||
|
transition: "easeOutQuad",
|
||||||
|
time: 0.2 });
|
||||||
|
} else {
|
||||||
|
this.actor.set_scale(1, 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
close: function() {
|
||||||
|
if (!this._open)
|
||||||
|
return;
|
||||||
|
this._open = false;
|
||||||
|
this.actor.hide();
|
||||||
|
this._previousObj = null;
|
||||||
|
this._obj = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onInsert: function() {
|
||||||
|
let obj = this._obj;
|
||||||
|
this.close();
|
||||||
|
Main.lookingGlass.insertObject(obj);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onBack: function() {
|
||||||
|
this.selectObject(this._previousObj, true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -462,6 +574,7 @@ LookingGlass.prototype = {
|
|||||||
this._maxItems = 150;
|
this._maxItems = 150;
|
||||||
|
|
||||||
this.actor = new St.BoxLayout({ name: 'LookingGlassDialog',
|
this.actor = new St.BoxLayout({ name: 'LookingGlassDialog',
|
||||||
|
style_class: 'lg-dialog',
|
||||||
vertical: true,
|
vertical: true,
|
||||||
visible: false });
|
visible: false });
|
||||||
|
|
||||||
@ -473,6 +586,10 @@ LookingGlass.prototype = {
|
|||||||
|
|
||||||
Main.uiGroup.add_actor(this.actor);
|
Main.uiGroup.add_actor(this.actor);
|
||||||
|
|
||||||
|
this._objInspector = new ObjInspector();
|
||||||
|
Main.uiGroup.add_actor(this._objInspector.actor);
|
||||||
|
this._objInspector.actor.hide();
|
||||||
|
|
||||||
let toolbar = new St.BoxLayout({ name: 'Toolbar' });
|
let toolbar = new St.BoxLayout({ name: 'Toolbar' });
|
||||||
this.actor.add_actor(toolbar);
|
this.actor.add_actor(toolbar);
|
||||||
let inspectIcon = St.TextureCache.get_default().load_gicon(new Gio.ThemedIcon({ name: 'gtk-color-picker' }),
|
let inspectIcon = St.TextureCache.get_default().load_gicon(new Gio.ThemedIcon({ name: 'gtk-color-picker' }),
|
||||||
@ -521,9 +638,6 @@ LookingGlass.prototype = {
|
|||||||
}));
|
}));
|
||||||
entryArea.add(this._entry, { expand: true });
|
entryArea.add(this._entry, { expand: true });
|
||||||
|
|
||||||
this._propInspector = new PropertyInspector();
|
|
||||||
notebook.appendPage('Properties', this._propInspector.actor);
|
|
||||||
|
|
||||||
this._windowList = new WindowList();
|
this._windowList = new WindowList();
|
||||||
this._windowList.connect('selected', Lang.bind(this, function(list, window) {
|
this._windowList.connect('selected', Lang.bind(this, function(list, window) {
|
||||||
notebook.selectIndex(0);
|
notebook.selectIndex(0);
|
||||||
@ -618,7 +732,6 @@ LookingGlass.prototype = {
|
|||||||
let result = new Result('>>> ' + command, obj, index);
|
let result = new Result('>>> ' + command, obj, index);
|
||||||
this._results.push(result);
|
this._results.push(result);
|
||||||
this._resultsArea.add(result.actor);
|
this._resultsArea.add(result.actor);
|
||||||
this._propInspector.setTarget(obj);
|
|
||||||
if (this._borderPaintTarget != null) {
|
if (this._borderPaintTarget != null) {
|
||||||
this._borderPaintTarget.disconnect(this._borderPaintId);
|
this._borderPaintTarget.disconnect(this._borderPaintId);
|
||||||
this._borderPaintTarget = null;
|
this._borderPaintTarget = null;
|
||||||
@ -686,6 +799,9 @@ LookingGlass.prototype = {
|
|||||||
this.actor.y = this._hiddenY;
|
this.actor.y = this._hiddenY;
|
||||||
this.actor.width = myWidth;
|
this.actor.width = myWidth;
|
||||||
this.actor.height = myHeight;
|
this.actor.height = myHeight;
|
||||||
|
this._objInspector.actor.set_size(Math.floor(myWidth * 0.8), Math.floor(myHeight * 0.8));
|
||||||
|
this._objInspector.actor.set_position(this.actor.x + Math.floor(myWidth * 0.1),
|
||||||
|
this._targetY + Math.floor(myHeight * 0.1));
|
||||||
},
|
},
|
||||||
|
|
||||||
slaveTo: function(actor) {
|
slaveTo: function(actor) {
|
||||||
@ -696,11 +812,24 @@ LookingGlass.prototype = {
|
|||||||
this._resizeTo(actor);
|
this._resizeTo(actor);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
insertObject: function(obj) {
|
||||||
|
this._pushResult('<insert>', obj);
|
||||||
|
},
|
||||||
|
|
||||||
|
inspectObject: function(obj, sourceActor) {
|
||||||
|
this._objInspector.open(sourceActor);
|
||||||
|
this._objInspector.selectObject(obj);
|
||||||
|
},
|
||||||
|
|
||||||
// Handle key events which are relevant for all tabs of the LookingGlass
|
// Handle key events which are relevant for all tabs of the LookingGlass
|
||||||
_globalKeyPressEvent : function(actor, event) {
|
_globalKeyPressEvent : function(actor, event) {
|
||||||
let symbol = event.get_key_symbol();
|
let symbol = event.get_key_symbol();
|
||||||
if (symbol == Clutter.Escape) {
|
if (symbol == Clutter.Escape) {
|
||||||
this.close();
|
if (this._objInspector.actor.visible) {
|
||||||
|
this._objInspector.close();
|
||||||
|
} else {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -737,6 +866,8 @@ LookingGlass.prototype = {
|
|||||||
if (this._keyPressEventId)
|
if (this._keyPressEventId)
|
||||||
global.stage.disconnect(this._keyPressEventId);
|
global.stage.disconnect(this._keyPressEventId);
|
||||||
|
|
||||||
|
this._objInspector.actor.hide();
|
||||||
|
|
||||||
this._historyNavIndex = -1;
|
this._historyNavIndex = -1;
|
||||||
this._open = false;
|
this._open = false;
|
||||||
Tweener.removeTweens(this.actor);
|
Tweener.removeTweens(this.actor);
|
||||||
|
Loading…
Reference in New Issue
Block a user