Adding color to borders gnome windows

I am working on an extension to add coloured borders to a window based on the window.

I am adding St.Bin to the global.window_group and giving the St.Bin a style using css.

Everything working fine and the borders are drawn on the window. However, when drawing multiple borders all the borders seems to be on top of all the windows. The borders doesn’t be have as normal windows.

As the screenshot shows the overlap between the added borders. I am asking if there are a better way to implement this!!

My code is below

extensions.js

const { GObject, St, Meta, GLib } = imports.gi;

const Main = imports.ui.main;

const Me = imports.misc.extensionUtils.getCurrentExtension();
const Window = Me.imports.window;

class Extension {
    constructor(uuid) {
        this._uuid = uuid;
        this.windows = {}
    }

    windowCreated(display, window) {
        log("Window Created")

        this.windows[window.get_description()] = new Window.Window(window)
    }

    enable() {
        log("Enabled")
        
        this.windowCreatedID = global.display.connect('window-created', this.windowCreated.bind(this))
    }

    disable() {
        log("Disabled")
        for (let windowKey in this.windows){
            this.windows[windowKey].windowClosed()
            global.window_group.remove_child(this.windows[windowKey].border)
            delete this.windows[windowKey]
        }

        global.display.disconnect(this.windowCreatedID)
    }
}

function init(meta) {
    return new Extension(meta.uuid);
}

window.js

const { GObject, St } = imports.gi;

const BORDERSIZE = 3;

class Window{
    constructor(window){
        this.window = window
        //this.workspaceChangedID = this.window.connect('workspace-changed', this.windowClosed.bind(this))
        this.sizeChangedID = this.window.connect('size-changed', this.updateBorderLayout.bind(this))
        this.positionChangedID = this.window.connect('position-changed', this.updateBorderLayout.bind(this))
        this.border = new St.Bin({ 
            style_class: 'border',
            reactive: true,
            can_focus: true,
            track_hover: true,
        });
        global.window_group.add_child(this.border)
        let rect = this.window.get_frame_rect()
        this.border.set_position(rect.x, rect.y)
        this.border.set_size(rect.width + BORDERSIZE, rect.height+ BORDERSIZE)
        this.isNewWindow = true
    }
    
    updateBorderLayout(){
        log("Update Window Border")
        let rect = this.window.get_frame_rect()
        this.border.set_position(rect.x, rect.y)
        this.border.set_size(rect.width + BORDERSIZE, rect.height + BORDERSIZE)
    }

    windowClosed(){
        log("Window Closed")
        
        log("Window ID = "+this.window.get_description())
        log("workspace-changed ID = "+this.workspaceChangedID)
        log("size-changed ID = "+this.sizeChangedID)
        log("position-changed ID = "+this.positionChangedID)

        this.window.disconnect(this.workspaceChangedID)
        this.window.disconnect(this.sizeChangeID)
        this.window.disconnect(this.positionChangedID)
    }
}

stylesheet.css

/* Add your custom extension styling here */
.border {
  border-style: solid;
  border-color: #FFAD00;
  border-radius: 5px;
  box-shadow: inset 0 0 0 1px rgba(200, 200, 200, 0);
  border-width: 3px;
}
 

However, when drawing multiple borders all the borders seems to be on top of all the windows. The borders doesn’t be have as normal windows.

Well yes. That’s because the borders aren’t actually windows. The compositor has no idea where to put them in the stack, so ignores them.

You have to move them to the right layer yourself, and connect to the global.display('restacked') signal to update them.

I am new to the gnome development. So if you could provide me with an example or a source that I could use to solve it?

Nevermind, your comment was more than enough to solve the problem thank you!

The new code

extension.js

const { GObject, St, Meta, GLib } = imports.gi;

const Main = imports.ui.main;

const Me = imports.misc.extensionUtils.getCurrentExtension();
const Window = Me.imports.window;

class Extension {
    constructor(uuid) {
        this._uuid = uuid;
        this.winCreatedHandlerID = null
        this.restackHandlerID = null
        this.windows = {}
    }

    windowCreated(display, metaWindow) {
        log("Window Created")

        this.windows[metaWindow.get_description()] = new Window.Window(metaWindow)
    }

    restack(display){
        global.window_group.get_children().forEach(
            (child) => {
                let metaWindow = this.checkMetaWindow(child)

                if (!metaWindow) return;
                
                let window = this.windows[metaWindow.get_description()]
                
                global.window_group.set_child_above_sibling(window.border, child)
            }
        )
    }

    checkMetaWindow(child){
        let metaWindow = null
        try {
            metaWindow = child.get_meta_window()
        } catch (error) {
            log(error)
        }

        if(!metaWindow) return false;

        let type = metaWindow.get_window_type()

        if (
            type != Meta.WindowType.NORMAL &&
            type != Meta.WindowType.DIALOG &&
            type != Meta.WindowType.MODAL_DIALOG
        ) return false;

        return metaWindow
    }

    enable() {
        log("Enabled")
        
        this.winCreatedHandlerID = global.display.connect('window-created', this.windowCreated.bind(this))
        this.restackHandlerID = global.display.connect('restacked', this.restack.bind(this))

        global.window_group.get_children().forEach(
            (child) => {
                let metaWindow = this.checkMetaWindow(child)

                if (!metaWindow) return;

                log(Window.Window)
                let window = new Window.Window(metaWindow)
                
                this.windows[metaWindow.get_description()] = window
                global.window_group.add_child(window.border)
                global.window_group.set_child_above_sibling(window.border, child)
            }
        )
    }

    disable() {
        log("Disabled")

        for (let windowKey in this.windows){
            this.windows[windowKey].windowClosed()
            global.window_group.remove_child(this.windows[windowKey].border)
            delete this.windows[windowKey]
        }

        global.display.disconnect(this.winCreatedHandlerID)
        global.display.disconnect(this.restackHandlerID)
    }
}

function init(meta) {
    return new Extension(meta.uuid);
}

window.js

const { GObject, St } = imports.gi;

const BORDERSIZE = 3;

class Window{
    constructor(metaWindow){
        this.metaWindow = metaWindow

        this.border = new St.Bin({style_class: 'border'});
        
        let rect = this.metaWindow.get_frame_rect()
        this.border.set_position(rect.x, rect.y)
        this.border.set_size(rect.width + BORDERSIZE, rect.height+ BORDERSIZE)

        this.sizeChangedID = this.metaWindow.connect('size-changed', this.updateBorderLayout.bind(this))
        this.positionChangedID = this.metaWindow.connect('position-changed', this.updateBorderLayout.bind(this))
    }

    updateBorderLayout(){
        log("Update Window Border")
        let rect = this.metaWindow.get_frame_rect()
        this.border.set_position(rect.x, rect.y)
        this.border.set_size(rect.width + BORDERSIZE, rect.height + BORDERSIZE)
    }

    windowClosed(){
        log("Window Closed")
        
        log("Window ID = "+this.metaWindow.get_description())
        log("workspace-changed ID = "+this.workspaceChangedID)
        log("size-changed ID = "+this.sizeChangedID)
        log("position-changed ID = "+this.positionChangedID)

        this.metaWindow.disconnect(this.workspaceChangedID)
        this.metaWindow.disconnect(this.sizeChangeID)
        this.metaWindow.disconnect(this.positionChangedID)
    }
}