GNOME Shell Extension Button - javascript

I'm trying to write an extension with just using JavaScript. I wrote it with Python through Hello World! code. But, yet in the beginning, my button for menu items is not working. Also, I couldn't add menu item with Hello World! code. I think, I miss something.
The button code is here:
const Lang = imports.lang;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const St = imports.gi.St;
const TimeButton = new Lang.Class({
Name: "Salah Time",
Extends: PanelMenu.Button,
_init: function () {
let box = new St.BoxLayout({
style_class: "system-status-icon"
});
let label = new St.Label({text: "Salah Time"});
box.add_child(label);
this.actor.addActor(box);
}
});
function init() {
}
function enable() {
let but = new TimeButton();
Main.panel._leftBox.insert_child_at_index(but, 1);
}
function disable() {
Main.panel._leftBox.remove_child(but);
}
There is no many tutorial for GJS. I'm already trying to write by reading other extensions.
Thanks.

const Lang = imports.lang;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const St = imports.gi.St;
const TimeButton = new Lang.Class({
Name: "TimeButton",
Extends: PanelMenu.Button,
_init: function () {
this.parent(null, "TimeButton");
// Icon
this.icon = new St.Icon({
icon_name: "appointment-symbolic",
style_class: "system-status-icon"
});
this.actor.add_actor(this.icon);
// Menu
this.menuItem = new PopupMenu.PopupMenuItem("Salah Time", {});
this.menu.addMenuItem(this.menuItem);
}
});
function init() {
}
function enable() {
let indicator = new TimeButton();
Main.panel.addToStatusArea("should-be-a-unique-string", indicator);
// hide
Main.panel.statusArea["should-be-a-unique-string"].actor.visible = false;
// change icon
Main.panel.statusArea["should-be-a-unique-string"].icon.icon_name = "appointment-soon-symbolic";
// show
Main.panel.statusArea["should-be-a-unique-string"].actor.visible = true;
}
function disable() {
// you could also track "indicator" and just call indicator.destroy()
Main.panel.statusArea["should-be-a-unique-string"].destroy();
}
Hope that helps someone (if you aren't around anymore).

Related

Trying to use p5 to change states after pressing button, but nothing happens after clicking the button

I am trying to create a function that starts out with a report button and, if you click it, the page will redirect you to where you can type some words into the text box and send or you have a cancel button. If you type in a comment and press the send button then whatever you typed will show up. However, if you click the cancel button it will return you to a page that just has the report button.
While I am certain my code so far is incorrect, my main question is why doesn't anything happen after I click the report button? I based my code off this post.
Here is my code:
let reportButton;
let cancelButton;
let reporting;
let button;
//let input;
const scenes = {
report: () => {
cancelButton?.remove();
// initialize idle state
reporting = false;
reportButton = createButton("Report");
reportButton.position(5, 5);
reportButton.mousePressed = () => {
scenes.reportComment();
};
draw = () => {
clear();
};
},
reportComment: () => {
reportButton?.remove();
reporting = true;
cancelButton = createButton("Cancel");
cancelButton.position(5, 5);
cancelButton.mousePressed = () => {
scenes.report();
};
draw = () => {
clear();
input = createInput();
input.position(20, 65);
button = createButton('Submit');
button.position(input.x + input.width, 65);
button.mousePressed(sendReport);
question = createElement('h2', 'Report');
question.position(20, 5);
textAlign(CENTER);
textSize(50);
};
},
};
function sendReport() {
const customQ = input.value();
question.html(customQ);
input.value('');
}
function setup() {
createCanvas(500, 100);
textSize(20);
}
function draw() {
scenes.report();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>
Your code (overwriting p5's mousePressed method with a new, unrelated function):
reportButton.mousePressed = () => {
scenes.reportComment();
};
Correct code (calling p5's mousePressed method, passing in a callback that represents the handler to trigger on press):
reportButton.mousePressed(() => {
scenes.reportComment();
});
It's a subtle difference, but a close look at the example in the createButton docs shows the way.
Manipulating p5.js objects usually involves method calls rather than setting object properties with =, in case this helps you "smell" the pattern easier in the future. You may have been misled by the look of draw = () => {} and mousePressed = () => {}, but these overwrite window variables, replacing function draw() and function mousePressed() to change the main loop that p5 runs and the global mouse press handler, respectively. That's a different case.
A good debugging strategy is to eliminate all unneeded code until all that's left is the following minimal reproduction:
function setup() {
const b = createButton("Report");
b.position(10, 10);
b.mousePressed = () => console.log("worked!");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>
After removing all distractions having to do with scenes, it's much easier to figure out that mousePressed is being used incorrectly.
Your other handler, cancelButton.mousePressed, should also be adjusted.
After you fix your handlers, you'll probably notice that you can't type into your input. The reason is that it's being created many times per second because it's in the draw() loop:
draw = () => {
// repeat this code many times a second
clear();
input = createInput();
input.position(20, 65);
// ...
Instead, put your element-creation code outside draw() so it's executed one time. For each scene, that top-level code is basically setup() for that scene. draw() is generally for animation, moving objects around the screen, and occasionally creating and destroying objects as the animation requires. Your current app doesn't need any draw()s at the current time, so I'd remove them to avoid confusion.
clear() clears the canvas, not DOM elements, so it's also not necessary in your app yet. Ditto for textAlign(CENTER); and textSize(50);.
Putting it all together:
const scenes = {
report: () => {
const reportButton = createButton("Report");
reportButton.position(5, 5);
reportButton.mousePressed(() => {
reportButton.remove();
scenes.reportComment();
});
},
reportComment: () => {
const cleanup = () => {
input.remove();
cancelButton.remove();
submitButton.remove();
header.remove();
};
const transitionToReport = () => {
cleanup();
scenes.report();
};
const transitionToSendReport = () => {
const value = input.value();
cleanup();
scenes.sendReport(value);
};
const cancelButton = createButton("Cancel");
cancelButton.position(5, 5);
cancelButton.mousePressed(transitionToReport);
const input = createInput();
input.position(20, 65);
const submitButton = createButton("Submit");
submitButton.position(input.x + input.width, 65);
submitButton.mousePressed(transitionToSendReport);
const header = createElement("h2", "Report");
header.position(20, 5);
},
sendReport: customQ => {
const header = createElement("h2", `report: '${customQ}'`);
header.position(20, 5);
},
};
function setup() {
scenes.report();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>
Or keeping it to two scenes:
const scenes = {
report: () => {
const reportButton = createButton("Report");
reportButton.position(5, 5);
reportButton.mousePressed(() => {
reportButton.remove();
scenes.reportComment();
});
},
reportComment: () => {
const transitionToReport = () => {
input.remove();
cancelButton.remove();
submitButton.remove();
header.remove();
scenes.report();
};
const cancelButton = createButton("Cancel");
cancelButton.position(5, 5);
cancelButton.mousePressed(transitionToReport);
const input = createInput();
input.position(20, 65);
const submitButton = createButton("Submit");
submitButton.position(input.x + input.width, 65);
const sendReport = () =>
header.elt.textContent = input.value();
submitButton.mousePressed(sendReport);
const header = createElement("h2", "Report");
header.position(20, 5);
},
};
function setup() {
scenes.report();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>
Note that I'm locally-scoping all variables to each scene. Each scene is responsible for cleaning up its own elements when transitioning to another scene. This helps manage complexity as the app grows.
UX isn't great since the user should be able to press Enter to transition to the next scene, and there should probably be some validation that the input has some content in it, but that's out of scope for this answer.

Mouse click not working in button added in DOM

i am trying to add this code in my code where i want to have this button in my scene !
function performAnnotation(event: MouseEvent) {
console.log(event)
console.log('hello')
}
function loadAnnotationIntoScene() {
const menuPanel = document.getElementById('menu-panel') as HTMLDivElement
const _menuList = document.createElement('ul') as HTMLUListElement
menuPanel.appendChild(_menuList)
annotation.forEach((element, index) => {
const myList = document.createElement('li') as HTMLLIElement
const _list = _menuList.appendChild(myList)
const myButton = document.createElement('button') as HTMLButtonElement
myList.appendChild(myButton)
myButton.classList.add('annotationButton')
myButton.innerHTML = element['text']
myButton.addEventListener('click', performAnnotation, false)
})
}
i am trying to create an annotation menu above the scene in right or left hand side but i am not able to add button with event listener !!!
When i try in fiddle it works fine but in three.js, it doesn't and i believe this is because of the orbit controller that i have in my scene !!
Right now my scene is like this:
function performAnnotation(event) {
console.log("hello")
}
const annotation = [1, 2, 3, 4]
function loadAnnotationIntoScene() {
const menuPanel = document.getElementById('menu-panel')
const _menuList = document.createElement('ul')
menuPanel.appendChild(_menuList)
annotation.forEach((element, index) => {
const myList = document.createElement('li')
const _list = _menuList.appendChild(myList)
const myButton = document.createElement('button')
myButton.innerHTML = "hello"
myList.appendChild(myButton)
myButton.addEventListener('click', performAnnotation, false)
})
}
loadAnnotationIntoScene()
<div id="menu-panel"></div>
can any one suggest me what is the best thing that i can do so that i can have annotation menu in my project !!!

How to make a function change only the first array element

I'm quite new to JavaScript, and I'm stuck with an issue which I didn't manage to find an answer for.
I have an array, each item of which is tied to a card in markup.
When a button is pressed, a form pops up and allows to enter name and url for the new image, which is then unshifted to thebeginning of the array.
The last element of the array is popped, so there's always the same number of elements.
And the issue itself is:
When I add a single card, it works perfect: last card is popped and the new one is tucked in the beginning.
But when I use and submit the form again, the card that I have previously edited changes as well, although it should not.
I suspect that the mistake lies in the function that updates the cards, but I'm all out of ideas how to fix it.
Is there a way to fix that function?
const formAdd = document.querySelector('.popup__form-add')
const newElement = { name: '', link: '' };
const blank = { name: '', link: '' };
const placeName = document.querySelector('.popup__edit_type_place-name');
const placeImage = document.querySelector('.popup__edit_type_place-picture');
const elements = document.querySelector('.elements');
const cards = elements.querySelectorAll('.element');
const buttonAdd = document.querySelector('.profile__button-add');
const buttonAddClose = document.querySelector('.popup__add-button-close');
//content popup functions
function popupAddDisplay() {
popupAdd.classList.add('popup_opened');
}
function popupAddHide() {
popupAdd.classList.remove('popup_opened');
}
function contentUpdate() {
event.preventDefault();
newElement.name = placeName.value;
newElement.link = placeImage.value;
console.log(newElement);
initialCards.pop();
initialCards.unshift(blank);
blank.name = newElement.name;
blank.link = newElement.link;
console.log(initialCards);
cardsCheck();
popupAddHide();
};
//cards array, page content loads from here
const initialCards = [{
name: 'Архыз',
link: 'https://pictures.s3.yandex.net/frontend-developer/cards-compressed/arkhyz.jpg'
},
{
name: 'Челябинская область',
link: 'https://pictures.s3.yandex.net/frontend-developer/cards-compressed/chelyabinsk-oblast.jpg'
},
{
name: 'Иваново',
link: 'https://pictures.s3.yandex.net/frontend-developer/cards-compressed/ivanovo.jpg'
},
{
name: 'Камчатка',
link: 'https://pictures.s3.yandex.net/frontend-developer/cards-compressed/kamchatka.jpg'
},
{
name: 'Холмогорский район',
link: 'https://pictures.s3.yandex.net/frontend-developer/cards-compressed/kholmogorsky-rayon.jpg'
},
{
name: 'Байкал',
link: 'https://pictures.s3.yandex.net/frontend-developer/cards-compressed/baikal.jpg'
}
];
//compares info from the markup with the array, then pushes array info into markup
function cardsCheck() {
cards.forEach(function(item, index) {
const nameAlt = initialCards[index].name;
const imageSrcAlt = initialCards[index].link;
item.querySelector('.element__name').textContent = nameAlt;
item.querySelector('.element__image').src = imageSrcAlt;
if (nameAlt == '') {
item.style.display = 'none'
}
console.log(nameAlt);
});
}
document.onload = cardsCheck();
buttonAdd.addEventListener('click', () => {
popupAddDisplay()
});
buttonAddClose.addEventListener('click', popupAddHide);
formAdd.addEventListener('submit', contentUpdate);```
You always use the same blank object. So at the end you have the same object multiple times in the array. If you change an attribute of this object it is changed wherever it is in the list also.
To avoid this you need to create a new object before adding it to the array
function contentUpdate() {
event.preventDefault();
newElement.name = placeName.value;
newElement.link = placeImage.value;
console.log(newElement);
initialCards.pop();
let blank = { name: '', link: '' };
initialCards.unshift(blank);
blank.name = newElement.name;
blank.link = newElement.link;
console.log(initialCards);
cardsCheck();
popupAddHide();
};
Every time you create a new card, it seems like you override the global variables blank and newElement.
However, you don't have to. You could create a local variable in your contentUpdate function called newElement.
That way, each time updateContent is called, a new local variable is created.
You could therefore try the following code:
const formAdd = document.querySelector('.popup__form-add')
const placeName = document.querySelector('.popup__edit_type_place-name');
const placeImage = document.querySelector('.popup__edit_type_place-picture');
const elements = document.querySelector('.elements');
const cards = elements.querySelectorAll('.element');
const buttonAdd = document.querySelector('.profile__button-add');
const buttonAddClose = document.querySelector('.popup__add-button-close');
//content popup functions
function popupAddDisplay() {
popupAdd.classList.add('popup_opened');
}
function popupAddHide() {
popupAdd.classList.remove('popup_opened');
}
function contentUpdate() {
event.preventDefault();
const newElement = {}
newElement.name = placeName.value;
newElement.link = placeImage.value;
console.log(newElement);
initialCards.pop();
initialCards.unshift(newElement);
console.log(initialCards);
cardsCheck();
popupAddHide();
}

How to access 'this' from within an addEventListener? [duplicate]

This question already has answers here:
"this" keyword in event methods when using JavaScript prototype object
(4 answers)
Closed 2 years ago.
I'm wanting to access this from within an addEventListener.
With my example below I want to output this.settings.car when a div is clicked.
I could write var that = this within the this.init = function() { //here } and access it using that.settings.car, however if I run multiple instances of this code then that will always reference the last instance.
Else I could rewrite the addEventListener to be:
this.settings.wrap.addEventListener('click', function (e) => {
console.log(this.settings.car)
})
and that'll work, however this will lead to me writing lots of duplicate code instead of 1 function.
I've been scratching my head at this for a while now. How can I access this from within the this.test function?
var gauge = (function() {
return function() {
this.settings = {
car: undefined,
}
this.init = function(e) {
this.settings.wrap = document.querySelector(e.wrapper)
this.settings.car = this.settings.wrap.innerText
this.settings.wrap.addEventListener('click', this.test)
}
this.test = function(e) {
console.log('clicked')
console.log(this.settings.car)
}
}
}())
// init
let boy = new gauge()
boy.init({
wrapper: '.boy'
})
let girl = new gauge()
girl.init({
wrapper: '.girl'
})
div {display:block;background:grey;margin:20px;height:200px;width:200px;color:white;text-align:center;line-height:200px;}
<div class="boy">ford</div>
<div class="girl">ferrari</div>
Here's a jsbin of the issue: https://jsbin.com/xayuheyime/1/edit?html,js,console,output
You need to use Function.prototype.bind to set the this value.
this.settings.wrap.addEventListener('click', this.test.bind(this))
var gauge = (function() {
return function() {
this.settings = {
car: undefined,
}
this.init = function(e) {
this.settings.wrap = document.querySelector(e.wrapper)
this.settings.car = this.settings.wrap.innerText
this.settings.wrap.addEventListener('click', this.test.bind(this))
}
this.test = function(e) {
console.log('clicked')
console.log(this.settings.car)
}
}
}())
// init
let boy = new gauge()
boy.init({
wrapper: '.boy'
})
let girl = new gauge()
girl.init({
wrapper: '.girl'
})
div.boy, div.girl {display:block;background:grey;margin:20px;height:200px;width:200px;color:white;text-align:center;line-height:200px;}
<div class="boy">ford</div>
<div class="girl">ferrari</div>
Arrow function seems fine here. Another way is how you suggested, use var that = this before your this.init function, and use that on your event handler call.
var gauge = (function() {
return function() {
this.settings = {
car: undefined,
}
this.init = (e) => {
this.settings.wrap = document.querySelector(e.wrapper)
this.settings.car = this.settings.wrap.innerText
this.settings.wrap.addEventListener('click', this.test)
}
this.test = () => {
console.log('clicked')
console.log(this.settings.car)
}
}
}())
// init
let boy = new gauge()
boy.init({
wrapper: '.boy'
})
let girl = new gauge()
girl.init({
wrapper: '.girl'
})

can't figure out with constructors javascript

My code does not work correctly, I can not understand why. when you transfer the card to the shoop, he does not add money to it
function Card (money=0) {
this.money = money;
}
Card.prototype.addMoney = function(mon) {
return this.money=this.money+mon;
}
function CreditCard (money =0) {
this.money = money;
}
CreditCard.prototype = Object.create(Card.prototype);
function Shop (card) {
this.card = card
this.money=card.money;
this.addMoney=this.card.addMoney;
}
Shop.prototype = Object.create(Card.prototype);
let card1 = new CreditCard(50);
card1.addMoney(10);//60
card1.addMoney(10);//70
let card2 = new Card(250);
let shop1 = new Shop(card1);
shop1.addMoney(10);//80 but don't work
console.log(card1.money);
this.addMoney = this.card.addMoney;
This is the line with the issue. The function internally references this.money, but since the function is now inside the Shop class, this changes to refer to the shop. So, you have to bind the function to the card. The new line would be:
this.addMoney = this.card.addMoney.bind(this.card);
A shop is not a card. You should not use inheritance here. A shop has a card, so go for composition:
function Shop (card) {
this.companyCard = card
}
Shop.prototype.addMoney = function(mon) {
this.companyCard.addMoney(mon);
};
Shop.prototype.getMoney = function() {
return this.companyCard.money;
};

Categories