Re: Getting a St.Bin to refresh its position?



Hi all,

thought I'd get back to you on this - I still haven't been able to
solve the problem and have come up with a small working example for
others to play with. (I've been ignoring the problem for a while but
it's got to the point where most my remaining bugs depend on me fixing
this one).

Reminder of the problem:

Shell.GenericContainer (applet) containing St.Bins (workspace)
containing St.Group/St.Widget (container for windows in that
workspace) containing St.Bin (windows).

Window St.Bin size/position is bound to the actual window position,
but whenever I update it, I don't *see* the changes unless I call
Shell.GenericContainer.queue_relayout(), which is wrong.

I've attached some code showing this problem or you can find it here:
http://pastebin.com/A7dDWtqx

In Window.Clone (the St.Bin for each window), I call
Shell.GenericContainer.queue_relayout in the size-changed but *not*
the position-changed event, so if you move a window around you will
*not* see the position updated, but if you resize a window it will be
updated.

If anyone knows what the problem is please tell me! (there's also
another problem that the hpadding of the panel button appears not to
be applied - do I have to take care of that myself within _allocate or
_getPreferredWidth? I thought since I subclassed PanelMenu.Button, the
parent code would take care of the this._minHPadding stuff).

(Also, the code is just the bare minimum to demonstrate my problem, so
it doesn't yet tidy up its own signals on disable, etc).

thanks!

(Yaa101 - thanks for your suggestion! But if I have the entire applet
load/unload every time I move a window on any of the workspaces, I'll
be doing it far too often).

On 1 September 2012 10:40, Yaa101 <yaa101 xs4all nl> wrote:
> Hi Amy,
>
> Call me simple, but the most simple solution for my extension was to
> simply unload and load (reload) all widgets, this is so fast that it is
> not noticable by eye, it enables me to have it reload in reaction to a
> prefs database change. For instance, when I resize the iconsize of my
> St.Bin's I can see them grow/schrink live.
>
> So: extension loads icon with prefs size --> you change pref size
> in prefs.js --> extension reacts on event signal of pref database
> change, either common or specific signal --> it reloads and thus has
> the new prefs in place when loading.
>
> b.t.w. I also have a Shell.GenericContainer() with St.Bin's as widget
> interface.
>
>
> --
> (o_
> //\  Regards, Groeten,
> V_/_ Bas Burger.
>
>
> On Fri, 31 Aug 2012 16:00:45 +1000
> Amy <mathematical coffee gmail com> wrote:
>
>> Hi all,
>>
>> I'm trying to make a workspace switcher like the gnome-panel 2.X one
>> for gnome-shell: it's very much like the workspaces sidebar in the
>> overview, except that it lives in your top panel, only window icons
>> are shown (as opposed to the window thumbnail) and the desktop
>> background is not shown. So basically you get a real-time graphical
>> representation of the windows in each workspace, where each window is
>> really just a rectangle.
>>
>> In essence my problem is that I have a WindowClone class (a
>> rectangle), and although I update its x and y position, I do not
>> *visually* see the rectangle change position like it should.
>> I want to do `windowClone.x = newx; windowClone.y = newy;` and see the
>> results instantly.
>>
>> Further detail:
>>
>> I have a Shell.GenericContainer() which sits in the top panel and
>> contains all the workspaces.
>>
>> Each workspace is a St.Bin that takes up whatever space the
>> Shell.GenericContainer() allocates it.
>>
>> The child of each workspace St.Bin is a St.Group.
>>
>> In each workspace, I have one WindowClone per window in the workspace.
>> A WindowClone is a (styled) St.Bin that tracks the
>> position/size/minimized state of its underlying window (very much like
>> the WindowClone classes in workspaceThumbnail.js and workspace.js).
>>
>> So, what happens is that the WindowClone tracks the size-changed and
>> position-changed events of its base window, and when that happens, it
>> sets the St.Bin's x, y, width and height accordingly.
>>
>> My problem is that although the x/y/width/height get updated, I do not
>> *see* the change - the St.Bin that corresponds to my window does not
>> appear to change position within its workspace.
>>
>> The only way I've found to get around this is to call
>> .queue_relayout() on the Shell.GenericContainer that contains the
>> workspaces (that contains the windows).
>>
>> This means each window has to store a reference to the
>> great-grandparent Shell.GenericContainer so that whenever a
>> size-changed or position-changed event fires, I can call
>>
>>     this.grandParent.queue_relayout()
>>
>> so that I can visually see the window St.Bin change its position.
>>
>> My questions are:
>> * should this be happening? in workspaceThumbnails.js, the
>> ThumbnailsBox is a Shell.GenericContainer containing
>> WorkspaceThumbnails (a Clutter.Group in a St.Group) containing
>> WindowClones (Clutter.Clone). When these WindowClones update their
>> position they do *not* have to call .queue_relayout() on the
>> Shell.GenericContainer, and their position is visually updated. So why
>> do I have to?
>> * if this *should* be happening, is the only way for me to get the
>> desired effect to store a pointer to the Shell.GenericContainer in
>> each WindowClone so that I can call .queue_relayout() on the
>> appropriate window events? It doesn't seem right that the WindowClone
>> should have to know about its great-grandparent in order to be
>> displayed correectly?
>> * is there a way for me to call WindowClone.propogate_queue_relayout()
>> such that the queue_relayout() request gets relayed up all the way to
>> the Shell.GenericContainer without me having to store the pointer
>> myself? This seems like a better way for it to be done (and calling
>> .queue_relayout() on the Window St.Bin and/or the Workspace
>> St.Group/St.Bin does not have the desired effect; only calling it on
>> the Shell.GenericContainer seems to cause the window St.Bins to be
>> repainted).
>>
>> I notice that if I add a St.Bin directly to (say) global.stage,
>> updating its x/y causes *instant* relayout, so it must be something to
>> do with the way I've parented everything. I suspect the
>> Shell.GenericContainer.
>>
>> cheers.
>> _______________________________________________
>> gnome-shell-list mailing list
>> gnome-shell-list gnome org
>> https://mail.gnome.org/mailman/listinfo/gnome-shell-list
>
/*
The setup:

An indicator in the panel (Shell.GenericContainer), containing
- workspaces (St.Bin containing a St.Group/St.Widget (depending on GNOME 3.2 or 3.4))
  containing:
  - windows (St.Bin)

The window St.Bins adjust their size/position with their actual windows, and although this works, I **do not see the St.Bins move accordingly unless I call queue_relayout() on the Shell.GenericContainer**. Calling .queue_relayout() on anything else has no effect, and ommitting any .queue_relayout() call also has no effect (i.e. St.Bin doesn't move).

To demonstrate this I've called .queue_relayout() on the size-changed signal and not called it in the position-changed signal, so when you move windows around you will not see the St.Bins move, but when you change their size the St.Bins will be updated. (in the WindowClone class)

How can I fix this? I shouldn't have to store a pointer to the SHell.GenericContainer *within* the lowest-level St.Bin simply to call .queue_relayout?

*/

/////     CODE     ////
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const St = imports.gi.St;

const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;

/* Compatibility for dev version (remove later) */
const StGroup = St.Group || St.Widget;

/* Extension variables */
let indicator;

// the indicator in the top panel
function WorkspaceApplet() {
    this._init.apply(this, arguments);
}

WorkspaceApplet.prototype = {
    __proto__: PanelMenu.Button.prototype,

    _init: function () {
        PanelMenu.Button.prototype._init.call(this,
                St.Align.START,       // menu alignment
                'workspace-inicator'  // text
        );

        this._container = new Shell.GenericContainer({
            reactive: true
        });
        this._container.connect('get-preferred-width',
                Lang.bind(this, this._getPreferredWidth));
        this._container.connect('get-preferred-height',
                Lang.bind(this, this._getPreferredHeight));
        this._container.connect('allocate', Lang.bind(this, this._allocate));

        this.actor.add_actor(this._container);


        // add a "workspace" actor.
        let workspace = new WorkspaceThumbnail(0);
        this._container.add_actor(workspace.actor);
    },

    //// allocation ////

    _getPreferredHeight: function (actor, forWidth, alloc) {
        alloc.min_size = alloc.natural_size = Main.panel.actor.height;
    },

    _getPreferredWidth: function (actor, forHeight, alloc) {
        alloc.min_size = alloc.natural_size = Main.panel.actor.height * global.stage.width / global.stage.height;
    },

    _allocate: function (actor, box, flags) {
        let children = this._container.get_children();

        // just allocate the whole space
        children[0].allocate(box, flags);
    }
};

// represents a workspace, contains up-to-date snapshot of window.
function WorkspaceThumbnail() {
    this._init.apply(this, arguments);
}

WorkspaceThumbnail.prototype = {
    _init: function (index) {
        this._index = index;
        this.workspace = global.screen.get_workspace_by_index(index);

        this.actor = new St.Bin({
            track_hover: true,
            reactive: true
        });
        this.actor.style = 'border: 1px solid white; background: #888888'; // so we can see
        this.actor._delegate = this;

        // need to hold many windows
        this._workspace = new  StGroup({
            clip_to_allocation: true
        });
        this.actor.child = this._workspace;

        this.workspace.connect('window-added', Lang.bind(this, this._onWindowAdded));
    },

    _onWindowAdded: function (ws, win) {
        let winActor = win.get_compositor_private();
        if (!winActor) {
            Mainloop.idle_add(Lang.bind(this, function () {
                this._onWindowAdded(ws, win);
            }));
        } else {
            // window has an actor, make a clone for it.
            log('making clone of window ' + win.get_title());
            let clone = new WindowClone(winActor);
            this._workspace.add_actor(clone.actor);
        }
    },
};

// one per window
function WindowClone() {
    this._init.apply(this, arguments);
}

WindowClone.prototype = {
    _init: function (winActor) {
        this.actor = new St.Bin({
            x_fill: false,
            y_fill: false,
            x_align: St.Align.MIDDLE,
            y_align: St.Align.MIDDLE
        });
        this.actor._delegate = this;
        this.actor.style = 'border: 1px solid white; background: #cecece'; // so we can see

        this.winActor = winActor;
        this.metaWindow = winActor.meta_window;

        // we could use .set_scale but that makes it not as easy to
        // style the St.Bin (e.g. if border width is 1 and we use a scale
        // factor on the actor, we won't see the border at all)
        // hard code scale for now; in reality it is based off the WorkspaceThumbnail's
        // width/height.
        this._scale = Main.panel.actor.height / global.stage.height;

        if (!this.actor.realized) {
            let onetime = this.actor.connect('notify::realized',
                Lang.bind(this, function () {
                if (this.actor.realized) {
                    this.actor.disconnect(onetime);
                    this.init();
                }
            }));
        } else {
            this.init();
        }
    },

    init: function () {
        this.winActor.connect('position-changed', Lang.bind(this, this._onPositionChanged));
        this.winActor.connect('size-changed', Lang.bind(this, this._onSizeChanged));

        this._grandaddy = indicator.actor;
        
        this._onPositionChanged();
        this._onSizeChanged();
    },

    _onPositionChanged: function () {
        // update our position
        let rect = this.metaWindow.get_outer_rect(),
            newx = Math.round(rect.x * this._scale),
            newy = Math.round(rect.y * this._scale);
        if (this.actor.x === newx && this.actor.y === newy) {
            return;
        }
        // log('new position %d, %d'.format(newx, newy));
        this.actor.x = newx;
        this.actor.y = newy;

        // NOTE !! If I don't include the following line, the thumbnail
        // will not update !! why is this?
        // this.actor.queue_relayout, this.parent.queue_relayout all do
        // *not* cause the thumbnail to update.
        /* // uncomment the following to make it work
        if (this._grandaddy) {
            this._grandaddy.queue_relayout();
        }
        */
    },

    _onSizeChanged: function () {
        let rect = this.metaWindow.get_outer_rect(),
            newwid = Math.round(rect.width * this._scale),
            newh   = Math.round(rect.height * this._scale);
        if (this.actor.width === newwid && this.actor.height === newh) {
            return;
        }
        // log('new size %dx%d'.format(newwid, newh));
        this.actor.width = newwid;
        this.actor.height = newh;

        // NOTE !! If I don't include the following line, the thumbnail
        // will not update !! why is this?
        // this.actor.queue_relayout, this.parent.queue_relayout all do
        // *not* cause the thumbnail to update.
        if (this._grandaddy) {
            this._grandaddy.queue_relayout();
        }
    }
};

/** Extension code **/
function init(metadata) {
}

function enable() {
    indicator = new WorkspaceApplet();
    Main.panel.addToStatusArea('graphical-workspace-indicator', indicator);
}

function disable() {
    indicator = null;
}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]