Extension event loop in Gnome 3.10 vs 3.14 - gtk

I wrote this accessibility extension:
https://extensions.gnome.org/extension/975/keyboard-modifiers-status/
https://github.com/sneetsher/Keyboard-Modifiers-Status
Which works as supposed in Gnome Shell v3.14 & v3.16 but not in v3.10. It shows the only the initial keyboard modifiers state after i
restarted it and never update it after that.
Here the full code:
const St = imports.gi.St;
const Mainloop = imports.mainloop;
const Main = imports.ui.main;
const Gdk = imports.gi.Gdk
let button, label, keymap;
function _update() {
let symbols = "⇧⇬⋀⌥①◆⌘⎇";
let state = keymap.get_modifier_state();
label.text = " ";
for (var i=0; i<=8; i++ ) {
if (state & 1<<i) {
label.text += symbols[i];
} else {
//label.text += "";
}
}
label.text += " ";
}
function init() {
button = new St.Bin({ style_class: 'panel-button',
reactive: false,
can_focus: false,
x_fill: true,
y_fill: false,
track_hover: false });
label = new St.Label({ style_class: "state-label", text: "" });
button.set_child(label);
keymap = Gdk.Keymap.get_default();
keymap.connect('state_changed', _update );
Mainloop.timeout_add(1000, _update );
}
function enable() {
Main.panel._rightBox.insert_child_at_index(button, 0);
}
function disable() {
Main.panel._rightBox.remove_child(button);
}
Trying to debug, I modified the code to show (state label + a counter)
let c,button, label, keymap;
c=0;
function _update() {
Gtk.main_iteration_do(false);
c++;
let symbols = "⇧⇬⋀⌥①◆⌘⎇";
//let keymap = Gdk.Keymap.get_default()
let state = keymap.get_modifier_state();
label.text = " ";
for (var i=0; i<=8; i++ ) {
if (state & 1<<i) {
label.text += symbols[i];
} else {
//label.text += "";
}
}
label.text += " "+c+" ";
return true;
}
I can confirm these:
keymap.connect('state_changed', _update ); this signal is never raised
timeout callback works well
label is updated and show the initial state & the incrementing counter
So I think there is something with event loop as it does not pull
state update or does not process its events.
Could you please point me to way to fix this and what's the difference
between v3.10 & v3.14?

Assuming that commenting out the definition of keymap was intentional, check that it is still assigned elsewhere in your code. Have you tried using a -(minus) rather than a _(underscore)? Most events use the former in JS space, rather than the latter and this has been the problem for me when in several cases where I was attaching events to changing the active workspace, where the back-end for Meta.Display fires workspace_switched, the GJS space connects through workspace-switched and there are a lot more examples there.
For official documentation, including the correct event, property and function names for within GJS space, refer to GNOME DevDocs I don't know when it became official, but they state that it is here

Related

Can an automated apps script email notification link back to specific sheet?

much to my surprise I've successfully made an apps scripts that sends me email notifications when a specific cell is changed to 'Submitted,' but I have no idea how to make this identify the sheet it came from - have linked a copy of the sheet below, there are going to be around 20 of these, each with 6 submission sheets, and I need to do a thing as soon as the sheet has been marked submitted, i.e. same day. I'd rather not hard code in separate messages for each sheet, can I do something around getting the URL and sheet with the get active sheet coding and insert it into the email message? I'm also aware currently I've hard coded in the sheet names and therefore need 6 different triggers, I'm working on that - tried loads of different coding pages and this is the only one that worked!
https://docs.google.com/spreadsheets/d/1b0LOr9vhmFu4WtYy_RbS-1cvXncNOI_x3YT0f30fZgY/edit#gid=1979912158
Cheers,
Meg
function emailSubmit() {
MailApp.sendEmail("Testemail", "Test", "Test message");
}
function onEdit(e) {
const specificSheet = "Sub1"
const specificCell = "C11"
let sheetCheck = (e.range.getSheet().getName() == specificSheet)
let cellCheck = (e.range.getA1Notation() == specificCell)
if (!(sheetCheck && cellCheck) || e.value !== "Submitted") {
return;
}
else {
emailSubmit()
}
}
function onEdit2(e) {
const specificSheet = "Sub2"
const specificCell = "C11"
let sheetCheck = (e.range.getSheet().getName() == specificSheet)
let cellCheck = (e.range.getA1Notation() == specificCell)
if (!(sheetCheck && cellCheck)) {
return
}
else {
emailSubmit()
}
}
To obtain the spreadsheet object bound to the fired onEdit trigger, use the event object source
Sample:
function emailSubmit(spreadsheet, sheet) {
console.log("spreadsheet: " + spreadsheet);
console.log("sheet: " + sheet);
MailApp.sendEmail("Testemail", "Test", "Spreadsheet " + spreadsheet + " and tab " + sheet + "have been submitted");
}
function onEdit(e) {
const allowedSheets = ["Sub1","Sub2"];
const specificCell = "C11";
const spreadsheetName = e.source.getName();
const sheetName = e.range.getSheet().getName();
let sheetCheck = (allowedSheets.indexOf(sheetName) != -1);
let cellCheck = (e.range.getA1Notation() == specificCell);
if (!(sheetCheck && cellCheck) || e.value !== "Submitted") {
return;
}
else {
emailSubmit(spreadsheetName, sheetName);
}
}
References:
Event Objects
getName()
indexOf()

