javascriptgnome-shell-extensionsgjs

Want add variables to PopupMenu class and save object in array in gjs


I want store two variable with PopupMenu.PopupImageMenuItem object, and push them into an array, then want change the text of those popupmenuitems later every 10 seconds.

        var list = new Array();

        function add_timer (){
            //~ return function(){
            let d = parseInt(input.text);
            if(isNaN(d) || d < 1){return;}
            ...
            PopupMenu.PopupImageMenuItem.prototype.count = d;
            PopupMenu.PopupImageMenuItem.prototype.left = d;
            let item = new PopupMenu.PopupImageMenuItem(text, stock_icon.icon_name);
            ...
            that.menu.addMenuItem(item);
            list.push(item);
                var i = item;   log(i.count+' <====== '+i.left);
<======== here I got last input number is right one each time.
2 <====== 2
45 <====== 45
<=========================================
            //~ }
        }
//~ ---------------------------------------------------------
            GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 10, () => {
                log("======"+list.length+"======");
                list.forEach((i)=>{
                    log(i.text +': '+ i.count+' <--- '+i.left);
<========= here I got undefined and error number. all number is last input one.
======2======
undefined: 45 <--- 45
undefined: 45 <--- 45
<======================
                })
                return GLib.SOURCE_CONTINUE;    //true
            });

It's like a closure problem, or error used prototype? or push works error? if the item in array is undefined, how can I get a list of PopupImageMenuItem object. My first time write js, so maybe it foolish problem. Please help.


Solution

  • This seems like a bit of confusion with prototypes.

    // This is a class
    const MyClass = class {
        constructor() {
        }
    };
    
    // This is an object (class instance)
    let myObject = new MyClass();
    
    // This is how you set properties on an object
    myObject.something = 32;
    
    
    // This is how you set properties on a class
    MyClass.prototype.something = 64;
    
    // Now all new objects will have "something"
    let myOtherObject = new MyClass();
    
    if (myOtherObject.something === 64)
        log('True!');
    
    
    // If you change the prototype, all new and current
    // objects will have the new value
    MyClass.prototype.something = 128;
    
    if (myOtherObject.something === 128)
        log('True!');
    
    if (new MyClass().something === 128)
        log('True!');
    

    Probably you want to do something like this:

    // Pro-tip: never use `var` unless you know you have to.
    //
    // Use `let` if you need to re-assign the variable later,
    // like `list = <something else>`, otherwise use `const`.
    const list = [];
    
    function add_timer() {
        const d = Number.parseInt(input.text);
        
        if (Number.isNaN(d) || d < 1)
            return;
    
        const item = new PopupMenu.PopupImageMenuItem(text, stock_icon.icon_name);
        item.count = d;
        item.left = d;
        that.menu.addMenuItem(item);
    
        // Remove the item from the list if it is destroyed
        list.push(item);
    
        item.connect('destroy', (actor) => {
            list.splice(list.indexOf(actor), 1);
        });
    }
    
    // Save the timer ID somewhere so you can remove it later
    let timerId = GLib.timeout_add_seconds(10, () => {
        // Or maybe remove the timer when there are no items
        if (list.length === 0) {
            timerId = 0;
            return GLib.SOURCE_REMOVE;
        }
    
        // NOTE: use `of`, not `in` like Python
        for (const item of list)
            log(`${item.label.text}: ${item.count} <--- ${item.left}`);
    
        return GLib.SOURCE_CONTINUE;
    });
    
    // To remove the timeout source by ID
    if (timerId > 0)
        GLib.Source.remove(timerId);