How to colour fonts in Gtk3 label/entry widgets in Vala?

1.5k views Asked by At

I'm really enjoying experimenting with Vala, and have managed to solve most of the snags I've encountered so far. But the one that's causing me a bit of time-wasting right now is the colouring (and general modification) of fonts in Gtk3 Label and Entry widgets. In theory there are three ways of doing it:

(1) Using PANGO mark-up.

(2) Using modify_base and modify_bg.

(3) Using CSS.

The CSS route seems to be the one that Gtk3 is wanting to follow; and it's certainly the most appealing from a programmer's point of view. But it seems that it's not yet fully (or even very much) implemented in Vala. Am I right about this?

So modify_base and modify_bg are acceptable for now; except that only modify_bg seems to work. Is modify_base not yet implemented in Vala?

PANGO is a more of a handful; but in view of the above, I'd like to know how well covered it is in Vala...

(Some sample code below.)

using Gtk;
using Posix;

public class CSStestWindow: ApplicationWindow {

    private string APPNAME = "CSStest";
    private int PAGENO;
    private Gdk.Color LIGHTGREY;
    private Gdk.Color PALEGREEN;
    private Gdk.Color PINK;
    private Gdk.Color PURPLE;
    private Button bQuit = new Button.from_stock(Stock.QUIT);
    private Button bColour = new Button.from_stock(Stock.ADD);
    private Entry eAtitle = new Entry();
    private Entry eBtitle = new Entry();
    private Entry eCtitle = new Entry();
    private Entry eDtitle = new Entry();
    private Label lAtitle = new Label("AAA");
    private Label lBtitle = new Label("BBB");
    private Label lCtitle = new Label("CCC");
    private Label lDtitle = new Label("DDD");
    private Label lTab0 = new Label("First");
    private Label lTab1 = new Label("Second");
    private Label lTab2 = new Label("<span foreground='red'>Third</span>");
    private Label lTab3 = new Label("Fourth");
    private Notebook nbMain = new Notebook();

    internal CSStestWindow(CSStest app) {
        Object(application:app,title:"CSStest");
        Gdk.Color.parse("lightgrey",out LIGHTGREY);
        Gdk.Color.parse("palegreen",out PALEGREEN);
        Gdk.Color.parse("pink",out PINK);
        Gdk.Color.parse("purple",out PURPLE);
        string style = """
            @define-color bg beige;
            @define-color fg blue;
            * {engine:none; font-weight:bold;}
            .window {background-color:@bg;}
        /* those above seem to work, those below don't */
            .redongreen {background-color:green; color:red;}
            .switch {font-style:italic;}
            .entry {background-color:yellow;}
            .entry:selected {font-style:italic;}
            GtkButton {color:#ff00ea; font:Serif 12;}
            GtkButton:hover {background-color:#3085a9;}
            GtkLabel {background-color:#898989;}
            GtkNotebook {background-color:#a939f0;}
            GtkNotebook > GtkEntry {color:@fg; background-color:#1209a2;}
            GtkNotebook tab GtkLabel:focused {font:Serif 12;}
            #lDtitle {background-color:lightblue;}
            #eDtitle {font:Times;}
            :insensitive {background-color:#320a91;}
        """;
        CssProvider provider = new CssProvider();
        try {
            provider.load_from_data(style,-1);
        } catch (GLib.Error e) {
            warning(e.message);
        }
        this.get_style_context().add_provider
            (provider,STYLE_PROVIDER_PRIORITY_THEME);
        this.get_style_context().add_class("window");
        this.window_position = WindowPosition.CENTER;
        this.set_default_size(480,320);
// ---- Set up Notebook -----------------------------------------------
        nbMain.switch_page.connect((p,pn)=>{switchPage(pn);});
        nbMain.set_show_border(true);
// ---- Notebook Tab 0 ------------------------------------------------
        Grid gTab0 = new Grid();
        gTab0.attach(lAtitle,0,0,1,1);
        changeColour(eAtitle,PALEGREEN);
        gTab0.attach(eAtitle,1,0,1,1);
        bQuit.clicked.connect(onQuit);
        gTab0.attach(bQuit,2,0,1,1);
        nbMain.append_page(gTab0,lTab0);
// ---- Notebook Tab 1 ------------------------------------------------
        Grid gTab1 = new Grid();
        gTab1.attach(lBtitle,0,0,1,1);
        changeColour(eBtitle,PINK);
        gTab1.attach(eBtitle,1,0,1,1);
        nbMain.append_page(gTab1,lTab1);
// ---- Notebook Tab 2 ------------------------------------------------
        lTab2.set_use_markup(true);
        Grid gTab2 = new Grid();
        gTab2.attach(lCtitle,0,0,1,1);
        lCtitle.get_style_context().add_class("redongreen");
        gTab2.attach(eCtitle,1,0,1,1);
        bColour.clicked.connect(()=>{changeColour(eCtitle,LIGHTGREY);});
        gTab2.attach(bColour,2,0,1,1);
        nbMain.append_page(gTab2,lTab2);
// ---- Notebook Tab 3 ----------------------------------------------------
        Grid gTab3 = new Grid();
        gTab3.attach(lDtitle,0,0,1,1);
        gTab3.attach(eDtitle,1,0,1,1);
        PAGENO = nbMain.append_page(gTab3,lTab3);
        Widget pw = nbMain.get_nth_page(PAGENO);
        pw.get_style_context().add_class("redongreen");
// ---- Pack Notebook into vBox on main window ------------------------
        Box vbMain = new Box(Orientation.VERTICAL,0);
        vbMain.pack_start(nbMain,true,true,0);
        this.add(vbMain);
        this.show_all();
        showDialog("Started");
    }
// ==== changeColour ==================================================
    private void changeColour(Entry entry,Gdk.Color colour) {
        entry.modify_bg(StateType.NORMAL,colour);
        entry.modify_base(StateType.NORMAL,PURPLE);
    }
// ==== onQuit ========================================================
    private void onQuit() {
        showDialog("Ending");
        exit(-1);
    }
// ==== showDialog ====================================================
    private void showDialog(string message) {
        Dialog dialog = new Dialog.with_buttons
            (APPNAME,this,DialogFlags.MODAL,
            Stock.OK,ResponseType.OK,null);
        var content = dialog.get_content_area();
// DURING COMPILATION:
// warning: assignment from incompatible pointer type
// [enabled by default]
        Label label = new Label(message);
        label.set_line_wrap(true);
        content.add(label);
        content.show_all();
        dialog.response.connect((id)=>{dialog.destroy();});
        dialog.run();
    }
// ==== switchPage ====================================================
    private void switchPage(uint pn) {
        switch (pn) {
        case 0:
            break;
        case 1:
            break;
        case 2:
            break;
        case 3:
            break;
        }
nbMain.get_tab_label(nbMain.get_nth_page(nbMain.get_current_page())).get_style_context().add_class("switch");
    }

}