Using Zxing Library with Jetpack compose

I am trying to implement qr scanner using zxing library. For this, i have added a button on screen, and on click of it, i am launching scanner as below
Button(
onClick = {
val intentIntegrator = IntentIntegrator(context)
intentIntegrator.setPrompt(QrScanLabel)
intentIntegrator.setOrientationLocked(true)
intentIntegrator.initiateScan()
},
modifier = Modifier
.fillMaxWidth()
) {
Text(
text = QrScanLabel
)
}
but, it launches an intent, which expects onActivityResult method to get back the results. And Jetpack compose uses rememberLauncherForActivityResult like below
val intentLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartIntentSenderForResult()
) {
if (it.resultCode != RESULT_OK) {
return#rememberLauncherForActivityResult
}
...
}
but how do we integrate both things together here?
I make a provisional solution with same library:
Gradle dependencies:
implementation('com.journeyapps:zxing-android-embedded:4.1.0') { transitive = false }
implementation 'com.google.zxing:core:3.4.0'
My new Screen with jetpack compose and camera capture, that works for my app:
#Composable
fun AdminClubMembershipScanScreen(navController: NavHostController) {
val context = LocalContext.current
var scanFlag by remember {
mutableStateOf(false)
}
val compoundBarcodeView = remember {
CompoundBarcodeView(context).apply {
val capture = CaptureManager(context as Activity, this)
capture.initializeFromIntent(context.intent, null)
this.setStatusText("")
capture.decode()
this.decodeContinuous { result ->
if(scanFlag){
return#decodeContinuous
}
scanFlag = true
result.text?.let { barCodeOrQr->
//Do something and when you finish this something
//put scanFlag = false to scan another item
scanFlag = false
}
//If you don't put this scanFlag = false, it will never work again.
//you can put a delay over 2 seconds and then scanFlag = false to prevent multiple scanning
}
}
}
AndroidView(
modifier = Modifier,
factory = { compoundBarcodeView },
)
}
Since zxing-android-embedded:4.3.0 there is a ScanContract, which can be used directly from Compose:
val scanLauncher = rememberLauncherForActivityResult(
contract = ScanContract(),
onResult = { result -> Log.i(TAG, "scanned code: ${result.contents}") }
)
Button(onClick = { scanLauncher.launch(ScanOptions()) }) {
Text(text = "Scan barcode")
}
Addendum to the accepted answer
This answer dives into the issues commented on by #Bharat Kumar and #Jose Pose S
in the accepted answer.
I basically just implemented the accepted answer in my code and then added the following code just after the defining compundBarCodeView
DisposableEffect(key1 = "someKey" ){
compoundBarcodeView.resume()
onDispose {
compoundBarcodeView.pause()
}
}
this makes sure the scanner is only active while it is in the foreground and unbourdens our device.
TL;DR
In escence even after you scan a QR code successfully and leave the scanner screen, the barcodeview will "haunt" you by continuing to scan from the backstack. which you usually dont want. And even if you use a boolean flag to prevent the scanner from doing anything after the focus has switched away from the scanner it will still burden your processor and slow down your UI since there is still a process constantly decrypting hi-res images in the background.
I have a problem, I've the same code as you, but i don't know why it's showing me a black screen
Code AddProduct
#ExperimentalPermissionsApi
#Composable
fun AddProduct(
navController: NavController
) {
val context = LocalContext.current
var scanFlag by remember {
mutableStateOf(false)
}
val compoundBarcodeView = remember {
CompoundBarcodeView(context).apply {
val capture = CaptureManager(context as Activity, this)
capture.initializeFromIntent(context.intent, null)
this.setStatusText("")
capture.decode()
this.decodeContinuous { result ->
if(scanFlag){
return#decodeContinuous
}
scanFlag = true
result.text?.let { barCodeOrQr->
//Do something
}
scanFlag = false
}
}
}
AndroidView(
modifier = Modifier.fillMaxSize(),
factory = { compoundBarcodeView },
)
}

