Ejemplo n.º 1
0
 def _update_layer_mode_combo(self):
     """Updates the layer mode combo's value from the model"""
     assert self._processing_model_updates
     combo = self._layer_mode_combo
     rootstack = self.app.doc.model.layer_stack
     current = rootstack.current
     if current is rootstack or not current:
         combo.set_sensitive(False)
         return
     elif not combo.get_sensitive():
         combo.set_sensitive(True)
     active_iter = None
     current_mode = current.mode
     for row in combo.get_model():
         mode = row[0]
         if mode == current_mode:
             active_iter = row.iter
         row[2] = (mode in current.PERMITTED_MODES)
     combo.set_active_iter(active_iter)
     label, desc = MODE_STRINGS.get(current_mode)
     template = self.LAYER_MODE_TOOLTIP_MARKUP_TEMPLATE
     tooltip = template.format(
         name = lib.xml.escape(label),
         description = lib.xml.escape(desc),
     )
     combo.set_tooltip_markup(tooltip)
Ejemplo n.º 2
0
 def __init__(self):
     """Initialize, called by the LayerMode FactoryAction making a menu"""
     Gtk.ImageMenuItem.__init__(self)
     menu = Gtk.Menu()
     self._menu_items = []
     prev_item = None
     spec_separator = (None,)
     modes_menu_spec = (STACK_MODES + spec_separator + STANDARD_MODES)
     for mode in modes_menu_spec:
         if mode is None:
             menu.append(Gtk.SeparatorMenuItem())
             continue
         label, tooltip = MODE_STRINGS.get(mode)
         if prev_item is None:
             item = Gtk.RadioMenuItem()
         else:
             item = Gtk.RadioMenuItem(group=prev_item)
         item.set_label(label)
         item.set_tooltip_text(tooltip)
         item.connect("activate", self._item_activated_cb, mode)
         menu.append(item)
         self._menu_items.append((mode, item))
         prev_item = item
     self._submenu = menu
     self.set_submenu(self._submenu)
     self._submenu.show_all()
     from gui.application import get_app
     app = get_app()
     self._model = app.doc.model
     rootstack = self._model.layer_stack
     rootstack.layer_properties_changed += self._update_actions
     rootstack.current_path_updated += self._update_actions
     self._updating = False
     self._update_actions()
Ejemplo n.º 3
0
 def _update_layer_mode_combo(self):
     """Updates the layer mode combo's value from the model"""
     assert self._processing_model_updates
     combo = self._layer_mode_combo
     rootstack = self.app.doc.model.layer_stack
     current = rootstack.current
     if current is rootstack or not current:
         combo.set_sensitive(False)
         return
     elif not combo.get_sensitive():
         combo.set_sensitive(True)
     active_iter = None
     current_mode = current.mode
     for row in combo.get_model():
         mode = row[0]
         if mode == current_mode:
             active_iter = row.iter
         row[2] = (mode in current.PERMITTED_MODES)
     combo.set_active_iter(active_iter)
     label, desc = MODE_STRINGS.get(current_mode)
     template = self.LAYER_MODE_TOOLTIP_MARKUP_TEMPLATE
     tooltip = template.format(
         name = lib.xml.escape(label),
         description = lib.xml.escape(desc),
     )
     combo.set_tooltip_markup(tooltip)
Ejemplo n.º 4
0
 def __init__(self):
     """Initialize, called by the LayerMode FactoryAction making a menu"""
     Gtk.ImageMenuItem.__init__(self)
     menu = Gtk.Menu()
     self._menu_items = []
     prev_item = None
     spec_separator = (None, )
     modes_menu_spec = (STACK_MODES + spec_separator + STANDARD_MODES)
     for mode in modes_menu_spec:
         if mode is None:
             menu.append(Gtk.SeparatorMenuItem())
             continue
         label, tooltip = MODE_STRINGS.get(mode)
         if prev_item is None:
             item = Gtk.RadioMenuItem()
         else:
             item = Gtk.RadioMenuItem(group=prev_item)
         item.set_label(label)
         item.set_tooltip_text(tooltip)
         item.connect("activate", self._item_activated_cb, mode)
         menu.append(item)
         self._menu_items.append((mode, item))
         prev_item = item
     self._submenu = menu
     self.set_submenu(self._submenu)
     self._submenu.show_all()
     from gui.application import get_app
     app = get_app()
     self._model = app.doc.model
     rootstack = self._model.layer_stack
     rootstack.layer_properties_changed += self._update_actions
     rootstack.current_path_updated += self._update_actions
     self._updating = False
     self._update_actions()