public class CSStest: Gtk.Application {
    internal CSStest() {
        Object(application_id: "org.test.CSStest");
    }
    protected override void activate() {
        new CSStestWindow(this).show();
    }
}

extern void exit(int exit_code);

public int main(string[] args) {
    return new CSStest().run(args);
}
2

There are 2 answers

0
b j norse On

Yippee! Thanks, nemequ: as you say, Gtk3 CSS styling works fully in Vala. Your assurance drove me back to first principles. In the example above, I had attached the CssProvider to the StyleContext of the top window in the GUI, whereas it should have been attached to the Gdk Screen object. Amended (and stripped-down) code follows.

using Gtk;
using Posix;

public class CSStestWindow: ApplicationWindow {

    internal CSStestWindow(CSStest app) {
        Object(application:app,title:"CSStest");
        this.window_position = WindowPosition.CENTER;
        this.set_default_size(480,320);
        string style = """
            GtkButton {color:#ff00ea; font:Serif 12;}
            GtkButton:hover {background-color:#3085a9;}
            GtkLabel {background-color:#898989;}
            GtkEntry {background-color:green; color:red;}
        """;
        CssProvider provider = new CssProvider();
        try {
            provider.load_from_data(style,-1);
        } catch (GLib.Error e) {
            warning(e.message);
        }
        StyleContext context = new StyleContext();
        var screen = Gdk.Screen.get_default();
        context.add_provider_for_screen
            (screen,provider,STYLE_PROVIDER_PRIORITY_USER);
        Box vbMain = new Box(Orientation.VERTICAL,0);
        bTop.clicked.connect(onQuit);
        Button bTop = new Button.from_stock(Stock.QUIT);
        vbMain.pack_start(bTop,false,false,0);
        Label lTop = new Label("Here we are again");
        vbMain.pack_start(lTop,false,false,0);
        Entry eTop = new Entry();
        vbMain.pack_start(eTop,false,false,0);
        this.add(vbMain);
        this.show_all();
    }
// ==== onQuit ============================================================
    private void onQuit() {
        exit(-1);
    }

}

public class CSStest: Gtk.Application {
    internal CSStest() {
        Object(application_id: "org.test.CSStest");
    }
    protected override void activate() {
        new CSStestWindow(this).show();
    }
}

extern void exit(int exit_code);

public int main(string[] args) {
    return new CSStest().run(args);
}
0
nemequ On

The CSS route seems to be the one that Gtk3 is wanting to follow; and it's certainly the most appealing from a programmer's point of view. But it seems that it's not yet fully (or even very much) implemented in Vala. Am I right about this?

No. CSS is covered fully in Vala, as is Pango. The Vala GTK+ bindings are generated from the GTK+ source code, and are quite complete. I'm not aware of any issues, but if specific things are missing or broken, please file a bug and we will take care of it.

As for how to actually go about that, I'm not exactly an expert in GTK+ but I believe what you want to do is use Gtk.CssProvider to load your CSS. Then apply it to the Gtk.StyleContext (which you can get with Gtk.Widget.get_style_context).

In any case, if you can find an example of how to do it in another language (preferably C), it's usually quite easy to translate that to Vala, even if you don't know the other language.