Compare commits
5 Commits
wip/folder
...
3.11.3
Author | SHA1 | Date | |
---|---|---|---|
![]() |
dafb7b5259 | ||
![]() |
92906e217c | ||
![]() |
5c7b721879 | ||
![]() |
c27dcb0414 | ||
![]() |
7fcae1e974 |
34
NEWS
34
NEWS
@@ -1,3 +1,37 @@
|
|||||||
|
3.11.3
|
||||||
|
======
|
||||||
|
* Fix fade effect of desktop icons [Florian; #707671]
|
||||||
|
* Fix issues with background management code [Jasper; #709313]
|
||||||
|
* Use new Glib facilities for application search [Jasper; #711631]
|
||||||
|
* Add focus indication to session menu button [Sebastien; #710539]
|
||||||
|
* Fix hover tracking for StEntries [Jasper; #706749]
|
||||||
|
* Fix reentrancy issue in message tray [Jasper; #711694]
|
||||||
|
* Tone down zoom animation on login/unlock [Jasper; #712362]
|
||||||
|
* Allow specifying monitor for OSD [Carlos; #712664]
|
||||||
|
* Fix resetting prompt on user switch [Ray; #710456]
|
||||||
|
* Stop using gnome-bluetooth-applet [Bastien; #719341]
|
||||||
|
* Add support for EAP-FAST password requests [Dan; #719813]
|
||||||
|
* Fix entry focus of chat notifications [Jasper; #709853]
|
||||||
|
* Make window previews keyboard navigatable [Jasper; #644306]
|
||||||
|
* Fix app switcher order with dialog windows [Florian; #719824]
|
||||||
|
* Allow remote search providers without icons [Debarshi; #719965]
|
||||||
|
* Fix various alignment issues in RTL locales [Yosef; #712638, #712596,
|
||||||
|
#712594, #712600, #712579]
|
||||||
|
* Misc. bug fixes and cleanups [Jasper, Florian, Giovanni, Dan; #712727,
|
||||||
|
#712753, #719378, #719730, #719803, #710115, #720017, #719815, #719567,
|
||||||
|
#720298]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Carlos Garnacho, Sebastien Lafargue, Tim Lunn,
|
||||||
|
Florian Müllner, Bastien Nocera, Yosef Or Boczko, Debarshi Ray,
|
||||||
|
Jasper St. Pierre, Ray Strode, Dan Williams
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Kjartan Maraas [nb], Reinout van Schouwen [nl], Rafael Ferreira [pt_BR],
|
||||||
|
Mattias Põldaru [et], Emin Tufan Çetin [tr], Jiri Grönroos [fi],
|
||||||
|
Khaled Hosny [ar], Fran Diéguez [gl], Victor Ibragimov [tg],
|
||||||
|
Daniel Mustieles [es]
|
||||||
|
|
||||||
3.11.2
|
3.11.2
|
||||||
======
|
======
|
||||||
* Cache search result display actors [Jasper; #704912]
|
* Cache search result display actors [Jasper; #704912]
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
AC_PREREQ(2.63)
|
AC_PREREQ(2.63)
|
||||||
AC_INIT([gnome-shell],[3.11.2],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
AC_INIT([gnome-shell],[3.11.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||||
|
|
||||||
AC_CONFIG_HEADERS([config.h])
|
AC_CONFIG_HEADERS([config.h])
|
||||||
AC_CONFIG_SRCDIR([src/shell-global.c])
|
AC_CONFIG_SRCDIR([src/shell-global.c])
|
||||||
|
@@ -5,6 +5,7 @@ const Gio = imports.gi.Gio;
|
|||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
const GObject = imports.gi.GObject;
|
const GObject = imports.gi.GObject;
|
||||||
const Gtk = imports.gi.Gtk;
|
const Gtk = imports.gi.Gtk;
|
||||||
|
const GMenu = imports.gi.GMenu;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
@@ -46,6 +47,25 @@ const INDICATORS_ANIMATION_MAX_TIME = 0.75;
|
|||||||
const PAGE_SWITCH_TRESHOLD = 0.2;
|
const PAGE_SWITCH_TRESHOLD = 0.2;
|
||||||
const PAGE_SWITCH_TIME = 0.3;
|
const PAGE_SWITCH_TIME = 0.3;
|
||||||
|
|
||||||
|
// Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too
|
||||||
|
function _loadCategory(dir, view) {
|
||||||
|
let iter = dir.iter();
|
||||||
|
let appSystem = Shell.AppSystem.get_default();
|
||||||
|
let nextType;
|
||||||
|
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
|
||||||
|
if (nextType == GMenu.TreeItemType.ENTRY) {
|
||||||
|
let entry = iter.get_entry();
|
||||||
|
let appInfo = entry.get_app_info();
|
||||||
|
let app = appSystem.lookup_app(entry.get_desktop_file_id());
|
||||||
|
if (appInfo.should_show())
|
||||||
|
view.addApp(app);
|
||||||
|
} else if (nextType == GMenu.TreeItemType.DIRECTORY) {
|
||||||
|
let itemDir = iter.get_directory();
|
||||||
|
_loadCategory(itemDir, view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const BaseAppView = new Lang.Class({
|
const BaseAppView = new Lang.Class({
|
||||||
Name: 'BaseAppView',
|
Name: 'BaseAppView',
|
||||||
Abstract: true,
|
Abstract: true,
|
||||||
@@ -77,22 +97,40 @@ const BaseAppView = new Lang.Class({
|
|||||||
this._allItems = [];
|
this._allItems = [];
|
||||||
},
|
},
|
||||||
|
|
||||||
addItem: function(icon) {
|
_getItemId: function(item) {
|
||||||
let id = icon.id;
|
throw new Error('Not implemented');
|
||||||
if (this._items[id] !== undefined)
|
},
|
||||||
return;
|
|
||||||
|
|
||||||
this._allItems.push(icon);
|
_createItemIcon: function(item) {
|
||||||
this._items[id] = icon;
|
throw new Error('Not implemented');
|
||||||
|
},
|
||||||
|
|
||||||
|
_compareItems: function(a, b) {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
},
|
||||||
|
|
||||||
|
_addItem: function(item) {
|
||||||
|
let id = this._getItemId(item);
|
||||||
|
if (this._items[id] !== undefined)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
let itemIcon = this._createItemIcon(item);
|
||||||
|
this._allItems.push(item);
|
||||||
|
this._items[id] = itemIcon;
|
||||||
|
|
||||||
|
return itemIcon;
|
||||||
},
|
},
|
||||||
|
|
||||||
loadGrid: function() {
|
loadGrid: function() {
|
||||||
this._allItems.sort(Lang.bind(this, function(a, b) {
|
this._allItems.sort(Lang.bind(this, this._compareItems));
|
||||||
return a.name.localeCompare(b.name);
|
|
||||||
}));
|
for (let i = 0; i < this._allItems.length; i++) {
|
||||||
this._allItems.forEach(Lang.bind(this, function(item) {
|
let id = this._getItemId(this._allItems[i]);
|
||||||
this._grid.addItem(item);
|
if (!id)
|
||||||
}));
|
continue;
|
||||||
|
this._grid.addItem(this._items[id]);
|
||||||
|
}
|
||||||
|
|
||||||
this.emit('view-loaded');
|
this.emit('view-loaded');
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -243,7 +281,7 @@ const AllView = new Lang.Class({
|
|||||||
this._pageIndicators.actor.connect('scroll-event', Lang.bind(this, this._onScroll));
|
this._pageIndicators.actor.connect('scroll-event', Lang.bind(this, this._onScroll));
|
||||||
this.actor.add_actor(this._pageIndicators.actor);
|
this.actor.add_actor(this._pageIndicators.actor);
|
||||||
|
|
||||||
this.folderIcons = [];
|
this._folderIcons = [];
|
||||||
|
|
||||||
this._stack = new St.Widget({ layout_manager: new Clutter.BinLayout() });
|
this._stack = new St.Widget({ layout_manager: new Clutter.BinLayout() });
|
||||||
let box = new St.BoxLayout({ vertical: true });
|
let box = new St.BoxLayout({ vertical: true });
|
||||||
@@ -429,11 +467,52 @@ const AllView = new Lang.Class({
|
|||||||
return Clutter.EVENT_PROPAGATE;
|
return Clutter.EVENT_PROPAGATE;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getItemId: function(item) {
|
||||||
|
if (item instanceof Shell.App)
|
||||||
|
return item.get_id();
|
||||||
|
else if (item instanceof GMenu.TreeDirectory)
|
||||||
|
return item.get_menu_id();
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
_createItemIcon: function(item) {
|
||||||
|
if (item instanceof Shell.App)
|
||||||
|
return new AppIcon(item);
|
||||||
|
else if (item instanceof GMenu.TreeDirectory)
|
||||||
|
return new FolderIcon(item, this);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
_compareItems: function(itemA, itemB) {
|
||||||
|
// bit of a hack: rely on both ShellApp and GMenuTreeDirectory
|
||||||
|
// having a get_name() method
|
||||||
|
let nameA = GLib.utf8_collate_key(itemA.get_name(), -1);
|
||||||
|
let nameB = GLib.utf8_collate_key(itemB.get_name(), -1);
|
||||||
|
return (nameA > nameB) ? 1 : (nameA < nameB ? -1 : 0);
|
||||||
|
},
|
||||||
|
|
||||||
removeAll: function() {
|
removeAll: function() {
|
||||||
this.folderIcons = [];
|
this._folderIcons = [];
|
||||||
this.parent();
|
this.parent();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
addApp: function(app) {
|
||||||
|
let appIcon = this._addItem(app);
|
||||||
|
if (appIcon)
|
||||||
|
appIcon.actor.connect('key-focus-in',
|
||||||
|
Lang.bind(this, this._ensureIconVisible));
|
||||||
|
},
|
||||||
|
|
||||||
|
addFolder: function(dir) {
|
||||||
|
let folderIcon = this._addItem(dir);
|
||||||
|
this._folderIcons.push(folderIcon);
|
||||||
|
if (folderIcon)
|
||||||
|
folderIcon.actor.connect('key-focus-in',
|
||||||
|
Lang.bind(this, this._ensureIconVisible));
|
||||||
|
},
|
||||||
|
|
||||||
addFolderPopup: function(popup) {
|
addFolderPopup: function(popup) {
|
||||||
this._stack.add_actor(popup.actor);
|
this._stack.add_actor(popup.actor);
|
||||||
popup.connect('open-state-changed', Lang.bind(this,
|
popup.connect('open-state-changed', Lang.bind(this,
|
||||||
@@ -498,8 +577,8 @@ const AllView = new Lang.Class({
|
|||||||
this._availWidth = availWidth;
|
this._availWidth = availWidth;
|
||||||
this._availHeight = availHeight;
|
this._availHeight = availHeight;
|
||||||
// Update folder views
|
// Update folder views
|
||||||
for (let i = 0; i < this.folderIcons.length; i++)
|
for (let i = 0; i < this._folderIcons.length; i++)
|
||||||
this.folderIcons[i].adaptToSize(availWidth, availHeight);
|
this._folderIcons[i].adaptToSize(availWidth, availHeight);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(AllView.prototype);
|
Signals.addSignalMethods(AllView.prototype);
|
||||||
@@ -622,9 +701,8 @@ const AppDisplay = new Lang.Class({
|
|||||||
Main.overview.connect('showing', Lang.bind(this, function() {
|
Main.overview.connect('showing', Lang.bind(this, function() {
|
||||||
Main.queueDeferredWork(this._frequentAppsWorkId);
|
Main.queueDeferredWork(this._frequentAppsWorkId);
|
||||||
}));
|
}));
|
||||||
this._softwareSettings = new Gio.Settings({ schema: 'org.gnome.software' });
|
global.settings.connect('changed::app-folder-categories', Lang.bind(this, function() {
|
||||||
this._softwareSettings.connect('changed::app-folders', Lang.bind(this, function() {
|
Main.queueDeferredWork(this._allAppsWorkId);
|
||||||
Main.queueDeferredWork(this._frequentAppsWorkId);
|
|
||||||
}));
|
}));
|
||||||
this._privacySettings = new Gio.Settings({ schema: 'org.gnome.desktop.privacy' });
|
this._privacySettings = new Gio.Settings({ schema: 'org.gnome.desktop.privacy' });
|
||||||
this._privacySettings.connect('changed::remember-app-usage',
|
this._privacySettings.connect('changed::remember-app-usage',
|
||||||
@@ -738,26 +816,23 @@ const AppDisplay = new Lang.Class({
|
|||||||
|
|
||||||
view.removeAll();
|
view.removeAll();
|
||||||
|
|
||||||
let apps = Gio.AppInfo.get_all();
|
let tree = new GMenu.Tree({ menu_basename: "applications.menu" });
|
||||||
|
tree.load_sync();
|
||||||
|
let root = tree.get_root_directory();
|
||||||
|
|
||||||
let folders = this._softwareSettings.get_value('app-folders').deep_unpack();
|
let iter = root.iter();
|
||||||
for (let id in folders) {
|
let nextType;
|
||||||
let folderApps = folders[id];
|
let folderCategories = global.settings.get_strv('app-folder-categories');
|
||||||
let icon = new FolderIcon(id, id, folderApps, view);
|
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
|
||||||
view.addItem(icon);
|
if (nextType == GMenu.TreeItemType.DIRECTORY) {
|
||||||
view.folderIcons.push(icon);
|
let dir = iter.get_directory();
|
||||||
|
|
||||||
|
if (folderCategories.indexOf(dir.get_menu_id()) != -1)
|
||||||
|
view.addFolder(dir);
|
||||||
|
else
|
||||||
|
_loadCategory(dir, view);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let appSys = Shell.AppSystem.get_default();
|
|
||||||
apps.forEach(Lang.bind(this, function(appInfo) {
|
|
||||||
if (!appInfo.should_show())
|
|
||||||
return;
|
|
||||||
|
|
||||||
let app = appSys.lookup_app(appInfo.get_id());
|
|
||||||
let icon = new AppIcon(app);
|
|
||||||
view.addItem(icon);
|
|
||||||
}));
|
|
||||||
|
|
||||||
view.loadGrid();
|
view.loadGrid();
|
||||||
|
|
||||||
if (this._focusDummy) {
|
if (this._focusDummy) {
|
||||||
@@ -881,6 +956,22 @@ const FolderView = new Lang.Class({
|
|||||||
this.actor.add_action(action);
|
this.actor.add_action(action);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getItemId: function(item) {
|
||||||
|
return item.get_id();
|
||||||
|
},
|
||||||
|
|
||||||
|
_createItemIcon: function(item) {
|
||||||
|
return new AppIcon(item);
|
||||||
|
},
|
||||||
|
|
||||||
|
_compareItems: function(a, b) {
|
||||||
|
return a.compare_by_name(b);
|
||||||
|
},
|
||||||
|
|
||||||
|
addApp: function(app) {
|
||||||
|
this._addItem(app);
|
||||||
|
},
|
||||||
|
|
||||||
createFolderIcon: function(size) {
|
createFolderIcon: function(size) {
|
||||||
let icon = new St.Widget({ layout_manager: new Clutter.BinLayout(),
|
let icon = new St.Widget({ layout_manager: new Clutter.BinLayout(),
|
||||||
style_class: 'app-folder-icon',
|
style_class: 'app-folder-icon',
|
||||||
@@ -889,7 +980,7 @@ const FolderView = new Lang.Class({
|
|||||||
|
|
||||||
let aligns = [ Clutter.ActorAlign.START, Clutter.ActorAlign.END ];
|
let aligns = [ Clutter.ActorAlign.START, Clutter.ActorAlign.END ];
|
||||||
for (let i = 0; i < Math.min(this._allItems.length, 4); i++) {
|
for (let i = 0; i < Math.min(this._allItems.length, 4); i++) {
|
||||||
let texture = this._allItems[i].app.create_icon_texture(subSize);
|
let texture = this._allItems[i].create_icon_texture(subSize);
|
||||||
let bin = new St.Bin({ child: texture,
|
let bin = new St.Bin({ child: texture,
|
||||||
x_expand: true, y_expand: true });
|
x_expand: true, y_expand: true });
|
||||||
bin.set_x_align(aligns[i % 2]);
|
bin.set_x_align(aligns[i % 2]);
|
||||||
@@ -966,9 +1057,8 @@ const FolderView = new Lang.Class({
|
|||||||
const FolderIcon = new Lang.Class({
|
const FolderIcon = new Lang.Class({
|
||||||
Name: 'FolderIcon',
|
Name: 'FolderIcon',
|
||||||
|
|
||||||
_init: function(id, name, apps, parentView) {
|
_init: function(dir, parentView) {
|
||||||
this.id = id;
|
this._dir = dir;
|
||||||
this.name = name;
|
|
||||||
this._parentView = parentView;
|
this._parentView = parentView;
|
||||||
|
|
||||||
this.actor = new St.Button({ style_class: 'app-well-app app-folder',
|
this.actor = new St.Button({ style_class: 'app-well-app app-folder',
|
||||||
@@ -981,21 +1071,14 @@ const FolderIcon = new Lang.Class({
|
|||||||
// whether we need to update arrow side, position etc.
|
// whether we need to update arrow side, position etc.
|
||||||
this._popupInvalidated = false;
|
this._popupInvalidated = false;
|
||||||
|
|
||||||
this.icon = new IconGrid.BaseIcon(this.name,
|
let label = this._dir.get_name();
|
||||||
|
this.icon = new IconGrid.BaseIcon(label,
|
||||||
{ createIcon: Lang.bind(this, this._createIcon), setSizeManually: true });
|
{ createIcon: Lang.bind(this, this._createIcon), setSizeManually: true });
|
||||||
this.actor.set_child(this.icon.actor);
|
this.actor.set_child(this.icon.actor);
|
||||||
this.actor.label_actor = this.icon.label;
|
this.actor.label_actor = this.icon.label;
|
||||||
|
|
||||||
this.view = new FolderView();
|
this.view = new FolderView();
|
||||||
let appSys = Shell.AppSystem.get_default();
|
_loadCategory(dir, this.view);
|
||||||
apps.forEach(Lang.bind(this, function(appId) {
|
|
||||||
let app = appSys.lookup_app(appId + '.desktop');
|
|
||||||
if (!app)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let icon = new AppIcon(app);
|
|
||||||
this.view.addItem(icon);
|
|
||||||
}));
|
|
||||||
this.view.loadGrid();
|
this.view.loadGrid();
|
||||||
|
|
||||||
this.actor.connect('clicked', Lang.bind(this,
|
this.actor.connect('clicked', Lang.bind(this,
|
||||||
@@ -1205,9 +1288,6 @@ const AppIcon = new Lang.Class({
|
|||||||
|
|
||||||
_init : function(app, iconParams) {
|
_init : function(app, iconParams) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.id = app.get_id();
|
|
||||||
this.name = app.get_name();
|
|
||||||
|
|
||||||
this.actor = new St.Button({ style_class: 'app-well-app',
|
this.actor = new St.Button({ style_class: 'app-well-app',
|
||||||
reactive: true,
|
reactive: true,
|
||||||
button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO,
|
button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO,
|
||||||
|
@@ -232,12 +232,13 @@ const WorkspaceTracker = new Lang.Class({
|
|||||||
|
|
||||||
let windows = global.get_window_actors();
|
let windows = global.get_window_actors();
|
||||||
for (i = 0; i < windows.length; i++) {
|
for (i = 0; i < windows.length; i++) {
|
||||||
let win = windows[i];
|
let actor = windows[i];
|
||||||
|
let win = actor.get_meta_window();
|
||||||
|
|
||||||
if (win.get_meta_window().is_on_all_workspaces())
|
if (win.is_on_all_workspaces())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
let workspaceIndex = win.get_workspace();
|
let workspaceIndex = win.get_workspace().index();
|
||||||
emptyWorkspaces[workspaceIndex] = false;
|
emptyWorkspaces[workspaceIndex] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -975,25 +976,29 @@ const WindowManager = new Lang.Class({
|
|||||||
wgroup.add_actor(switchData.movingWindowBin);
|
wgroup.add_actor(switchData.movingWindowBin);
|
||||||
|
|
||||||
for (let i = 0; i < windows.length; i++) {
|
for (let i = 0; i < windows.length; i++) {
|
||||||
let window = windows[i];
|
let actor = windows[i];
|
||||||
|
let window = actor.get_meta_window();
|
||||||
|
|
||||||
if (!window.meta_window.showing_on_its_workspace())
|
if (!window.showing_on_its_workspace())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (this._movingWindow && window.meta_window == this._movingWindow) {
|
if (window.is_on_all_workspaces())
|
||||||
switchData.movingWindow = { window: window,
|
continue;
|
||||||
parent: window.get_parent() };
|
|
||||||
|
let record = { window: actor,
|
||||||
|
parent: actor.get_parent() };
|
||||||
|
|
||||||
|
if (this._movingWindow && window == this._movingWindow) {
|
||||||
|
switchData.movingWindow = record;
|
||||||
switchData.windows.push(switchData.movingWindow);
|
switchData.windows.push(switchData.movingWindow);
|
||||||
window.reparent(switchData.movingWindowBin);
|
actor.reparent(switchData.movingWindowBin);
|
||||||
} else if (window.get_workspace() == from) {
|
} else if (window.get_workspace().index() == from) {
|
||||||
switchData.windows.push({ window: window,
|
switchData.windows.push(record);
|
||||||
parent: window.get_parent() });
|
actor.reparent(switchData.outGroup);
|
||||||
window.reparent(switchData.outGroup);
|
} else if (window.get_workspace().index() == to) {
|
||||||
} else if (window.get_workspace() == to) {
|
switchData.windows.push(record);
|
||||||
switchData.windows.push({ window: window,
|
actor.reparent(switchData.inGroup);
|
||||||
parent: window.get_parent() });
|
actor.show();
|
||||||
window.reparent(switchData.inGroup);
|
|
||||||
window.show();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -62,7 +62,7 @@ const XdndHandler = new Lang.Class({
|
|||||||
let cursorWindow = windows[windows.length - 1];
|
let cursorWindow = windows[windows.length - 1];
|
||||||
|
|
||||||
// FIXME: more reliable way?
|
// FIXME: more reliable way?
|
||||||
if (!cursorWindow.is_override_redirect())
|
if (!cursorWindow.get_meta_window().is_override_redirect())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let constraint_position = new Clutter.BindConstraint({ coordinate : Clutter.BindCoordinate.POSITION,
|
let constraint_position = new Clutter.BindConstraint({ coordinate : Clutter.BindCoordinate.POSITION,
|
||||||
|
Reference in New Issue
Block a user