Ejemplo n.º 5
0
 def _layer_mode_combo_changed_cb(self, *ignored):
     """Propagate the user's choice of layer mode to the model"""
     if self._processing_model_updates:
         return
     docmodel = self.app.doc.model
     combo = self._layer_mode_combo
     model = combo.get_model()
     mode = model.get_value(combo.get_active_iter(), 0)
     if docmodel.layer_stack.current.mode == mode:
         return
     label, desc = MODE_STRINGS.get(mode)
     docmodel.set_current_layer_mode(mode)
Ejemplo n.º 6
0
    def _v_layer_mode_combo_query_tooltip_cb(self, combo, x, y, kbd, tooltip):

        label, desc = MODE_STRINGS.get(self._layer.mode, (None, None))
        if not (label and desc):
            return False
        template = self._LAYER_MODE_TOOLTIP_MARKUP_TEMPLATE
        markup = template.format(
            name=lib.xml.escape(label),
            description=lib.xml.escape(desc),
        )
        tooltip.set_markup(markup)
        return True
Ejemplo n.º 7
0
    def _v_layer_mode_combo_query_tooltip_cb(self, combo, x, y, kbd, tooltip):

        label, desc = MODE_STRINGS.get(self._layer.mode, (None, None))
        if not (label and desc):
            return False
        template = self._LAYER_MODE_TOOLTIP_MARKUP_TEMPLATE
        markup = template.format(
            name = lib.xml.escape(label),
            description = lib.xml.escape(desc),
        )
        tooltip.set_markup(markup)
        return True
Ejemplo n.º 8
0
 def _layer_mode_combo_changed_cb(self, *ignored):
     """Propagate the user's choice of layer mode to the model"""
     if self._processing_model_updates:
         return
     docmodel = self.app.doc.model
     combo = self._layer_mode_combo
     model = combo.get_model()
     mode = model.get_value(combo.get_active_iter(), 0)
     if docmodel.layer_stack.current.mode == mode:
         return
     label, desc = MODE_STRINGS.get(mode)
     docmodel.set_current_layer_mode(mode)
Ejemplo n.º 9
0
    def init_view(self):
        """Set initial state of the view objects."""

        # 3-column mode liststore (id, name, sensitive)
        store = Gtk.ListStore(int, str, bool)
        modes = STACK_MODES + STANDARD_MODES
        for mode in modes:
            label, desc = MODE_STRINGS.get(mode)
            store.append([mode, label, True])
        self._store = store
        self.view.layer_mode_combo.set_model(store)

        # The eye button is greyed out while the view is locked.
        lvm = self._docmodel.layer_view_manager
        lvm.current_view_changed += self._m_current_view_changed_cb

        # Update to the current state of the model
        self._m2v_all()
Ejemplo n.º 10
0
    def init_view(self):
        """Set initial state of the view objects."""

        # 3-column mode liststore (id, name, sensitive)
        store = Gtk.ListStore(int, str, bool)
        modes = STACK_MODES + STANDARD_MODES
        for mode in modes:
            label, desc = MODE_STRINGS.get(mode)
            store.append([mode, label, True])
        self._store = store
        self.view.layer_mode_combo.set_model(store)

        # The eye button is greyed out while the view is locked.
        lvm = self._docmodel.layer_view_manager
        lvm.current_view_changed += self._m_current_view_changed_cb

        # Update to the curent state of the model
        self._m2v_all()
