Builder Files

From Inkscape Wiki
Jump to navigation Jump to search


Gtk allows one to define the UI interface using "Builder" files. "Builder" files are XML files that describe widget layout, menus, etc.

Builder files should end with .ui but many of ours end with .glade as the UI creater program Glade was used to create them. Glade works only with Gtk3. Cambalache is a Glade replacement that works with both Gtk3 and Gtk4.

Tools

gtk-builder-tool

Allows one to simplify or examine Gtk3 builder files. Using the simplify option over our builder files results in:

  • Changing "True" to "1", "False" to "0"
  • Changing '"' to " and ''' to '.
  • Removing properties with default values, e.g.:
    • "can-focus" == False
    • "label-xalign" == 0
    • "receives-default" == False
    • "expand" == False
    • "fill" == True
    • "position" == 0
    • "xalign" == 0.5
  • Removing unneeded <packing>...</packing> (where all properties are default).
  • Removing <child><placeholder/></child> and <placeholder/>
  • Moving <!-- n-columns=x n-rows=y --> comment to before <child>.
  • Replacing underscores by hyphens in property names.

gtk-builder-tool does not understand the "child" property (in GtkScrolledWindow, GtkViewPort).

One can use a script to restore "True", "False", (without touching the 'active' property in ComboBox's). It also restores quotes and ampersands in strings (to avoid breaking translations). See below.

gtk4-builder-tool

In addition to the uses of gtk-builder-tool, gtk4-bulder-tool has a --3to4 option that converts gtk3 builder files to gtk4 builder files, the conversion is not perfect. In particular, it does not remove properties it doesn't recognize.

Using the 'simplify' mode without --3to4 (after using gtk-builder-tool):

  • Changes translatable="true" or "yes" to translatable="1".
  • Strips many comments.

Using the 'simplify' mode with --3to4 results in:

  • Replacing <requires lib="gtk+" version="3.24"/> with <requires lib="gtk" version="4.0"/>
  • Changes <attribute name='xxx'... to <attribute name="xxx"...
  • Removing the property "visible" when its value is "True" (widgets are visible by default in Gtk4).
  • Replacing the property "can-focus" with value "True" by the property "focusable" with value 1.
  • Removing <packing>...</packing> for GtkBox.
  • Replacing <object>...</object><packing>...</packing> by <object>...<layout>...</layout></object> for GtkGrid.
  • Changing <child>...</child> to <property name="child">...</property> for child of GtkFlowBoxChild and GtkViewPort.
  • Changing the property "margin" to the four properties "margin-start", "margin-end", "margin-top", and "margin-bottom".
  • Adding the property "draw-value" with value 1 for GtkScale.
  • Adding the property "visible" with value 0 to GtkFlowBoxChild.

Properties not supported in Gtk4 and not replaced:

  • GtkAspectFrame::label-xalign
  • GtkAspectFrame::shadow-type
  • GtkBox::double-buffered
  • GtkBox::events
  • GtkBox::no-show-all
  • GtkButton::always-show-image
  • GtkButtonBox::can-focus
  • GtkButtonBox::hexpand
  • GtkButtonBox::layout-style
  • GtkButtonBox::visible
  • GtkButton::can-default
  • GtkButton::image
  • GtkButton::image-position
  • GtkButton::no-show-all
  • GtkButton::relief
  • GtkButton::use-stock
  • GtkCellRendererPixbuf::stock_size
  • GtkCheckButton::image
  • GtkCheckButton::no-show-all
  • GtkDialog::gravity
  • GtkDialog::has-resize-grip
  • GtkDialog::skip-taskbar-hint
  • GtkDialog::type-hint
  • GtkDialog::type_hint
  • GtkDialog::window-position
  • GtkEntry::caps-lock-warning
  • GtkEntry::no_show_all
  • GtkEventBox::can-focus
  • GtkEventBox::can_focus
  • GtkEventBox::halign
  • GtkEventBox::hexpand
  • GtkEventBox::name
  • GtkEventBox::no-show-all
  • GtkEventBox::valign
  • GtkEventBox::visible
  • GtkEventBox::visible-window
  • GtkExpander::label-fill
  • GtkFlowBox::no-show-all
  • GtkFrame::shadow-type
  • GtkGrid::no-show-all
  • GtkImage::no-show-all
  • GtkImage::stock
  • GtkLabel::no-show-all
  • GtkLabel::no_show_all
  • GtkLabel::ypad
  • GtkLinkButton::relief
  • GtkMenuButton::draw-indicator
  • GtkMenuButton::image
  • GtkMenuButton::no-show-all
  • GtkMenuButton::popup
  • GtkMenuButton::relief
  • GtkMenuButton::use-popover
  • GtkMenu::can-focus
  • GtkMenuItem::can-focus
  • GtkMenuItem::use-underline
  • GtkMenuItem::visible
  • GtkMenu::reserve-toggle-size
  • GtkMenu::visible
  • GtkModelButton::action-name
  • GtkModelButton::action-target
  • GtkModelButton::can-focus
  • GtkModelButton::halign
  • GtkModelButton::inverted
  • GtkModelButton::receives-default
  • GtkModelButton::visible
  • GtkNotebook::double-buffered
  • GtkNotebook::no-show-all
  • GtkPopover::constrain-to
  • GtkPopover::no-show-all
  • GtkPopover::relative-to
  • GtkPopover::transitions-enabled
  • GtkRadioMenuItem::can-focus
  • GtkRadioMenuItem::draw-as-radio
  • GtkRadioMenuItem::group
  • GtkRadioMenuItem::use-underline
  • GtkRadioMenuItem::visible
  • GtkScrolledWindow::double-buffered
  • GtkScrolledWindow::no-show-all
  • GtkScrolledWindow::shadow-type
  • GtkScrolledWindow::shadow_type
  • GtkScrolledWindow::window-placement-set
  • GtkSearchEntry::can-default
  • GtkSearchEntry::caps-lock-warning
  • GtkSearchEntry::completion
  • GtkSearchEntry::is-focus
  • GtkSearchEntry::primary-icon-activatable
  • GtkSearchEntry::primary-icon-name
  • GtkSearchEntry::primary-icon-sensitive
  • GtkSeparatorMenuItem::can-focus
  • GtkSeparatorMenuItem::margin-bottom
  • GtkSeparatorMenuItem::margin-top
  • GtkSeparatorMenuItem::visible
  • GtkSpinButton::caps-lock-warning
  • GtkSpinButton::input-purpose
  • GtkSpinner::active
  • GtkToggleButton::always-show-image
  • GtkToggleButton::image
  • GtkToggleButton::no-show-all
  • GtkToggleButton::relief
  • GtkToggleButton::use-action-appearance
  • GtkToolItem::can-focus
  • GtkToolItem::visible
  • GtkTreeView::no_show_all
  • GtkViewport::shadow-type
  • GtkWindow::can_default
  • GtkWindow::gravity
  • GtkWindow::is_focus
  • GtkWindow::urgency_hint
  • GtkWindow::window-position
  • GtkWindow::window_position

Restore "True"/"False"/quotes/ampersands.

Perl Script

#!/usr/bin/perl

# Restore use of True (instead of '1') and False (instead of '0') after
# using gtk-builder-tool or gtk4-builder-tool.

use strict;
use warnings;
use File::Copy;

open my $true_false, '<', "true_false.txt" or die "Cannot read true_false.txt";
chomp (my @properties = <$true_false>);

my $pattern = "";
foreach (@properties) {
    $pattern = $pattern . "\"";
    $pattern = $pattern . $_;
    $pattern = $pattern . "\"";
    $pattern = $pattern. "|";
}
chop $pattern;
#print $pattern . "\n";

my $file = $ARGV[0];
#print $file . "\n";

my $file_out = $file . "_out";
open my $input, '<', $file or die "Cannot open $file";
open my $output, '>', $file_out or die "Cannot open $file_out";

my $class = "";
while (my $line = <$input>) {

    # Track current class
    if ($line =~ m/class=\"(.*?)\"/) {
        $class = $1;
        # print "Class: " . $class . "\n";
    }

    # For gtk4-builder-tool
    $line =~ s/translatable=\"1\"/translatable=\"yes\"/;
    $line =~ s/translatable=\"0\"/translatable=\"no\"/;

    if ($line =~ m/($pattern)/) {
        #  print $line;
        if ($line =~ m/active/ and $class =~ m/ComboBox/) {
            # Don't change 'active' property to True/False for ComboBox or ComboBoxText
            # print "FOUND COMBOBOX WITH ACTIVE\n";
        } else {
            $line =~ s/>0</>False</;
            $line =~ s/>1</>True</;
        }
        # print $line;
    }
    $line =~ s/&quot;/\"/g;  # This is improper... but it would effect translations.
    $line =~ s/&apos;/\'/g;
    print $output $line;
}

close $input;
close $output;
move ($file_out, $file) or die "Couldn't move $file_out to $file!";

Input File: save as true_false.txt

accepts-tab
activate-on-single-click
activates-default
active
always-show-image
can-default
can-focus
caps-lock-warning
column-homogeneous
cursor-visible
destroy-with-parent
double-buffered
draw-as-radio
draw-indicator
draw-value
editable
enable-popup
enable-search
expand
fill
fixed-height-mode
focus-on-click
has-default
has-entry
has-focus
has-frame
has-origin
has-resize-grip
has-tooltip
headers-clickable
headers-visible
hexpand
homogeneous
hover-selection
inline-completion
inverted
is-focus
label-fill
modal
no-show-all
numeric
overlay-scrolling
pass-through
popup-set-width
position-set
primary-icon-activatable
primary-icon-sensitive
propagate-natural-height
propagate-natural-width
receives-default
reserve-toggle-size
resizable
resize
resize-toplevel
reveal-child
row-homogeneous
search-mode-enabled
secondary-use-markup
selectable
sensitive
show-border
show-expanders
show-fill-level
show-text
shrink
single-line-mode
skip-taskbar-hint
sort-indicator
tab-expand
tab-fill
transitions-enabled
urgency-hint
use-action-appearance
use-fallback
use-markup
use-popover
use-stock
use-underline
vexpand
visible
visible-window
wide-handle
window-placement-set
wrap

Perl program that moves button images from top of file to inside button, compatible with both Gtk3 and Gtk4.

#!/usr/bin/perl

# Move button images from top of file to inside button.
# Tavmjong Bah

use strict;
use warnings;
use v5.10;

use File::Copy;
use XML::LibXML;
use Text::Diff;

my @files = <*>;

my $flip_flop = 0;

foreach my $file (@files) {
    if ($file =~ ".ui\$" or $file =~ ".glade\$") {

        my $file_out = $file . "_out";

        my $dom = XML::LibXML->load_xml(location => $file);

        # Find GtkButton's
        foreach my $object ($dom->findnodes('//object[@class = "GtkButton"]')) {

            # See if button has 'image' and 'label' properties.
            my ($property_image) = $object->findnodes('./property[@name = "image"]');
            my ($property_label) = $object->findnodes('./property[@name = "label"]');

            # If button has 'image' property.
            if (defined($property_image)) {
                # Find corresponding image element.
                my $image = $property_image->to_literal();
                my ($gtk_image) = $dom->findnodes("//object[\@id = \"$image\"]");
                # my $gtk_image = $dom->getElementById("xml:$image");

                # If we found image element.
                if (defined($gtk_image)) {

                    # Remove 'image' property'.
                    $property_image->unbindNode();

                    # Add child to object.
                    my $child = $dom->createElement('child');
                    $object->appendChild($child);

                    if (defined($property_label)) {
                        # We have both 'image' and 'label', need to create box.
                        say "Button with both image and label! ", $file;

                        # Add box
                        my $box = $dom->createElement('object');
                        $box->{class} = "GtkBox";
                        $child->appendChild($box);

                        # Add/move image
                        my $child_image = $dom->createElement('child');
                        $child_image->appendChild($gtk_image);
                        $box->appendChild($child_image);

                        # Add label and move 'label' property.
                        my $child_label = $dom->createElement('child');
                        $box->appendChild($child_label);

                        my $object_label = $dom->createElement('object');
                        $object_label->{class} = "GtkLabel";
                        $object_label->appendChild($property_label);
                        $child_label->appendChild($object_label);

                        # Make visible (Gtk3)
                        my $property_visible = $dom->createElement('property');
                        $property_visible->{name} = "visible";
                        $property_visible->appendText('True');
                        $object_label->appendChild($property_visible);

                        # my $class = $dom->createAttribute('class', 'GtkBox');
                    } else {
                        # Only have image; move image to child.
                        $child->appendChild($gtk_image);
                    }
                } else {
                    say "Warning, could not find $image in $file";
                }
            }
        }

        # Make it possible to pretty print by removing empty text nodes.
        foreach ($dom->findnodes('//text()')) {
            $_->parentNode->removeChild($_) unless /\S/;
        }

        # say $dom->toString(1);
        $dom->toFile($file_out, 1);

        # say "DIFF =============================================";
        # my $diffs = diff "$file" => "$file_out";
        # print $diffs;
        move ($file_out, $file);
    }
}