VSCode extension - how to alter file's text

I have an extension that grabs the open file's text and alters it. Once the text is altered, how do I put it back into the file that is displayed in VSCode?
// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
export function activate(context: vscode.ExtensionContext) {
// Use the console to output diagnostic information (console.log) and errors (console.error)
// This line of code will only be executed once when your extension is activated
console.log('Congratulations, your extension "myExtension" is now active!');
console.log(process.versions);
// The command has been defined in the package.json file
// Now provide the implementation of the command with registerCommand
// The commandId parameter must match the command field in package.json
let disposable = vscode.commands.registerCommand('extension.myExtension', () => {
// The code you place here will be executed every time your command is executed
let activeEditor = vscode.window.activeTextEditor;
if (!activeEditor) {
return;
}
let text = activeEditor.document.getText();
getAsyncApi(text).then((textToInsertIntoDoc) => {
let finaldoc = insertTextIntoDoc(text, textToInsertIntoDoc);
// not what I want - just used to see new text
vscode.window.showInformationMessage(textToInsertIntoDoc);
});
});
context.subscriptions.push(disposable);
}
The API you can use here is TextEditor.edit, whose definition is
edit(callback: (editBuilder: TextEditorEdit) => void, options?: { undoStopBefore: boolean; undoStopAfter: boolean; }): Thenable<boolean>;
It asks for a callback as the first parameter and in the callback, you can make edits to the document by visiting editBuilder.
I put a sample extension in https://github.com/Microsoft/vscode-extension-samples/tree/master/document-editing-sample which reverses the content in current selection, which is basically a simple use TextEditor.edit.
This is a revision of the main function in Rebornix's extension sample (included with the set of Microsoft extension samples) that handles the selection issues you raised. It reverses the content of the selection(s) (leaving the selections) or if a selection is empty it will reverse the word under the cursor at that selection without leaving anything selected. It often makes sense to leave a selection, but you can add code to remove selection.
let disposable = vscode.commands.registerCommand('extension.reverseWord', function () {
// Get the active text editor
const editor = vscode.window.activeTextEditor;
if (editor) {
const document = editor.document;
editor.edit(editBuilder => {
editor.selections.forEach(sel => {
const range = sel.isEmpty ? document.getWordRangeAtPosition(sel.start) || sel : sel;
let word = document.getText(range);
let reversed = word.split('').reverse().join('');
editBuilder.replace(range, reversed);
})
}) // apply the (accumulated) replacement(s) (if multiple cursors/selections)
}
});
Admittedly, while I could remove a single selection by setting .selection to a new empty selection that doesn't seem to work with .selections[i]. But you can make multiple changes without having selections in the first place.
What you don't want to do is make a selection through code just to alter text through code. Users make selections, you don't (unless the end purpose of the function is to make a selection).
I came to this question looking for a way to apply a textEdit[] array (which is normally returned by a provideDocumentRangeFormattingEdits callback function). If you build changes in the array you can apply them to your document in your own function:
const { activeTextEditor } = vscode.window;
if (activeTextEditor) {
const { document } = activeTextEditor;
if (document) {
/*
build your textEdits similarly to the above with insert, delete, replace
but not within an editBuilder arrow function
const textEdits: vscode.TextEdit[] = [];
textEdits.push(vscode.TextEdit.replace(...));
textEdits.push(vscode.TextEdit.insert(...));
*/
const workEdits = new vscode.WorkspaceEdit();
workEdits.set(document.uri, textEdits); // give the edits
vscode.workspace.applyEdit(workEdits); // apply the edits
}
}
So that's another way to apply edits to a document. Even though I got the editBuilder sample to work correctly without selecting text, I have had problems with selections in other cases. WorkspaceEdit doesn't select the changes.
Here is the code snippet that will solve your issue :
activeEditor.edit((selectedText) => {
selectedText.replace(activeEditor.selection, newText);
})
Due to the issues I commented about in the above answer, I ended up writing a quick function that does multi-cursor friendly insert, and if the selection was empty, then it does not leave the inserted text selected afterwards (i.e. it has the same intuitive behavior as if you had pressed CTRL + V, or typed text on the keyboard, etc.)
Invoking it is simple:
// x is the cursor index, it can be safely ignored if you don't need it.
InsertText(x => 'Hello World');
Implementation:
function InsertText(getText: (i:number) => string, i: number = 0, wasEmpty: boolean = false) {
let activeEditor = vscode.window.activeTextEditor;
if (!activeEditor) { return; }
let sels = activeEditor.selections;
if (i > 0 && wasEmpty)
{
sels[i - 1] = new vscode.Selection(sels[i - 1].end, sels[i - 1].end);
activeEditor.selections = sels; // required or the selection updates will be ignored! 😱
}
if (i < 0 || i >= sels.length) { return; }
let isEmpty = sels[i].isEmpty;
activeEditor.edit(edit => edit.replace(sels[i], getText(i))).then(x => {
InsertText(getText, i + 1, isEmpty);
});
}
let strContent = "hello world";
const edit = new vscode.WorkspaceEdit();
edit.insert(YOUR_URI, new vscode.Position(0, 0), strContent);
let success = await vscode.workspace.applyEdit(edit);