Ejemplo n.º 11
0
    def _ensure_view_connected(self):
        if self._builder:
            return
        builder_xml = os.path.splitext(__file__)[0] + ".glade"
        builder = Gtk.Builder()
        builder.set_translation_domain("mypaint")
        builder.add_from_file(builder_xml)
        builder.connect_signals(self)
        self._builder = builder

        # 3-column mode liststore (id, name, sensitive)
        store = self._get_view_object("layer-mode-liststore")
        store.clear()
        modes = STACK_MODES + STANDARD_MODES
        for mode in modes:
            label, desc = MODE_STRINGS.get(mode)
            store.append([mode, label, True])

        # Update to the curent state of the model
        self._layer = self._root.current
        self._m2v_all()
Ejemplo n.º 12
0
    def __init__(self):
        GObject.GObject.__init__(self)
        from application import get_app
        app = get_app()
        self.app = app
        self.set_spacing(widgets.SPACING_CRAMPED)
        self.set_border_width(widgets.SPACING_TIGHT)
        # GtkTreeView init
        docmodel = app.doc.model
        view = layers.RootStackTreeView(docmodel)
        self._treemodel = view.get_model()
        self._treeview = view
        # RootStackTreeView events
        view.current_layer_rename_requested += self._layer_properties_cb
        view.current_layer_changed += self._blink_current_layer_cb
        view.current_layer_menu_requested += self._popup_menu_cb
        # Drag and drop
        view.drag_began += self._view_drag_began_cb
        view.drag_ended += self._view_drag_ended_cb
        statusbar_cid = app.statusbar.get_context_id(self.STATUSBAR_CONTEXT)
        self._drag_statusbar_context_id = statusbar_cid
        # View scrolls
        view_scroll = Gtk.ScrolledWindow()
        view_scroll.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
        vscroll_pol = Gtk.PolicyType.ALWAYS
        hscroll_pol = Gtk.PolicyType.AUTOMATIC
        view_scroll.set_policy(hscroll_pol, vscroll_pol)
        view_scroll.add(view)
        view_scroll.set_size_request(-1, 200)
        view_scroll.set_hexpand(True)
        view_scroll.set_vexpand(True)
        # Context menu
        ui_dir = os.path.dirname(os.path.abspath(__file__))
        ui_path = os.path.join(ui_dir, "layerswindow.xml")
        self.app.ui_manager.add_ui_from_file(ui_path)
        menu = self.app.ui_manager.get_widget("/LayersWindowPopup")
        menu.set_title(_("Layer"))
        self.connect("popup-menu", self._popup_menu_cb)
        menu.attach_to_widget(self, None)
        self._menu = menu
        self._layer_specific_ui_mergeids = []
        self._layer_specific_ui_class = None

        # Main layout grid
        grid = Gtk.Grid()
        grid.set_row_spacing(widgets.SPACING_TIGHT)
        grid.set_column_spacing(widgets.SPACING)

        # Mode dropdown
        row = 0
        label = Gtk.Label(label=_('Mode:'))
        label.set_tooltip_text(
            _("Blending mode: how the current layer combines with the "
              "layers underneath it."))
        label.set_alignment(0, 0.5)
        label.set_hexpand(False)
        grid.attach(label, 0, row, 1, 1)

        store = Gtk.ListStore(int, str, bool)
        modes = STACK_MODES + STANDARD_MODES
        for mode in modes:
            label, desc = MODE_STRINGS.get(mode)
            store.append([mode, label, True])
        combo = Gtk.ComboBox()
        combo.set_model(store)
        combo.set_hexpand(True)
        cell = Gtk.CellRendererText()
        combo.pack_start(cell, True)
        combo.add_attribute(cell, "text", 1)
        combo.add_attribute(cell, "sensitive", 2)
        self._layer_mode_combo = combo

        grid.attach(combo, 1, row, 5, 1)

        # Opacity slider
        row += 1
        opacity_lbl = Gtk.Label(label=_('Opacity:'))
        opacity_lbl.set_tooltip_text(
            _("Layer opacity: how much of the current layer to use. "
              "Smaller values make it more transparent."))
        opacity_lbl.set_alignment(0, 0.5)
        opacity_lbl.set_hexpand(False)
        adj = Gtk.Adjustment(lower=0, upper=100,
                             step_incr=1, page_incr=10)
        self._opacity_scale = Gtk.HScale.new(adj)
        self._opacity_scale.set_draw_value(False)
        self._opacity_scale.set_hexpand(True)
        grid.attach(opacity_lbl, 0, row, 1, 1)
        grid.attach(self._opacity_scale, 1, row, 5, 1)
        # Layer list and controls
        row += 1
        layersbox = Gtk.VBox()
        style = layersbox.get_style_context()
        style.add_class(Gtk.STYLE_CLASS_LINKED)
        style = view_scroll.get_style_context()
        style.set_junction_sides(Gtk.JunctionSides.BOTTOM)
        list_tools = inline_toolbar(
            self.app,
            [
                ("NewLayerGroupAbove", "mypaint-layer-group-new-symbolic"),
                ("NewPaintingLayerAbove", "mypaint-add-symbolic"),
                ("RemoveLayer", "mypaint-remove-symbolic"),
                ("RaiseLayerInStack", "mypaint-up-symbolic"),
                ("LowerLayerInStack", "mypaint-down-symbolic"),
                ("DuplicateLayer", None),
                ("MergeLayerDown", None),
            ]
        )
        style = list_tools.get_style_context()
        style.set_junction_sides(Gtk.JunctionSides.TOP)
        layersbox.pack_start(view_scroll, True, True, 0)
        layersbox.pack_start(list_tools, False, False, 0)
        layersbox.set_hexpand(True)
        layersbox.set_vexpand(True)
        grid.attach(layersbox, 0, row, 6, 1)
        # Background layer controls
        row += 1
        show_bg_btn = Gtk.CheckButton()
        change_bg_act = self.app.find_action("BackgroundWindow")
        change_bg_btn = widgets.borderless_button(action=change_bg_act)
        show_bg_act = self.app.find_action("ShowBackgroundToggle")
        show_bg_btn.set_related_action(show_bg_act)
        grid.attach(show_bg_btn, 0, row, 5, 1)
        grid.attach(change_bg_btn, 5, row, 1, 1)
        # Pack
        self.pack_start(grid, False, True, 0)
        # Updates from the real layers tree (TODO: move to lib/layers.py)
        self._processing_model_updates = False
        self._opacity_scale.connect('value-changed',
                                    self._opacity_scale_changed_cb)
        self._layer_mode_combo.connect('changed',
                                       self._layer_mode_combo_changed_cb)
        rootstack = docmodel.layer_stack
        rootstack.layer_properties_changed += self._layer_propchange_cb
        rootstack.current_path_updated += self._current_path_updated_cb
        # Initial update
        self.connect("show", self._show_cb)
