Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backward-compatible GNOME 3.36 support #1

Merged
merged 13 commits into from
May 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions argos@pew.worldwidemann.com/button.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ var ArgosButton = new Lang.Class({

this._lineView = new ArgosLineView();
this._lineView.setMarkup("<small><i>" + GLib.markup_escape_text(file.get_basename(), -1) + " ...</i></small>");
this.add_actor(this._lineView);
Utilities.getActor(this).add_actor(this._lineView);

this._isDestroyed = false;

Expand Down Expand Up @@ -136,9 +136,9 @@ var ArgosButton = new Lang.Class({
this._cycleTimeout = null;
}

this.visible = buttonLines.length > 0 || !dropdownMode;
Utilities.getActor(this).visible = buttonLines.length > 0 || !dropdownMode;

if (!this.visible)
if (!Utilities.getActor(this).visible)
return;

if (buttonLines.length === 0) {
Expand Down Expand Up @@ -191,8 +191,10 @@ var ArgosButton = new Lang.Class({
menuItem.actor.insert_child_below(lineView, menuItem.label);
menuItem.label.visible = false;
menus[dropdownLines[i + 1].menuLevel] = menuItem.menu;
} else if ((i + 1) < dropdownLines.length && dropdownLines[i + 1].menuLevel === dropdownLines[i].menuLevel &&
dropdownLines[i + 1].alternate === "true") {
} else if ((i + 1) < dropdownLines.length &&
dropdownLines[i + 1].menuLevel === dropdownLines[i].menuLevel &&
dropdownLines[i + 1].hasOwnProperty("alternate") &&
dropdownLines[i + 1].alternate === "true") {
menuItem = new ArgosMenuItem(this, dropdownLines[i], dropdownLines[i + 1]);
// Skip alternate line
i++;
Expand Down
46 changes: 32 additions & 14 deletions argos@pew.worldwidemann.com/menuitem.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,32 @@ const Lang = imports.lang;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const PopupMenu = imports.ui.popupMenu;
const AltSwitcher = imports.ui.status.system.AltSwitcher;

const Extension = imports.misc.extensionUtils.getCurrentExtension();
const ArgosLineView = Extension.imports.lineview.ArgosLineView;
const Utilities = Extension.imports.utilities;
const AltSwitcher = Utilities.AltSwitcher;

var ArgosMenuItem = class extends PopupMenu.PopupBaseMenuItem {
constructor(button, line, alternateLine) {
let hasAction = line.hasAction || (typeof alternateLine !== "undefined" && alternateLine.hasAction);

super({
activate: hasAction,
hover: hasAction,
can_focus: hasAction
});
// "constructor" aka "init function" for ArgosMenuItem.
// Defined as ordinary function, referencing "this"; these references will
// be resolved by later bind() calls.
// Utilities.MakeSimpleClass() is used below to actually define the class.
const _ArgosMenuItem_init = function(button, line, alternateLine) {
let hasAction = line.hasAction ||
(typeof alternateLine !== "undefined" && alternateLine.hasAction);

let altSwitcher = null;

let lineView = new ArgosLineView(line);

if (typeof alternateLine === "undefined") {
this.actor.add_child(lineView);
Utilities.getActor(this).add_child(lineView);
} else {
let alternateLineView = new ArgosLineView(alternateLine);
altSwitcher = new AltSwitcher(lineView, alternateLineView);
lineView.visible = true;
alternateLineView.visible = true;
this.actor.add_child(altSwitcher.actor);
Utilities.getActor(this).add_child(altSwitcher.actor);
}

if (hasAction) {
Expand Down Expand Up @@ -77,5 +76,24 @@ var ArgosMenuItem = class extends PopupMenu.PopupBaseMenuItem {
}
}));
}
}
};
}

// Helper for the init function. This function is passed the same arguments
// as the constuctor and returns an object to be passed to the superclass
// constructor / init function.
const _ArgosMenuItem_superArg = function(button, line, alternateLine) {
let hasAction = line.hasAction ||
(typeof alternateLine !== "undefined" && alternateLine.hasAction);

return {
activate: hasAction,
hover: hasAction,
can_focus: hasAction
};
}

var ArgosMenuItem = Utilities.makeSimpleClass(
PopupMenu.PopupBaseMenuItem,
_ArgosMenuItem_superArg,
_ArgosMenuItem_init,
"ArgosMenuItem");
12 changes: 10 additions & 2 deletions argos@pew.worldwidemann.com/metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
"name": "Argos",
"description": "Create GNOME Shell extensions in seconds",
"url": "https://github.com/p-e-w/argos",
"version": 3,
"shell-version": ["3.32"]
"shell-version": [
"3.26",
"3.28",
"3.30",
"3.32",
"3.34",
"3.36",
"3.38",
"40"
]
}
216 changes: 209 additions & 7 deletions argos@pew.worldwidemann.com/utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@
* (https://gnu.org/licenses/gpl.html)
*/

const Lang = imports.lang;
const GObject = imports.gi.GObject;
const St = imports.gi.St;
const GLib = imports.gi.GLib;
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const Config = imports.misc.config;

const Extension = imports.misc.extensionUtils.getCurrentExtension();
const EMOJI = Extension.imports.emoji.EMOJI;
Expand Down Expand Up @@ -129,28 +134,28 @@ function parseLine(lineString) {

line.markup = line.text;

if (line.unescape !== "false")
if (!line.hasOwnProperty("unescape") || line.unescape !== "false")
line.markup = GLib.strcompress(line.markup);

if (line.emojize !== "false") {
if (!line.hasOwnProperty("emojize") || line.emojize !== "false") {
line.markup = line.markup.replace(/:([\w+-]+):/g, function(match, emojiName) {
emojiName = emojiName.toLowerCase();
return EMOJI.hasOwnProperty(emojiName) ? EMOJI[emojiName] : match;
});
}

if (line.trim !== "false")
if (!line.hasOwnProperty("trim") || line.trim !== "false")
line.markup = line.markup.trim();

if (line.useMarkup === "false") {
if (line.hasOwnProperty("useMarkup") && line.useMarkup === "false") {
line.markup = GLib.markup_escape_text(line.markup, -1);
// Restore escaped ESC characters (needed for ANSI sequences)
line.markup = line.markup.replace("&#x1b;", "\x1b");
}

// Note that while it is possible to format text using a combination of Pango markup
// and ANSI escape sequences, lines like "<b>ABC \e[1m DEF</b>" lead to unmatched tags
if (line.ansi !== "false")
if (!line.hasOwnProperty("ansi") || line.ansi !== "false")
line.markup = ansiToMarkup(line.markup);

if (markupAttributes.length > 0)
Expand All @@ -167,7 +172,8 @@ function parseLine(lineString) {
}

line.hasAction = line.hasOwnProperty("bash") || line.hasOwnProperty("href") ||
line.hasOwnProperty("eval") || line.refresh === "true";
line.hasOwnProperty("eval") ||
(line.hasOwnProperty("refresh") && line.refresh === "true");

return line;
}
Expand Down Expand Up @@ -272,15 +278,211 @@ function spawnWithCallback(workingDirectory, argv, envp, flags, childSetup, call
});
}

function getShellVersion(str) {

let v = str.split(".");
let n = 0;

if (v.length == 2) {
// GNOME 40 and newer versioning scheme
// https://discourse.gnome.org/t/new-gnome-versioning-scheme/4235
// must be > 3.x.y with x <= 38
// 40.alpha -> 33997
// 41.beta -> 34098
// 41.rc -> 34099
// 41.0 -> 34100
// 40.1 -> 34001
let testReleases = new Map([["alpha", -3],
["beta", -2],
["rc", -1]]);
let minor = testReleases.get(v[1]);
let major = Number(v[0]);

if (typeof(minor) == "undefined") {
minor = Number(v[1]);
}

if (major >= 40)
n = 30000 + major * 100 + minor;

} else if (v.length == 3 && v[0] == "3") {
n = v.map(Number).reduce(
function(a, x) {
return 100 * a + x;
});

};

if (n == 0) {
log("argos: Unsupported GNOME shell version " + str);
return 0;
}

// log("argos: GNOME shell version " + str + " => " + n);
return n;
}

const shellVersion = getShellVersion(Config.PACKAGE_VERSION);

function readStream(stream, callback) {
stream.read_line_async(GLib.PRIORITY_LOW, null, function(source, result) {
let [line] = source.read_line_finish(result);

if (line === null) {
callback(null);
} else {
callback(imports.byteArray.toString(line) + "\n");
if (shellVersion <= 33400)
callback(String(line) + "\n");
else
callback(imports.byteArray.toString(line) + "\n");
readStream(source, callback);
}
});
}

function getActor(obj) {
if (shellVersion >= 33400)
return obj;
else
return obj.actor;
}

function makeSimpleClass(BaseClass, getSuperArgs, initFn, name) {
if (shellVersion < 33200) {
return new Lang.Class({
Name: name,
Extends: BaseClass,
_init: function(...args) {
this.parent(getSuperArgs(...args));
initFn.bind(this)(...args);
}
});
} else if (shellVersion < 33400) {
return class extends BaseClass {
constructor(...args) {
super(getSuperArgs(...args));
initFn.bind(this)(...args);
}
}
} else {
return GObject.registerClass(
{
GTypeName: name
},
class extends BaseClass {
_init(...args) {
super._init(getSuperArgs(...args));
initFn.bind(this)(...args);
}
});
}
}

if (shellVersion <= 33400)
var AltSwitcher = imports.ui.status.system.AltSwitcher;
else
var AltSwitcher = GObject.registerClass(
class AltSwitcher extends St.Bin {
_init(standard, alternate) {
super._init();
this._standard = standard;
this._standard.connect('notify::visible', this._sync.bind(this));
if (this._standard instanceof St.Button)
this._standard.connect('clicked',
() => this._clickAction.release());

this._alternate = alternate;
this._alternate.connect('notify::visible', this._sync.bind(this));
if (this._alternate instanceof St.Button)
this._alternate.connect('clicked',
() => this._clickAction.release());

this._capturedEventId = global.stage.connect('captured-event', this._onCapturedEvent.bind(this));

this._flipped = false;

this._clickAction = new Clutter.ClickAction();
this._clickAction.connect('long-press', this._onLongPress.bind(this));

this.connect('destroy', this._onDestroy.bind(this));
}

vfunc_map() {
super.vfunc_map();
this._flipped = false;
}

vfunc_unmap() {
super.vfunc_unmap();
this._flipped = false;
}

_sync() {
let childToShow = null;

if (this._standard.visible && this._alternate.visible) {
let [x_, y_, mods] = global.get_pointer();
let altPressed = (mods & Clutter.ModifierType.MOD1_MASK) != 0;
if (this._flipped)
childToShow = altPressed ? this._standard : this._alternate;
else
childToShow = altPressed ? this._alternate : this._standard;
} else if (this._standard.visible) {
childToShow = this._standard;
} else if (this._alternate.visible) {
childToShow = this._alternate;
} else {
this.hide();
return;
}

let childShown = this.get_child();
if (childShown != childToShow) {
if (childShown) {
if (childShown.fake_release)
childShown.fake_release();
childShown.remove_action(this._clickAction);
}
childToShow.add_action(this._clickAction);

let hasFocus = this.contains(global.stage.get_key_focus());
this.set_child(childToShow);
if (hasFocus)
childToShow.grab_key_focus();

// The actors might respond to hover, so
// sync the pointer to make sure they update.
global.sync_pointer();
}

this.show();
}

_onDestroy() {
if (this._capturedEventId > 0) {
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
}
}

_onCapturedEvent(actor, event) {
let type = event.type();
if (type == Clutter.EventType.KEY_PRESS || type == Clutter.EventType.KEY_RELEASE) {
let key = event.get_key_symbol();
if (key == Clutter.KEY_Alt_L || key == Clutter.KEY_Alt_R)
this._sync();
}

return Clutter.EVENT_PROPAGATE;
}

_onLongPress(action, actor, state) {
if (state == Clutter.LongPressState.QUERY ||
state == Clutter.LongPressState.CANCEL)
return true;

this._flipped = !this._flipped;
this._sync();
return true;
}
});