Gui.Window won't show in Unity

I have some code like follwing:
if( GUI.Button(
new Rect(Screen.width/4f,50f,Screen.width/2f,Screen.height/9f),"RessetLevel")) {
showingWinsows = true;
if(showingWinsows)
{
rectWindow = GUI.Window(
0,
rectWindow,
DoMyWindow,
"Are you sure you want to reset All level ?"
);
}
I've insert the userGuiLayout = false; in my Awake() function. But the window still doesn't show. How do I fix this?
The problem is that the window is only drawn when the button is pressed.
You need to move the code that checks the flag out of the if( GUI.Button ), something like this:
if( GUI.Button(new Rect(Screen.width/4f,50f,Screen.width/2f,Screen.height/9f),"Resset Level"))
{
showingWinsows = true;
}
if(showingWinsows)
{
rectWindow = GUI.Window(0,rectWindow,DoMyWindow,"Are you sure you want to reset All level ?");
}

Update OpenLayers popup

I am trying to update some popups in my map but I am not able to do that.
Firstly I create some markers, and with the next code, I create a popup associated to them. One popup for each marker:
popFeature = new OpenLayers.Feature(markers, location);
popFeature.closeBox = true;
popFeature.popupClass = OpenLayers.Class(OpenLayers.Popup.FramedCloud, {
'autoSize': true
});
popFeature.data.popupContentHTML = "hello";
popFeature.data.overflow = (false) ? "auto" : "hidden";
var markerClick = function (evt) {
if (this.popup == null) {
this.popup = this.createPopup(this.closeBox);
map.addPopup(this.popup);
this.popup.show();
} else {
this.popup.toggle();
}
currentPopup = this.popup;
OpenLayers.Event.stop(evt);
};
mark.events.register("mousedown", popFeature, markerClick);
After that, I add the new marker to my marker layer.
Everything is fine until here, but, I want to update the popupcontentHTML some time later and I don't know how I can access to that value.
I read OL API but I don't understand how to get it. I am lost about features, events, extensions...
I want to know if I can access to that property and write other word.
I answer myself, maybe it helps other people in future:
for(i = 0; i < map.popups.length; i++){
if(map.popups[i].lonlat.lon == marker.lonlat.lon){
map.popups[i].setContentHTML("new content");
}
}
Content will be refreshed at the moment.