Ejemplo n.º 13
0
    def __init__(self):
        GObject.GObject.__init__(self)
        from application import get_app
        app = get_app()
        self.app = app
        self.set_spacing(widgets.SPACING_CRAMPED)
        self.set_border_width(widgets.SPACING_TIGHT)
        # GtkTreeView init
        docmodel = app.doc.model
        view = layers.RootStackTreeView(docmodel)
        self._treemodel = view.get_model()
        self._treeview = view
        # RootStackTreeView events
        view.current_layer_rename_requested += self._layer_properties_cb
        view.current_layer_changed += self._blink_current_layer_cb
        view.current_layer_menu_requested += self._popup_menu_cb
        # Drag and drop
        view.drag_began += self._view_drag_began_cb
        view.drag_ended += self._view_drag_ended_cb
        statusbar_cid = app.statusbar.get_context_id(self.STATUSBAR_CONTEXT)
        self._drag_statusbar_context_id = statusbar_cid
        # View scrolls
        view_scroll = Gtk.ScrolledWindow()
        view_scroll.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
        vscroll_pol = Gtk.PolicyType.ALWAYS
        hscroll_pol = Gtk.PolicyType.AUTOMATIC
        view_scroll.set_policy(hscroll_pol, vscroll_pol)
        view_scroll.add(view)
        view_scroll.set_size_request(-1, 200)
        view_scroll.set_hexpand(True)
        view_scroll.set_vexpand(True)
        # Context menu
        ui_dir = os.path.dirname(os.path.abspath(__file__))
        ui_path = os.path.join(ui_dir, "layerswindow.xml")
        self.app.ui_manager.add_ui_from_file(ui_path)
        menu = self.app.ui_manager.get_widget("/LayersWindowPopup")
        menu.set_title(_("Layer"))
        self.connect("popup-menu", self._popup_menu_cb)
        menu.attach_to_widget(self, None)
        self._menu = menu
        self._layer_specific_ui_mergeids = []
        self._layer_specific_ui_class = None

        # Main layout grid
        grid = Gtk.Grid()
        grid.set_row_spacing(widgets.SPACING_TIGHT)
        grid.set_column_spacing(widgets.SPACING)
        row = -1

        # Visibility set management
        row += 1
        layer_view_ui = gui.layervis.LayerViewUI(docmodel)
        grid.attach(layer_view_ui.widget, 0, row, 6, 1)
        self._layer_view_ui = layer_view_ui

        # Mode dropdown
        row += 1
        # ComboBox w/ list model (mode_num, label, sensitive, scale)
        store = Gtk.ListStore(int, str, bool, float)
        modes = list(STACK_MODES + STANDARD_MODES)
        modes.remove(DEFAULT_MODE)
        modes.insert(0, DEFAULT_MODE)
        for mode in modes:
            label, desc = MODE_STRINGS.get(mode)
            sensitive = True
            scale = 1/1.2   # PANGO_SCALE_SMALL
            store.append([mode, label, sensitive, scale])
        combo = Gtk.ComboBox()
        combo.set_model(store)
        combo.set_hexpand(True)
        combo.set_vexpand(False)
        cell = Gtk.CellRendererText()
        combo.pack_start(cell, True)
        combo.add_attribute(cell, "text", 1)
        combo.add_attribute(cell, "sensitive", 2)
        combo.add_attribute(cell, "scale", 3)
        combo.set_wrap_width(2)
        combo.set_app_paintable(True)
        self._layer_mode_combo = combo
        grid.attach(combo, 0, row, 5, 1)

        # Opacity widgets
        adj = Gtk.Adjustment(lower=0, upper=100,
                             step_incr=1, page_incr=10)
        sbut = Gtk.ScaleButton()
        sbut.set_adjustment(adj)
        sbut.remove(sbut.get_child())
        sbut.set_hexpand(False)
        sbut.set_vexpand(False)
        label_text_widest = self.OPACITY_LABEL_TEXT_TEMPLATE % (100,)
        label = Gtk.Label(label_text_widest)
        label.set_width_chars(len(label_text_widest))
        # prog = Gtk.ProgressBar()
        # prog.set_show_text(False)
        sbut.add(label)
        self._opacity_scale_button = sbut
        # self._opacity_progress = prog
        self._opacity_label = label
        self._opacity_adj = adj
        grid.attach(sbut, 5, row, 1, 1)

        # Layer list and controls
        row += 1
        layersbox = Gtk.VBox()
        style = layersbox.get_style_context()
        style.add_class(Gtk.STYLE_CLASS_LINKED)
        style = view_scroll.get_style_context()
        style.set_junction_sides(Gtk.JunctionSides.BOTTOM)
        list_tools = inline_toolbar(
            self.app,
            [
                ("NewLayerGroupAbove", "mypaint-layer-group-new-symbolic"),
                ("NewPaintingLayerAbove", "mypaint-add-symbolic"),
                ("RemoveLayer", "mypaint-remove-symbolic"),
                ("RaiseLayerInStack", "mypaint-up-symbolic"),
                ("LowerLayerInStack", "mypaint-down-symbolic"),
                ("DuplicateLayer", None),
                ("MergeLayerDown", None),
            ]
        )
        style = list_tools.get_style_context()
        style.set_junction_sides(Gtk.JunctionSides.TOP)
        layersbox.pack_start(view_scroll, True, True, 0)
        layersbox.pack_start(list_tools, False, False, 0)
        layersbox.set_hexpand(True)
        layersbox.set_vexpand(True)
        grid.attach(layersbox, 0, row, 6, 1)

        # Background layer controls
        row += 1
        show_bg_btn = Gtk.CheckButton()
        change_bg_act = self.app.find_action("BackgroundWindow")
        change_bg_btn = widgets.borderless_button(action=change_bg_act)
        show_bg_act = self.app.find_action("ShowBackgroundToggle")
        show_bg_btn.set_related_action(show_bg_act)
        grid.attach(show_bg_btn, 0, row, 5, 1)
        grid.attach(change_bg_btn, 5, row, 1, 1)

        # Pack
        self.pack_start(grid, False, True, 0)
        # Updates from the real layers tree (TODO: move to lib/layers.py)
        self._processing_model_updates = False
        self._opacity_adj.connect('value-changed',
                                  self._opacity_adj_changed_cb)
        self._layer_mode_combo.connect('changed',
                                       self._layer_mode_combo_changed_cb)
        rootstack = docmodel.layer_stack
        rootstack.layer_properties_changed += self._layer_propchange_cb
        rootstack.current_path_updated += self._current_path_updated_cb
        # Initial update
        self.connect("show", self._show_cb)
