Skip to content

Commit

Permalink
feat: Window movements are now animated
Browse files Browse the repository at this point in the history
To present a smooth first class experience
  • Loading branch information
mmstick committed Mar 31, 2020
1 parent e465115 commit 3ede7c7
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 57 deletions.
38 changes: 24 additions & 14 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ export class Ext extends Ecs.World {

// State

/** Animate window movements */
animate_windows: boolean = true;

/** Column sizes in snap-to-grid */
column_size: number = 128;
Expand Down Expand Up @@ -205,7 +207,6 @@ export class Ext extends Ecs.World {
} else {
this.signals.set(object, [signal]);
}

}

connect_meta(win: Window.ShellWindow, signal: string, callback: () => void): number {
Expand Down Expand Up @@ -561,24 +562,30 @@ export class Ext extends Ecs.World {
/** Handle window creation events */
on_window_create(window: Meta.Window) {
GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
let win = this.get_window(window);
let actor = window.get_compositor_private();
if (win && actor) {
const entity = win.entity;
actor.connect('destroy', () => {
if (win) this.on_destroy(entity);
return false;
});

if (win.is_tilable(this)) {
this.connect_window(win);
}
if (actor) {
this.on_window_create_inner(window, actor);
}

return false;
});
}

on_window_create_inner(window: Meta.Window, actor: Clutter.Actor) {
let win = this.get_window(window);
if (win) {
const entity = win.entity;
actor.connect('destroy', () => {
if (win) this.on_destroy(entity);
return false;
});

if (win.is_tilable(this)) {
this.connect_window(win);
}
}
}

/** Handle workspace change events */
on_workspace_changed(win: Window.ShellWindow) {
Log.debug(`workspace changed for ${win.name(this)}`);
Expand Down Expand Up @@ -948,6 +955,9 @@ export class Ext extends Ecs.World {

// If not found, create a new entity with a ShellWindow component.
if (!entity) {
const actor = meta.get_compositor_private();
if (!actor) return null;

let window_app: any, name: string;

try {
Expand All @@ -967,8 +977,8 @@ export class Ext extends Ecs.World {
this.monitors.insert(entity, [win.meta.get_monitor(), win.workspace_id()]);

Log.debug(`created window (${win.entity}): ${win.name(this)}: ${id}`);
const actor = meta.get_compositor_private();
if (this.auto_tiler && win.is_tilable(this) && actor) {

if (this.auto_tiler && win.is_tilable(this)) {
let id = actor.connect('first-frame', () => {
this.auto_tiler?.auto_tile(this, win, this.init);
actor.disconnect(id);
Expand Down
21 changes: 11 additions & 10 deletions src/forest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export class Forest extends Ecs.World {
? ext.workspace_by_id(workspace)
: null;

const new_positions = new Array();
// const new_positions = new Array();
for (const [entity, r] of this.requested) {
const window = ext.windows.get(entity);
if (!window) continue;
Expand All @@ -95,12 +95,11 @@ export class Forest extends Ecs.World {
const signals = ext.size_signals.get(window.entity);
if (signals) {
Log.debug(`Moving Window(${entity}) from [${backup.fmt()}] to [${r.rect.fmt()}]`);
move_window(window, r.rect, signals);

const actual = window.rect();
Log.debug(`Moved Window(${entity}) to ${actual.fmt()}`);

new_positions.push([window, backup, actual]);
move_window(ext, window, r.rect, signals, () => {
const actual = window.rect();
Log.debug(`Moved Window(${entity}) to ${actual.fmt()}`);
// new_positions.push([window, backup, actual]);
});
} else {
Log.error(`Attempted move of Window(${entity}), but it does not have attached signals`);
}
Expand Down Expand Up @@ -704,7 +703,7 @@ export class Forest extends Ecs.World {
}


function move_window(window: ShellWindow, rect: Rectangular, signals: [SignalID, SignalID, SignalID]) {
function move_window(ext: Ext, window: ShellWindow, rect: Rectangular, signals: [SignalID, SignalID, SignalID], on_complete: () => void) {
if (!(window.meta instanceof Meta.Window)) {
Log.error(`attempting to a window entity in a tree which lacks a Meta.Window`);
return;
Expand All @@ -718,6 +717,8 @@ function move_window(window: ShellWindow, rect: Rectangular, signals: [SignalID,
}

for (const sig of signals) utils.block_signal(window.meta, sig);
window.move(rect);
for (const sig of signals) utils.unblock_signal(window.meta, sig);
window.move(ext, rect, () => {
for (const sig of signals) utils.unblock_signal(window.meta, sig);
on_complete();
});
}
5 changes: 5 additions & 0 deletions src/mod.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,18 @@ declare namespace Clutter {

add(child: Actor): void;
destroy(): void;
ease(params: Object): void;
hide(): void;
get_child_at_index(nth: number): Clutter.Actor | null;
get_n_children(): number;
get_parent(): Clutter.Actor | null;
get_transition(param: string): any | null;
is_visible(): boolean;
remove_all_children(): void;
remove_all_transitions(): void;
remove_child(child: Actor): void;
set_child_below_sibling(child: Actor, sibling: Actor | null): void;
set_easing_duration(msecs: number | null): void;
show(): void;
}

Expand Down Expand Up @@ -113,6 +117,7 @@ declare namespace Meta {

activate(time: number): void;
change_workspace_by_index(workspace: number, append: boolean): void;
get_buffer_rect(): Rectangular;
get_compositor_private(): Clutter.Actor | null;
get_description(): string;
get_frame_rect(): Rectangular;
Expand Down
40 changes: 25 additions & 15 deletions src/tiling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import * as GrabOp from 'grab_op';
import * as Rect from 'rectangle';
import * as window from 'window';
import * as shell from 'shell';
import * as Tweener from 'tweener';

import type { Entity } from './ecs';
import type { Rectangle } from './rectangle';
import type { Ext } from './extension';
import { AutoTiler } from './auto_tiler';

const GLib: GLib = imports.gi.GLib;
const { Meta } = imports.gi;
const Main = imports.ui.main;
const { ShellWindow } = window;
Expand Down Expand Up @@ -233,9 +233,9 @@ export class Tiler {

ext.auto_tiler.forest.arrange(ext, fork.workspace);

GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
let actor = window.meta.get_compositor_private();
if (actor) Tweener.on_tween_completion(actor, () => {
ext.set_overlay(window.rect());
return false;
});
}
}
Expand Down Expand Up @@ -308,6 +308,8 @@ export class Tiler {
if (move_to === null) return;

const focused = ext.focus_window();
let watching: null | window.ShellWindow = null;

if (ext.auto_tiler && focused) {
if (move_to instanceof ShellWindow) {
const parent = ext.auto_tiler.windows_are_siblings(focused.entity, move_to.entity);
Expand All @@ -318,24 +320,32 @@ export class Tiler {
fork.left.entity = (fork.right as any).entity;
(fork.right as any).entity = temp;
ext.auto_tiler.tile(ext, fork, fork.area as any);
ext.set_overlay(focused.rect());
focused.activate();
return;
watching = focused
}
}

ext.auto_tiler.detach_window(ext, focused.entity);
ext.auto_tiler.attach_to_window(ext, move_to, focused, Lib.cursor_rect());
ext.set_overlay(focused.rect());
focused.activate();
if (!watching) {
ext.auto_tiler.detach_window(ext, focused.entity);
ext.auto_tiler.attach_to_window(ext, move_to, focused, Lib.cursor_rect());
watching = focused;
}
} else {
global.log(`attach to monitor ${move_to}`);
ext.auto_tiler.detach_window(ext, focused.entity);
ext.auto_tiler.attach_to_monitor(ext, focused, [move_to, ext.active_workspace()]);
ext.set_overlay(focused.rect());
focused.activate();
watching = focused;
}
}

if (watching) {
let actor = watching.meta.get_compositor_private();
if (actor) Tweener.on_tween_completion(actor, () => {
if (watching) {
ext.set_overlay(watching.rect());
watching.activate();
}
});
}
}

move_left(ext: Ext) {
Expand Down Expand Up @@ -483,12 +493,12 @@ export class Tiler {
if (ext.auto_tiler) {
ext.auto_tiler.attach_swap(this.swap_window, this.window);
}
meta_swap.move(meta.rect());
meta_swap.move(ext, meta.rect());
this.swap_window = null;
}
}

meta.move(ext.overlay);
meta.move(ext, ext.overlay);
ext.add_tag(this.window, Tags.Tiled);
}
}
Expand Down Expand Up @@ -523,7 +533,7 @@ export class Tiler {
0, 0, 0, 0
);

win.move(rect);
win.move(ext, rect);

ext.snapped.insert(win.entity, true);
}
Expand Down
41 changes: 41 additions & 0 deletions src/tweener.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const GLib: GLib = imports.gi.GLib;
const { Clutter } = imports.gi;

export interface TweenParams {
x: number;
y: number;
width: number;
height: number;
duration: number;
mode: any | null;
onComplete: () => void;
}

export function add(a: Clutter.Actor, p: TweenParams) {
if (!p.mode) p.mode = Clutter.AnimationMode.LINEAR;

a.ease(p);
}

export function remove(a: Clutter.Actor) {
a.remove_all_transitions();
}

export function is_tweening(a: Clutter.Actor) {
return a.get_transition('x')
|| a.get_transition('y')
|| a.get_transition('width')
|| a.get_transition('height')
|| a.get_transition('scale-x')
|| a.get_transition('scale-x');
}

export function on_tween_completion(actor: Clutter.Actor, callback: () => void) {
GLib.timeout_add(150, GLib.PRIORITY_DEFAULT, () => {
if (is_tweening(actor)) return true;

callback();

return false;
});
}
Loading

0 comments on commit 3ede7c7

Please sign in to comment.