Tuesday, May 22, 2012

Gnome Shell - Netbook Interface - Part II

It seems these posts have become horribly popular though I am infinitely unqualified to teach anyone about gnome shell. I don't know javascript... At all... This is going to get interesting.

So it turns out that Gnome-Shell has been forked already. I'm pleased to have learnt this. I would like to be able to ultimately offer a bunch of different interfaces based upon need and preference in parallel with each other as well as Gnome Shell and Unity. In other words, rather than an extension, I'd really like to create a fork. Still - humble beginnings. I barely know how to walk. Of course, if I do manage to pull this off, it should make an interesting log of just how many times I got it all so very wrong.

So I'm desperately trying to learn Javascript. In particular, I'd love to figure out how to sort out the whole classes debacle. It turns out javascript doesn't really have classes. (Sorry - forgot to do my geek speak warning...).

Instead, everything is an object. Including functions. So, you could use a function as a class:

function testClass () {
    this.something = 3;
}


So in order to use this I would then do something like:


myObject = new testClass();
global.log( myObject.something );


So this gives me properties but what about methods? Two ways - define it internally such as the following:


function testClass () {
    this.something = 3;
    this.square = function( number ){
        return Math.pow( number, 2 );
    }
}


Or I can do it externally using the prototype keyword:


function testClass () {
    this.something = 3;
}


testClass.prototype.square = function( number ){
    return Math.pow( number, 2 );
}


The preference of the gnome-shell developers seems to be the first method - define them internally. So with that piece of information in hand (got to love the long routes taken when trying to learn something for the first time), we'll actually make a start on the netbook interface.


One of my design patterns is to simply extend GUI elements. This is completely contrary to my rant about separating out the GUI bits and doing the logic elsewhere - though it can be argued that given that we're only doing GUI bits, that paradigm doesn't really apply here anyway.


So the GUI element I'm looking at. Think about the application icons. They all share a whole bunch of attributes (probably those defined in an application's .desktop file - normally found in /usr/share/applications on a Debian based system. I'm not sure if this is the same in Redhat/Slackware etc. based systems). So it'd be silly not to use a class. But, because I like things to be fairly tidy, the GUI bit should just contain all of those extra bits of information. So it'd be something like this:


So essentially, I want to return a boxLayout object (with the vertical property set to true) which contains an Icon and a Label (we probably want to make this a bit more flexible in the future. i.e. Categories are the same thing only with the icon and label in a hbox - a boxLayout object with the vertical property set to false).

By this reasoning, we should be able to do something along the lines of:

function applicationObject() {
    self = new St.BoxLayout({
            vertical: true,
            style_class: 'application'});
    this.icon = new St.Icon();
    this.name = new St.Label();
    self.add_actor( this.icon );
    self.add_actor( this.name );
    return self;
}

Of course this is missing a bunch of things. What happens onClick? Where does it get the information about the icon from? And the name of the application? So okay - it needs a lot of work. This is what I have so far:

extension.js
const St = imports.gi.St;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const Shell = imports.gi.Shell;
const Lang = imports.lang;


let button, menu;


function _tellMe(){
    global.log('Can\' get this to work' );
}


function applicationObject() {
    // change this to allow for .desktop file information
    // to come in and an icon and localised text to be
    // returned.
    self = new St.BoxLayout(
            { vertical: true,
            style_class: 'application' });
    this.icon = new St.Icon(
            { icon_name: 'applications-system',
            icon_type: St.IconType.FULLCOLOR,
            style_class: 'applicationIcon' });
    this.lblName = new St.Label(
            { text: 'calculator' });
    self.add_actor( this.icon );
    self.add_actor( this.lblName );
    self.connect( 'button-press-event', _tellMe )
    return self;
}


function _showMenu() {
    if (!menu){
        menu = new St.BoxLayout(
                { vertical: false,
                style_class: 'menu',
                height: Main.layoutManager.primaryMonitor.height - Main.panel.actor.height,
                width: Main.layoutManager.primaryMonitor.width,
                reactive: true });
        global.log('Menu');
        menu.set_position(0, Main.panel.actor.height);
        Main.uiGroup.add_actor(menu);
        menu.visible = false;
        something = new applicationObject();
        menu.add_actor( something );
    }
    global.log(menu.visible);
    if (menu.visible) {
        menu.hide();
    }
    else {
        menu.show();
    }
}


function init() {
    button = new St.Bin(
           {style_class: 'panel-button',
            reactive: true,
            can_focus: true,
            x_fill: false,
            y_fill: false,
            track_hover: true});
   let icon = new St.Icon(
           {icon_name: 'applications-system',
            icon_type: St.IconType.FULLCOLOR,
            icon_size: 24,
            style_class: 'system-status-icon' });
    button.set_child(icon);
    button.connect('button-press-event', _showMenu);
}


function enable() {
    Main.panel._leftBox.insert_child_at_index(button, 0);
    _showMenu();
}


function disable() {
    Main.panel._leftBox.remove_child(button);
}

stylesheet.css
.menu {
    color: #ffffff;
    background-color: rgba(10,10,10,0.3);
    border-radius: 5px;
    padding: .5em;
}


.application {
    icon-size: 3em;
    font-size: 48px;
    border: 1px;
    border-radius: 10px;
}


.applicationIcon {
    icon-size: 3em;
}

So I've got the start of something. It's not brilliant (yet)... I'm missing something - and I've got a feeling it's a one-liner. At the moment I can't click on the icon. It's showing, but it's not reactive/responsive in anyway whatsoever. Rather, it's like an overlay. It's there, but I can't for the life of me figure out how to make it respond to clicks. Anyone got any hints?

No comments:

Post a Comment