Ejemplo n.º 14
0
    def __init__(self):
        GObject.GObject.__init__(self)
        from gui.application import get_app
        app = get_app()
        self.app = app
        self.set_spacing(widgets.SPACING_CRAMPED)
        self.set_border_width(widgets.SPACING_TIGHT)
        # GtkTreeView init
        docmodel = app.doc.model
        view = layers.RootStackTreeView(docmodel)
        self._treemodel = view.get_model()
        self._treeview = view
        # RootStackTreeView events
        view.current_layer_rename_requested += self._layer_properties_cb
        view.current_layer_changed += self._blink_current_layer_cb
        view.current_layer_menu_requested += self._popup_menu_cb
        # Drag and drop
        view.drag_began += self._view_drag_began_cb
        view.drag_ended += self._view_drag_ended_cb
        statusbar_cid = app.statusbar.get_context_id(self.STATUSBAR_CONTEXT)
        self._drag_statusbar_context_id = statusbar_cid
        # View scrolls
        view_scroll = Gtk.ScrolledWindow()
        view_scroll.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
        vscroll_pol = Gtk.PolicyType.ALWAYS
        hscroll_pol = Gtk.PolicyType.AUTOMATIC
        view_scroll.set_policy(hscroll_pol, vscroll_pol)
        view_scroll.add(view)
        view_scroll.set_size_request(-1, 200)
        view_scroll.set_hexpand(True)
        view_scroll.set_vexpand(True)
        # Context menu
        ui_dir = os.path.dirname(os.path.abspath(__file__))
        ui_path = os.path.join(ui_dir, "layerswindow.xml")
        self.app.ui_manager.add_ui_from_file(ui_path)
        menu = self.app.ui_manager.get_widget("/LayersWindowPopup")
        menu.set_title(_("Layer"))
        self.connect("popup-menu", self._popup_menu_cb)
        menu.attach_to_widget(self, None)
        self._menu = menu
        self._layer_specific_ui_mergeids = []
        self._layer_specific_ui_class = None

        # Main layout grid
        grid = Gtk.Grid()
        grid.set_row_spacing(widgets.SPACING_TIGHT)
        grid.set_column_spacing(widgets.SPACING)
        row = -1

        # Visibility set management
        row += 1
        layer_view_ui = gui.layervis.LayerViewUI(docmodel)
        grid.attach(layer_view_ui.widget, 0, row, 6, 1)
        self._layer_view_ui = layer_view_ui

        # Mode dropdown
        row += 1
        # ComboBox w/ list model (mode_num, label, sensitive, scale)
        store = Gtk.ListStore(int, str, bool, float)
        modes = list(STACK_MODES + STANDARD_MODES)
        modes.remove(DEFAULT_MODE)
        modes.insert(0, DEFAULT_MODE)
        for mode in modes:
            label, desc = MODE_STRINGS.get(mode)
            sensitive = True
            scale = 1/1.2   # PANGO_SCALE_SMALL
            store.append([mode, label, sensitive, scale])
        combo = Gtk.ComboBox()
        combo.set_model(store)
        combo.set_hexpand(True)
        combo.set_vexpand(False)
        cell = Gtk.CellRendererText()
        combo.pack_start(cell, True)
        combo.add_attribute(cell, "text", 1)
        combo.add_attribute(cell, "sensitive", 2)
        combo.add_attribute(cell, "scale", 3)
        combo.set_wrap_width(2)
        combo.set_app_paintable(True)
        self._layer_mode_combo = combo
        grid.attach(combo, 0, row, 5, 1)

        # Opacity widgets
        adj = Gtk.Adjustment(lower=0, upper=100,
                             step_incr=1, page_incr=10)
        sbut = Gtk.ScaleButton()
        sbut.set_adjustment(adj)
        sbut.remove(sbut.get_child())
        sbut.set_hexpand(False)
        sbut.set_vexpand(False)
        label_text_widest = self.OPACITY_LABEL_TEXT_TEMPLATE % (100,)
        label = Gtk.Label(label_text_widest)
        label.set_width_chars(len(label_text_widest))
        # prog = Gtk.ProgressBar()
        # prog.set_show_text(False)
        sbut.add(label)
        self._opacity_scale_button = sbut
        # self._opacity_progress = prog
        self._opacity_label = label
        self._opacity_adj = adj
        grid.attach(sbut, 5, row, 1, 1)

        # Layer list and controls
        row += 1
        layersbox = Gtk.VBox()
        style = layersbox.get_style_context()
        style.add_class(Gtk.STYLE_CLASS_LINKED)
        style = view_scroll.get_style_context()
        style.set_junction_sides(Gtk.JunctionSides.BOTTOM)
        list_tools = inline_toolbar(
            self.app,
            [
                ("NewLayerGroupAbove", "mypaint-layer-group-new-symbolic"),
                ("NewPaintingLayerAbove", "mypaint-add-symbolic"),
                ("RemoveLayer", "mypaint-remove-symbolic"),
                ("RaiseLayerInStack", "mypaint-up-symbolic"),
                ("LowerLayerInStack", "mypaint-down-symbolic"),
                ("DuplicateLayer", None),
                ("MergeLayerDown", None),
            ]
        )
        style = list_tools.get_style_context()
        style.set_junction_sides(Gtk.JunctionSides.TOP)
        layersbox.pack_start(view_scroll, True, True, 0)
        layersbox.pack_start(list_tools, False, False, 0)
        layersbox.set_hexpand(True)
        layersbox.set_vexpand(True)
        grid.attach(layersbox, 0, row, 6, 1)

        # Background layer controls
        row += 1
        show_bg_btn = Gtk.CheckButton()
        change_bg_act = self.app.find_action("BackgroundWindow")
        change_bg_btn = widgets.borderless_button(action=change_bg_act)
        show_bg_act = self.app.find_action("ShowBackgroundToggle")
        show_bg_btn.set_related_action(show_bg_act)
        grid.attach(show_bg_btn, 0, row, 5, 1)
        grid.attach(change_bg_btn, 5, row, 1, 1)

        # Pack
        self.pack_start(grid, False, True, 0)
        # Updates from the real layers tree (TODO: move to lib/layers.py)
        self._processing_model_updates = False
        self._opacity_adj.connect('value-changed',
                                  self._opacity_adj_changed_cb)
        self._layer_mode_combo.connect('changed',
                                       self._layer_mode_combo_changed_cb)
        rootstack = docmodel.layer_stack
        rootstack.layer_properties_changed += self._layer_propchange_cb
        rootstack.current_path_updated += self._current_path_updated_cb
        # Initial update
        self.connect("show", self._show_cb)