workspace: Let WindowClone inherit from StWidget

Using inheritance over delegation will give us more control over
the actor drawing.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/774
This commit is contained in:
Florian Müllner 2018-11-14 19:26:33 +01:00 committed by Florian Müllner
parent dbb71f0dfc
commit 51938c398a

View File

@ -94,15 +94,25 @@ class WindowCloneLayout extends Clutter.LayoutManager {
} }
}); });
var WindowClone = class { var WindowClone = GObject.registerClass({
constructor(realWindow, workspace) { Signals: {
'drag-begin': {},
'drag-cancelled': {},
'drag-end': {},
'hide-chrome': {},
'selected': { param_types: [GObject.TYPE_UINT] },
'show-chrome': {},
'size-changed': {}
},
}, class WindowClone extends St.Widget {
_init(realWindow, workspace) {
this.realWindow = realWindow; this.realWindow = realWindow;
this.metaWindow = realWindow.meta_window; this.metaWindow = realWindow.meta_window;
this.metaWindow._delegate = this; this.metaWindow._delegate = this;
this._workspace = workspace; this._workspace = workspace;
this._windowClone = new Clutter.Clone({ source: realWindow }); this._windowClone = new Clutter.Clone({ source: realWindow });
// We expect this.actor to be used for all interaction rather than // We expect this to be used for all interaction rather than
// this._windowClone; as the former is reactive and the latter // this._windowClone; as the former is reactive and the latter
// is not, this just works for most cases. However, for DND all // is not, this just works for most cases. However, for DND all
// actors are picked, so DND operations would operate on the clone. // actors are picked, so DND operations would operate on the clone.
@ -113,14 +123,16 @@ var WindowClone = class {
// the invisible border; this is inconvenient; rather than trying // the invisible border; this is inconvenient; rather than trying
// to compensate all over the place we insert a ClutterActor into // to compensate all over the place we insert a ClutterActor into
// the hierarchy that is sized to only the visible portion. // the hierarchy that is sized to only the visible portion.
this.actor = new St.Widget({ reactive: true, super._init({
reactive: true,
can_focus: true, can_focus: true,
accessible_role: Atk.Role.PUSH_BUTTON, accessible_role: Atk.Role.PUSH_BUTTON,
layout_manager: new WindowCloneLayout() }); layout_manager: new WindowCloneLayout()
});
this.actor.add_child(this._windowClone); this.add_child(this._windowClone);
this.actor._delegate = this; this._delegate = this;
this.slotId = 0; this.slotId = 0;
this._slot = [0, 0, 0, 0]; this._slot = [0, 0, 0, 0];
@ -142,23 +154,23 @@ var WindowClone = class {
this._updateAttachedDialogs(); this._updateAttachedDialogs();
this._computeBoundingBox(); this._computeBoundingBox();
this.actor.x = this._boundingBox.x; this.x = this._boundingBox.x;
this.actor.y = this._boundingBox.y; this.y = this._boundingBox.y;
let clickAction = new Clutter.ClickAction(); let clickAction = new Clutter.ClickAction();
clickAction.connect('clicked', this._onClicked.bind(this)); clickAction.connect('clicked', this._onClicked.bind(this));
clickAction.connect('long-press', this._onLongPress.bind(this)); clickAction.connect('long-press', this._onLongPress.bind(this));
this.actor.add_action(clickAction); this.add_action(clickAction);
this.actor.connect('destroy', this._onDestroy.bind(this)); this.connect('destroy', this._onDestroy.bind(this));
this.actor.connect('key-press-event', this._onKeyPress.bind(this)); this.connect('key-press-event', this._onKeyPress.bind(this));
this.actor.connect('enter-event', () => this.emit('show-chrome')); this.connect('enter-event', () => this.emit('show-chrome'));
this.actor.connect('key-focus-in', () => this.emit('show-chrome')); this.connect('key-focus-in', () => this.emit('show-chrome'));
this.actor.connect('leave-event', () => this.emit('hide-chrome')); this.connect('leave-event', () => this.emit('hide-chrome'));
this.actor.connect('key-focus-out', () => this.emit('hide-chrome')); this.connect('key-focus-out', () => this.emit('hide-chrome'));
this._draggable = DND.makeDraggable(this.actor, this._draggable = DND.makeDraggable(this,
{ restoreOnSuccess: true, { restoreOnSuccess: true,
manualMode: true, manualMode: true,
dragActorMaxSize: WINDOW_DND_SIZE, dragActorMaxSize: WINDOW_DND_SIZE,
@ -185,7 +197,7 @@ var WindowClone = class {
deleteAll() { deleteAll() {
// Delete all windows, starting from the bottom-most (most-modal) one // Delete all windows, starting from the bottom-most (most-modal) one
let windows = this.actor.get_children(); let windows = this.get_children();
for (let i = windows.length - 1; i >= 1; i--) { for (let i = windows.length - 1; i >= 1; i--) {
let realWindow = windows[i].source; let realWindow = windows[i].source;
let metaWindow = realWindow.meta_window; let metaWindow = realWindow.meta_window;
@ -215,7 +227,7 @@ var WindowClone = class {
} }
hasAttachedDialogs() { hasAttachedDialogs() {
return this.actor.get_n_children() > 1; return this.get_n_children() > 1;
} }
_doAddAttachedDialog(metaWin, realWin) { _doAddAttachedDialog(metaWin, realWin) {
@ -229,7 +241,7 @@ var WindowClone = class {
this._onMetaWindowSizeChanged(); this._onMetaWindowSizeChanged();
}); });
this.actor.add_child(clone); this.add_child(clone);
} }
_updateAttachedDialogs() { _updateAttachedDialogs() {
@ -267,7 +279,7 @@ var WindowClone = class {
_computeBoundingBox() { _computeBoundingBox() {
let rect = this.metaWindow.get_frame_rect(); let rect = this.metaWindow.get_frame_rect();
this.actor.get_children().forEach(child => { this.get_children().forEach(child => {
let realWindow; let realWindow;
if (child == this._windowClone) if (child == this._windowClone)
realWindow = this.realWindow; realWindow = this.realWindow;
@ -280,7 +292,7 @@ var WindowClone = class {
// Convert from a MetaRectangle to a native JS object // Convert from a MetaRectangle to a native JS object
this._boundingBox = { x: rect.x, y: rect.y, width: rect.width, height: rect.height }; this._boundingBox = { x: rect.x, y: rect.y, width: rect.width, height: rect.height };
this.actor.layout_manager.boundingBox = rect; this.layout_manager.boundingBox = rect;
} }
// Find the actor just below us, respecting reparenting done by DND code // Find the actor just below us, respecting reparenting done by DND code
@ -306,17 +318,13 @@ var WindowClone = class {
let actualAbove = this.getActualStackAbove(); let actualAbove = this.getActualStackAbove();
if (actualAbove == null) if (actualAbove == null)
this.actor.lower_bottom(); this.lower_bottom();
else else
this.actor.raise(actualAbove); this.raise(actualAbove);
}
destroy() {
this.actor.destroy();
} }
_disconnectSignals() { _disconnectSignals() {
this.actor.get_children().forEach(child => { this.get_children().forEach(child => {
let realWindow; let realWindow;
if (child == this._windowClone) if (child == this._windowClone)
realWindow = this.realWindow; realWindow = this.realWindow;
@ -338,14 +346,12 @@ var WindowClone = class {
this._disconnectSignals(); this._disconnectSignals();
this.metaWindow._delegate = null; this.metaWindow._delegate = null;
this.actor._delegate = null; this._delegate = null;
if (this.inDrag) { if (this.inDrag) {
this.emit('drag-end'); this.emit('drag-end');
this.inDrag = false; this.inDrag = false;
} }
this.disconnectAll();
} }
_activate() { _activate() {
@ -393,8 +399,8 @@ var WindowClone = class {
_onDragBegin(draggable, time) { _onDragBegin(draggable, time) {
this._dragSlot = this._slot; this._dragSlot = this._slot;
[this.dragOrigX, this.dragOrigY] = this.actor.get_position(); [this.dragOrigX, this.dragOrigY] = this.get_position();
this.dragOrigScale = this.actor.scale_x; this.dragOrigScale = this.scale_x;
this.inDrag = true; this.inDrag = true;
this.emit('drag-begin'); this.emit('drag-begin');
} }
@ -417,18 +423,17 @@ var WindowClone = class {
// We may not have a parent if DnD completed successfully, in // We may not have a parent if DnD completed successfully, in
// which case our clone will shortly be destroyed and replaced // which case our clone will shortly be destroyed and replaced
// with a new one on the target workspace. // with a new one on the target workspace.
if (this.actor.get_parent() != null) { if (this.get_parent() != null) {
if (this._stackAbove == null) if (this._stackAbove == null)
this.actor.lower_bottom(); this.lower_bottom();
else else
this.actor.raise(this._stackAbove); this.raise(this._stackAbove);
} }
this.emit('drag-end'); this.emit('drag-end');
} }
}; });
Signals.addSignalMethods(WindowClone.prototype);
/** /**
@ -452,7 +457,7 @@ var WindowOverlay = class {
this.title = new St.Label({ style_class: 'window-caption', this.title = new St.Label({ style_class: 'window-caption',
text: this._getCaption() }); text: this._getCaption() });
this.title.clutter_text.ellipsize = Pango.EllipsizeMode.END; this.title.clutter_text.ellipsize = Pango.EllipsizeMode.END;
windowClone.actor.label_actor = this.title; windowClone.label_actor = this.title;
this._maxTitleWidth = -1; this._maxTitleWidth = -1;
@ -467,7 +472,7 @@ var WindowOverlay = class {
this.closeButton.connect('clicked', () => this._windowClone.deleteAll()); this.closeButton.connect('clicked', () => this._windowClone.deleteAll());
windowClone.actor.connect('destroy', this._onDestroy.bind(this)); windowClone.connect('destroy', this._onDestroy.bind(this));
windowClone.connect('show-chrome', this._onShowChrome.bind(this)); windowClone.connect('show-chrome', this._onShowChrome.bind(this));
windowClone.connect('hide-chrome', this._onHideChrome.bind(this)); windowClone.connect('hide-chrome', this._onHideChrome.bind(this));
@ -503,7 +508,7 @@ var WindowOverlay = class {
show() { show() {
this._hidden = false; this._hidden = false;
if (this._windowClone.actor['has-pointer']) if (this._windowClone['has-pointer'])
this._animateVisible(); this._animateVisible();
} }
@ -675,7 +680,7 @@ var WindowOverlay = class {
_idleHideOverlay() { _idleHideOverlay() {
this._idleHideOverlayId = 0; this._idleHideOverlayId = 0;
if (!this._windowClone.actor['has-pointer'] && if (!this._windowClone['has-pointer'] &&
!this.closeButton['has-pointer']) !this.closeButton['has-pointer'])
this._animateInvisible(); this._animateInvisible();
@ -1296,8 +1301,8 @@ var Workspace = class {
if (clone.inDrag) if (clone.inDrag)
continue; continue;
let cloneWidth = clone.actor.width * scale; let cloneWidth = clone.width * scale;
let cloneHeight = clone.actor.height * scale; let cloneHeight = clone.height * scale;
clone.slot = [x, y, cloneWidth, cloneHeight]; clone.slot = [x, y, cloneWidth, cloneHeight];
let cloneCenter = x + cloneWidth / 2; let cloneCenter = x + cloneWidth / 2;
@ -1312,10 +1317,10 @@ var Workspace = class {
if (!clone.positioned) { if (!clone.positioned) {
// This window appeared after the overview was already up // This window appeared after the overview was already up
// Grow the clone from the center of the slot // Grow the clone from the center of the slot
clone.actor.x = x + cloneWidth / 2; clone.x = x + cloneWidth / 2;
clone.actor.y = y + cloneHeight / 2; clone.y = y + cloneHeight / 2;
clone.actor.scale_x = 0; clone.scale_x = 0;
clone.actor.scale_y = 0; clone.scale_y = 0;
clone.positioned = true; clone.positioned = true;
} }
@ -1325,14 +1330,14 @@ var Workspace = class {
* therefore we need to resize them now so they * therefore we need to resize them now so they
* can be scaled up later */ * can be scaled up later */
if (initialPositioning) { if (initialPositioning) {
clone.actor.opacity = 0; clone.opacity = 0;
clone.actor.scale_x = 0; clone.scale_x = 0;
clone.actor.scale_y = 0; clone.scale_y = 0;
clone.actor.x = x; clone.x = x;
clone.actor.y = y; clone.y = y;
} }
Tweener.addTween(clone.actor, Tweener.addTween(clone,
{ opacity: 255, { opacity: 255,
time: Overview.ANIMATION_TIME, time: Overview.ANIMATION_TIME,
transition: 'easeInQuad' transition: 'easeInQuad'
@ -1342,10 +1347,10 @@ var Workspace = class {
this._animateClone(clone, clone.overlay, x, y, scale); this._animateClone(clone, clone.overlay, x, y, scale);
} else { } else {
// cancel any active tweens (otherwise they might override our changes) // cancel any active tweens (otherwise they might override our changes)
Tweener.removeTweens(clone.actor); Tweener.removeTweens(clone);
clone.actor.set_position(x, y); clone.set_position(x, y);
clone.actor.set_scale(scale, scale); clone.set_scale(scale, scale);
clone.actor.set_opacity(255); clone.set_opacity(255);
clone.overlay.relayout(false); clone.overlay.relayout(false);
this._showWindowOverlay(clone, clone.overlay); this._showWindowOverlay(clone, clone.overlay);
} }
@ -1366,13 +1371,13 @@ var Workspace = class {
clone.setStackAbove(this._dropRect); clone.setStackAbove(this._dropRect);
} else { } else {
let previousClone = clones[i - 1]; let previousClone = clones[i - 1];
clone.setStackAbove(previousClone.actor); clone.setStackAbove(previousClone);
} }
} }
} }
_animateClone(clone, overlay, x, y, scale) { _animateClone(clone, overlay, x, y, scale) {
Tweener.addTween(clone.actor, Tweener.addTween(clone,
{ x: x, { x: x,
y: y, y: y,
scale_x: scale, scale_x: scale,
@ -1411,7 +1416,7 @@ var Workspace = class {
let actorUnderPointer = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y); let actorUnderPointer = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
for (let i = 0; i < this._windows.length; i++) { for (let i = 0; i < this._windows.length; i++) {
if (this._windows[i].actor == actorUnderPointer) if (this._windows[i] == actorUnderPointer)
return GLib.SOURCE_CONTINUE; return GLib.SOURCE_CONTINUE;
} }
@ -1433,12 +1438,12 @@ var Workspace = class {
// destroyed; we'd like to animate this, but it's too late at // destroyed; we'd like to animate this, but it's too late at
// this point.) // this point.)
if (win) { if (win) {
let [stageX, stageY] = clone.actor.get_transformed_position(); let [stageX, stageY] = clone.get_transformed_position();
let [stageWidth, stageHeight] = clone.actor.get_transformed_size(); let [stageWidth, stageHeight] = clone.get_transformed_size();
win._overviewHint = { win._overviewHint = {
x: stageX, x: stageX,
y: stageY, y: stageY,
scale: stageWidth / clone.actor.width scale: stageWidth / clone.width
}; };
} }
clone.destroy(); clone.destroy();
@ -1518,11 +1523,11 @@ var Workspace = class {
let scale = win._overviewHint.scale; let scale = win._overviewHint.scale;
delete win._overviewHint; delete win._overviewHint;
clone.slot = [x, y, clone.actor.width * scale, clone.actor.height * scale]; clone.slot = [x, y, clone.width * scale, clone.height * scale];
clone.positioned = true; clone.positioned = true;
clone.actor.set_position(x, y); clone.set_position(x, y);
clone.actor.set_scale(scale, scale); clone.set_scale(scale, scale);
clone.overlay.relayout(false); clone.overlay.relayout(false);
} }
@ -1596,7 +1601,7 @@ var Workspace = class {
let overlay = this._windowOverlays[i]; let overlay = this._windowOverlays[i];
if (overlay) if (overlay)
overlay.hide(); overlay.hide();
this._windows[i].actor.opacity = 0; this._windows[i].opacity = 0;
} else { } else {
let fromTop = topIndex - i; let fromTop = topIndex - i;
let time; let time;
@ -1605,7 +1610,7 @@ var Workspace = class {
else else
time = windowBaseTime; time = windowBaseTime;
this._windows[i].actor.opacity = 255; this._windows[i].opacity = 255;
this._fadeWindow(i, time, 0); this._fadeWindow(i, time, 0);
} }
} }
@ -1619,7 +1624,7 @@ var Workspace = class {
for (let i = 0; i < this._windows.length; i++) { for (let i = 0; i < this._windows.length; i++) {
let clone = this._windows[i]; let clone = this._windows[i];
Tweener.removeTweens(clone.actor); Tweener.removeTweens(clone);
} }
if (this._repositionWindowsId > 0) { if (this._repositionWindowsId > 0) {
@ -1654,7 +1659,7 @@ var Workspace = class {
let overlay = this._windowOverlays[i]; let overlay = this._windowOverlays[i];
if (overlay) if (overlay)
overlay.hide(); overlay.hide();
this._windows[i].actor.opacity = 0; this._windows[i].opacity = 0;
} else { } else {
let fromTop = topIndex - i; let fromTop = topIndex - i;
let time; let time;
@ -1663,7 +1668,7 @@ var Workspace = class {
else else
time = windowBaseTime * nTimeSlots; time = windowBaseTime * nTimeSlots;
this._windows[i].actor.opacity = 0; this._windows[i].opacity = 0;
this._fadeWindow(i, time, 255); this._fadeWindow(i, time, 255);
} }
} }
@ -1678,18 +1683,18 @@ var Workspace = class {
if (clone.metaWindow.showing_on_its_workspace()) { if (clone.metaWindow.showing_on_its_workspace()) {
let [origX, origY] = clone.getOriginalPosition(); let [origX, origY] = clone.getOriginalPosition();
clone.actor.scale_x = 1; clone.scale_x = 1;
clone.actor.scale_y = 1; clone.scale_y = 1;
clone.actor.x = origX; clone.x = origX;
clone.actor.y = origY; clone.y = origY;
Tweener.addTween(clone.actor, Tweener.addTween(clone,
{ time: time, { time: time,
opacity: opacity, opacity: opacity,
transition: 'easeOutQuad' transition: 'easeOutQuad'
}); });
} else { } else {
// The window is hidden // The window is hidden
clone.actor.opacity = 0; clone.opacity = 0;
} }
} }
@ -1706,7 +1711,7 @@ var Workspace = class {
for (let i = 0; i < this._windows.length; i++) { for (let i = 0; i < this._windows.length; i++) {
let clone = this._windows[i]; let clone = this._windows[i];
Tweener.removeTweens(clone.actor); Tweener.removeTweens(clone);
} }
if (this._repositionWindowsId > 0) { if (this._repositionWindowsId > 0) {
@ -1732,7 +1737,7 @@ var Workspace = class {
if (clone.metaWindow.showing_on_its_workspace()) { if (clone.metaWindow.showing_on_its_workspace()) {
let [origX, origY] = clone.getOriginalPosition(); let [origX, origY] = clone.getOriginalPosition();
Tweener.addTween(clone.actor, Tweener.addTween(clone,
{ x: origX, { x: origX,
y: origY, y: origY,
scale_x: 1.0, scale_x: 1.0,
@ -1743,7 +1748,7 @@ var Workspace = class {
}); });
} else { } else {
// The window is hidden, make it shrink and fade it out // The window is hidden, make it shrink and fade it out
Tweener.addTween(clone.actor, Tweener.addTween(clone,
{ scale_x: 0, { scale_x: 0,
scale_y: 0, scale_y: 0,
opacity: 0, opacity: 0,
@ -1834,16 +1839,16 @@ var Workspace = class {
clone.connect('size-changed', () => { clone.connect('size-changed', () => {
this._recalculateWindowPositions(WindowPositionFlags.NONE); this._recalculateWindowPositions(WindowPositionFlags.NONE);
}); });
clone.actor.connect('destroy', () => { clone.connect('destroy', () => {
this._removeWindowClone(clone.metaWindow); this._removeWindowClone(clone.metaWindow);
}); });
this.actor.add_actor(clone.actor); this.actor.add_actor(clone);
overlay.connect('chrome-visible', () => { overlay.connect('chrome-visible', () => {
let focus = global.stage.key_focus; let focus = global.stage.key_focus;
if (focus == null || this.actor.contains(focus)) if (focus == null || this.actor.contains(focus))
clone.actor.grab_key_focus(); clone.grab_key_focus();
this._windowOverlays.forEach(o => { this._windowOverlays.forEach(o => {
if (o != overlay) if (o != overlay)
@ -1854,7 +1859,7 @@ var Workspace = class {
if (this._windows.length == 0) if (this._windows.length == 0)
clone.setStackAbove(null); clone.setStackAbove(null);
else else
clone.setStackAbove(this._windows[this._windows.length - 1].actor); clone.setStackAbove(this._windows[this._windows.length - 1]);
this._windows.push(clone); this._windows.push(clone);
this._windowOverlays.push(overlay); this._windowOverlays.push(overlay);