Skip to content

Instantly share code, notes, and snippets.

@Ryan1729
Last active April 7, 2020 09:53
Show Gist options
  • Save Ryan1729/1e78776683b8bdc8fa902023fc6f6afe to your computer and use it in GitHub Desktop.
Save Ryan1729/1e78776683b8bdc8fa902023fc6f6afe to your computer and use it in GitHub Desktop.
Theming Git-Gui on windows

As far as i can tell there is no way to change the theme of git-gui as packaged in git for windows without editing .tcl files.

When you use the Git GUI here context menu option, the .tcl files that are interpreted are those in C:\Users\USERNAME\AppData\Local\Programs\Git\mingw64\lib\tk8.6\ttk. You can confirm this with Process Monitor. You'll want to filter for wish.exe if you do so. Oddly enough there is a totally separte copy of these files in Program Files/Git.

Hello world

A .tcl file that git-gui interprets directly is ttk.tcl. To demonstrate that this is the case add the following line ttk.tcl somewhere at the top level.

tk_messageBox -type ok -icon info -message "Hello world" -title "Info"

Now if you open git-gui from the context menu it should first pop up a window with "Hello World" and an ok button on it. Upon clicking ok it appears again. After the second time git-gui will open up. Adding this window box seems to mess up gitk if you open it from git-gui. Specifcally the info from the repo in question never gets loaded.

You can replace "Hello world" in that code with another string to get information out of the system.

For instance you can find the for loop inside the DefaultTheme function that loks like this:

    foreach theme $preferred {
	if {[package provide ttk::theme::$theme] ne ""} {
	    return $theme
	}
    }

and then make it look like this to find out what theme you are using:

    foreach theme $preferred {
	if {[package provide ttk::theme::$theme] ne ""} {
        tk_messageBox -type ok -icon info -message $theme -title "Info"
	    return $theme
	}
    }

In my case, I see "vista". An examination of ttk.tcl reveals that this indicates that vistaTheme.tcl is the file that contains the theme that is actually used (see the LoadThemes function.) Let's try changing something in there just to see it work. An easy change to make is to set the radio button padding.

Find the line in vistaTheme.tcl that looks like ttk::style configure TRadiobutton -padding 2 and change it to ttk::style configure TRadiobutton -padding 200. Then try starting git-gui and notice that the radio buttons now take up so much space that most of the interface is pushed away. You'll proably want to change it back to 2.

While you can edit vista.tcl to change things, long term it's probably better to make a separate theme file and make it the default by editing ttk.cl to load it as the default.

Let's assume you want a dark version of the vista theme. We'll call it "darkVistaTheme" You can add a new theme that gets loaded by default by finding part in LoadThemes that looks like this:

    # "default" always present:
    uplevel #0 [list source [file join $library defaults.tcl]]

and below it adding something like the following:

    # Load darkVistaTheme
   uplevel #0 [list source [file join $library darkVistaTheme.tcl]]

then you will need to find the preffered list in DefaultTheme and add darkVistaTheme to the front of the list. That is find:

	set preferred [list aqua vista xpnative winnative]

and change it to

 	set preferred [list darkVistaTheme aqua vista xpnative winnative]

Now you can copy the vistaTheme.tcl file to a new file called darkVistaTheme.tcl and make a change and you should see the change when you open git-gui again. Now technically, we should probably make replace all instances of vista in darkVistaTheme.tcl with darkVistaTheme but we don't really care if the theme is overwritten in memory, as long as we couldrollback these edits and restore previous behaviour so whatever.

Hopefully it's clear how to give your theme a different name other than darkVistaTheme if you want to.

To make a dark theme one of the first things you will want to do is set -foreground to some light colour and -background to some dark colour for most of the widgets. For example, you could find the line we mentioned before that looks like ttk::style configure TRadiobutton -padding 2 and change it to ttk::style configure TRadiobutton -padding 2 -foreground #EEEEEE -background #333333.

You can also set variables contining colours and use those in multiple places so you can change them all at once later.

You could put these at the start of the namespace eval ttk::theme::vista block.

	variable darkBackground
   set darkBackground "#333333"
   
   variable lightForeground
   set lightForeground "#EEEEEE"

and then use them like this:

	ttk::style configure TRadiobutton -padding 2 -foreground $lightForeground -background $darkBackground

The question now is, what settings do things have? According to this tk documentation page we can list the elements of a widget, (each element has a set of settings) with this code:

	ttk::style layout TButton

Feel free to replace TButton with another widget name.

We can get the information out of the running program with our info box trick again:

	tk_messageBox -type ok -icon info -message [ttk::style layout TButton] -title "Info"

Note the use of [] instead of the () you might be expecting. () have a different meaning and they will therefore not do what we want. The use of [] as evaluation demarcators is a little unusual, however putting something that will be used as often as subexpressions on keys that do not require Shift, does not seem like the craziest idea.

The output I get in the message box is:

Button.border -sticky nswe -border 1 -children {Button.focus -sticky nswe 
-children {Button.spacing -sticky nswe -children {Button.label -sticky nswe}}}

just like the tk documentation page says. It goes on to format it like this:

Button.border -sticky nswe -border 1 -children {
    Button.focus -sticky nswe -children {
        Button.spacing -sticky nswe -children {
            Button.label -sticky nswe
        }
    }
}

and to say that this indicates that there are four elements of TButton: Button.border, Button.focus, Button.spacing and Button.label.

It goes on to say we can get the options of Button.label like this:

	ttk::style element options Button.label

and if we run

	tk_messageBox -type ok -icon info -message [ttk::style element options Button.label] -title "Info"

we also get

-compound -space -text -font -foreground -underline -width -anchor -justify 
-wraplength -embossed -image -stipple -background

just like that page says.

That page goes on to state the lamentable fact that we cannot set styles on elements. They come from the parent styles! This comes with the following caveat which I will quote:

Except when you can. There are a few nasty, widget-specific things called "sublayouts" in the current implementation which let you sometimes modify just a single element, via configuring an option like "TButton.Label" (rather than just "TButton", the name of the style). Are the cases where you can do this documented? Is there some way to introspect to determine when you can do this? No to both.

For now, it seems the best we can do is dig around in all the elements mentioned in darkVistaTheme.tcl and find elements with -background and -foreground options and set the styles above them to different colours. Note that there is a root style called "." which other styles may inherit from.

Suprisingly, if I run the following:

	tk_messageBox -type ok -icon info -message [ttk::style layout .] -title "Info"

the info box comes up empty!

From this SO page I was able to piece together that I could call ttk::style configure . and get some output, but that only lists the options that were previously set. I know this because I tried running it before and after the existing code in darkVistaTheme.tcl that deals with "." and it only showed anything when run after that code.

@nkmathew
Copy link

nkmathew commented Apr 7, 2020

Nice tutorial

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment