Commit c76d243b authored by Marinus Schraal's avatar Marinus Schraal

toolbar: Rename to HeaderBar and cleanup

Rework the widget, highlights:
 * Rename Toolbar to HeaderBar
 * Split out the menu button to SelectionBarMenuButton
 * Use Gtk.Template for SelectionBarMenuButton
 * Make State internal to HeaderBar
 * Use Gtk.Template for HeaderBar
 * Use the state property directly in callers
 * Add selection-mode property to HeaderBar
parent 7b864087
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.10 -->
<menu id="selection-menu">
<section>
<item>
<attribute name="label" translatable="yes">Select All</attribute>
<attribute name="action">win.select_all</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Select None</attribute>
<attribute name="action">win.select_none</attribute>
</item>
</section>
</menu>
<object class="GtkMenuButton" id="selection-menu-button">
<property name="menu-model">selection-menu</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="relief">none</property>
<property name="can-focus">False</property>
<child>
<object class="GtkBox" id="selection-menu-button-box">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">horizontal</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="selection-menu-button-label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Click on items to select them</property>
</object>
<packing>
<property name="pack-type">start</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkArrow" id="selection-menu-button-arrow">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="arrow-type">down</property>
<property name="shadow-type">none</property>
</object>
<packing>
<property name="pack-type">start</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<style>
<class name="selection-menu"/>
</style>
</object>
<object class="GtkHeaderBar" id="header-bar">
<template class="HeaderBar" parent="GtkHeaderBar">
<property name="visible">True</property>
<property name="vexpand">False</property>
<child>
<object class="GtkToggleButton" id="search-button">
<object class="GtkToggleButton" id="_search_button">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">center</property>
......@@ -68,7 +15,7 @@
<class name="image-button"/>
</style>
<child>
<object class="GtkImage" id="search-button-image">
<object class="GtkImage" id="_search_button_image">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon-name">edit-find-symbolic</property>
......@@ -81,7 +28,7 @@
</packing>
</child>
<child>
<object class="GtkToggleButton" id="select-button">
<object class="GtkToggleButton" id="_select_button">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">center</property>
......@@ -91,7 +38,7 @@
<class name="image-button"/>
</style>
<child>
<object class="GtkImage" id="select-button-image">
<object class="GtkImage" id="_select_button_image">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon-name">object-select-symbolic</property>
......@@ -104,7 +51,7 @@
</packing>
</child>
<child>
<object class="GtkButton" id="done-button">
<object class="GtkButton" id="_cancel_button">
<property name="visible">False</property>
<property name="no_show_all">True</property>
<property name="can_focus">False</property>
......@@ -121,17 +68,18 @@
</packing>
</child>
<child>
<object class="GtkButton" id="back-button">
<object class="GtkButton" id="_back_button">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">center</property>
<property name="sensitive">True</property>
<property name="tooltip_text" translatable="yes">Back</property>
<signal name="clicked" handler="_on_back_button_clicked" swapped="no"/>
<style>
<class name="image-button"/>
</style>
<child>
<object class="GtkImage" id="back-button-image">
<object class="GtkImage" id="_back_button_image">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon-name">go-previous-symbolic</property>
......@@ -143,12 +91,12 @@
<property name="pack_type">start</property>
</packing>
</child>
</object>
</template>
<object class="GtkSizeGroup" id="size1">
<property name="mode">vertical</property>
<widgets>
<widget name="search-button"/>
<widget name="done-button"/>
<widget name="_search_button"/>
<widget name="_cancel_button"/>
</widgets>
</object>
</interface>
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.10 -->
<menu id="selection-menu">
<section>
<item>
<attribute name="label" translatable="yes">Select All</attribute>
<attribute name="action">win.select_all</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Select None</attribute>
<attribute name="action">win.select_none</attribute>
</item>
</section>
</menu>
<template class="SelectionBarMenuButton" parent="GtkMenuButton">
<property name="menu-model">selection-menu</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="relief">none</property>
<property name="can-focus">False</property>
<child>
<object class="GtkBox" id="selection-menu-button-box">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">horizontal</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="_menu_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Click on items to select them</property>
</object>
<packing>
<property name="pack-type">start</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkArrow" id="selection-menu-button-arrow">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="arrow-type">down</property>
<property name="shadow-type">none</property>
</object>
<packing>
<property name="pack-type">start</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<style>
<class name="selection-menu"/>
</style>
</template>
</interface>
......@@ -12,9 +12,10 @@
<file preprocess="xml-stripblanks">ArtistAlbumsWidget.ui</file>
<file preprocess="xml-stripblanks">EmptyView.ui</file>
<file preprocess="xml-stripblanks">PlayerToolbar.ui</file>
<file preprocess="xml-stripblanks">SelectionBarMenuButton.ui</file>
<file preprocess="xml-stripblanks">SelectionToolbar.ui</file>
<file preprocess="xml-stripblanks">SongWidget.ui</file>
<file preprocess="xml-stripblanks">headerbar.ui</file>
<file preprocess="xml-stripblanks">HeaderBar.ui</file>
<file preprocess="xml-stripblanks">PlaylistContextMenu.ui</file>
<file preprocess="xml-stripblanks">PlaylistControls.ui</file>
<file preprocess="xml-stripblanks">PlaylistDialog.ui</file>
......
......@@ -349,7 +349,7 @@ class Searchbar(Gtk.SearchBar):
else:
fields_filter = 'search_all'
stack = self._stack_switcher.get_stack()
stack = self._stack_switcher.props.stack
if search_term != "":
stack.set_visible_child_name('search')
view = stack.get_visible_child()
......
......@@ -30,8 +30,8 @@ from gi.repository import GLib, GObject, Gtk, Gdk
from gnomemusic import log
from gnomemusic.albumartcache import Art
from gnomemusic.grilo import grilo
from gnomemusic.toolbar import ToolbarState
from gnomemusic.views.baseview import BaseView
from gnomemusic.widgets.headerbar import HeaderBar
from gnomemusic.widgets.albumwidget import AlbumWidget
from gnomemusic.widgets.coverstack import CoverStack
import gnomemusic.utils as utils
......@@ -89,7 +89,7 @@ class AlbumsView(BaseView):
@log
def _back_button_clicked(self, widget, data=None):
self._header_bar.reset_header_title()
self._header_bar.state = HeaderBar.State.MAIN
self.set_visible_child(self._grid)
@log
......@@ -105,9 +105,9 @@ class AlbumsView(BaseView):
self._album_widget.update(
item, self._header_bar, self._selection_toolbar)
self._header_bar.set_state(ToolbarState.CHILD_VIEW)
self._header_bar.header_bar.set_title(utils.get_album_title(item))
self._header_bar.header_bar.set_subtitle(utils.get_artist_name(item))
self._header_bar.props.state = HeaderBar.State.CHILD
self._header_bar.props.title = utils.get_album_title(item)
self._header_bar.props.subtitle = utils.get_artist_name(item)
self.set_visible_child(self._album_widget)
@log
......@@ -119,7 +119,7 @@ class AlbumsView(BaseView):
def get_selected_songs(self, callback):
# FIXME: we call into private objects with full knowledge of
# what is there
if self._header_bar._state == ToolbarState.CHILD_VIEW:
if self._header_bar.props.state == HeaderBar.State.CHILD:
callback(self._album_widget._disc_listbox.get_selected_items())
else:
self.items_selected = []
......
# Copyright (c) 2016 The GNOME Music Developers
# Copyright 2018 The GNOME Music Developers
#
# GNOME Music is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -22,7 +22,8 @@
# code, but you are not obligated to do so. If you do not wish to do so,
# delete this exception statement from your version.
from gettext import gettext as _, ngettext
import gi
gi.require_version("Gd", "1.0")
from gi.repository import Gd, GdkPixbuf, GObject, Gtk
from gnomemusic import log
......@@ -91,7 +92,7 @@ class BaseView(Gtk.Stack):
self._star_handler = StarHandlerWidget(self, 9)
self._window = window
self._header_bar = window.toolbar
self._header_bar = window.headerbar
self._selection_toolbar = window.selection_toolbar
self._header_bar._select_button.connect(
'toggled', self._on_header_bar_toggled)
......@@ -107,10 +108,17 @@ class BaseView(Gtk.Stack):
self._init = False
grilo.connect('ready', self._on_grilo_ready)
self._header_bar.connect('selection-mode-changed',
self._on_selection_mode_changed)
self._header_bar.connect(
'notify::selection-mode', self._on_selection_mode_changed)
grilo.connect('changes-pending', self._on_changes_pending)
self.bind_property(
'selection-mode', self._selection_toolbar, 'visible',
GObject.BindingFlags.SYNC_CREATE)
self.bind_property(
'selection-mode', self._header_bar, 'selection-mode')
@log
def _on_changes_pending(self, data=None):
pass
......@@ -139,29 +147,25 @@ class BaseView(Gtk.Stack):
self.selection_mode = button.get_active()
if self.selection_mode:
self._header_bar.set_selection_mode(True)
self.set_player_visible(False)
select_toolbar = self._selection_toolbar
select_toolbar.set_visible(True)
select_toolbar.add_to_playlist_button.set_sensitive(False)
else:
self._header_bar.set_selection_mode(False)
self.set_player_visible(self.player.current_song is not None)
self._selection_toolbar.set_visible(False)
self.unselect_all()
@log
def _on_cancel_button_clicked(self, button):
self.unselect_all()
self._header_bar.set_selection_mode(False)
self.props.selection_mode = False
@log
def _on_grilo_ready(self, data=None):
if (self._header_bar.get_stack().get_visible_child() == self
if (self._header_bar.props.stack.props.visible_child == self
and not self._init):
self._populate()
self._header_bar.get_stack().connect('notify::visible-child',
self._on_headerbar_visible)
self._header_bar.props.stack.connect(
'notify::visible-child', self._on_headerbar_visible)
@log
def _on_headerbar_visible(self, widget, param):
......@@ -181,14 +185,7 @@ class BaseView(Gtk.Stack):
"""Updates header during item selection."""
select_toolbar = self._selection_toolbar
select_toolbar.add_to_playlist_button.set_sensitive(n_items > 0)
if n_items > 0:
self._header_bar._selection_menu_label.set_text(
ngettext("Selected {} item",
"Selected {} items",
n_items).format(n_items))
else:
self._header_bar._selection_menu_label.set_text(
_("Click on items to select them"))
self._header_bar.props.items_selected = n_items
@log
def _populate(self, data=None):
......@@ -253,8 +250,7 @@ class BaseView(Gtk.Stack):
self._set_selection(False)
select_toolbar = self._selection_toolbar
select_toolbar.add_to_playlist_button.set_sensitive(False)
self._header_bar._selection_menu_label.set_text(
_("Click on items to select them"))
self._header_bar.props.items_selected = 0
self.queue_draw()
@log
......
......@@ -31,9 +31,9 @@ from gnomemusic import log
from gnomemusic.player import DiscoveryStatus
from gnomemusic.playlists import Playlists
from gnomemusic.query import Query
from gnomemusic.toolbar import ToolbarState
from gnomemusic.utils import View
from gnomemusic.views.baseview import BaseView
from gnomemusic.widgets.headerbar import HeaderBar
from gnomemusic.widgets.albumwidget import AlbumWidget
from gnomemusic.widgets.artistalbumswidget import ArtistAlbumsWidget
import gnomemusic.utils as utils
......@@ -96,7 +96,7 @@ class SearchView(BaseView):
self._window.views[View.ALBUM]._grid)
self.set_visible_child(self._grid)
self._window.toolbar.set_state(ToolbarState.MAIN)
self._header_bar.props.state = HeaderBar.State.MAIN
@log
def _on_item_activated(self, widget, id, path):
......@@ -117,10 +117,10 @@ class SearchView(BaseView):
self._album_widget.update(
item, self._header_bar, self._selection_toolbar)
self._header_bar.set_state(ToolbarState.SEARCH_VIEW)
self._header_bar.props.state = HeaderBar.State.SEARCH
self._header_bar.header_bar.set_title(title)
self._header_bar.header_bar.set_subtitle(artist)
self._header_bar.props.title = title
self._header_bar.props.subtitle = artist
self.set_visible_child(self._album_widget)
self._header_bar.searchbar.reveal(False)
elif self.model[_iter][12] == 'artist':
......@@ -133,8 +133,8 @@ class SearchView(BaseView):
self.add(self._artist_albums_widget)
self._artist_albums_widget.show()
self._header_bar.set_state(ToolbarState.SEARCH_VIEW)
self._header_bar.header_bar.set_title(artist)
self._header_bar.props.state = HeaderBar.State.SEARCH
self._header_bar.props.title = artist
self.set_visible_child(self._artist_albums_widget)
self._header_bar.searchbar.reveal(False)
elif self.model[_iter][12] == 'song':
......@@ -344,7 +344,7 @@ class SearchView(BaseView):
@log
def populate(self):
self._init = True
self._header_bar.set_state(ToolbarState.MAIN)
self._header_bar.props.state = HeaderBar.State.MAIN
@log
def get_selected_songs(self, callback):
......
......@@ -22,7 +22,7 @@
# code, but you are not obligated to do so. If you do not wish to do so,
# delete this exception statement from your version.
from gettext import gettext as _, ngettext
from gettext import ngettext
from gi.repository import GdkPixbuf, GLib, GObject, Gtk
from gnomemusic import log
......@@ -184,20 +184,14 @@ class AlbumWidget(Gtk.EventBox):
items = self._disc_listbox.get_selected_items()
self.selection_toolbar.add_to_playlist_button.set_sensitive(
len(items) > 0)
if len(items) > 0:
self._header_bar._selection_menu_label.set_text(
ngettext("Selected %d item", "Selected %d items",
len(items)) % len(items))
else:
self._header_bar._selection_menu_label.set_text(
_("Click on items to select them"))
self._header_bar.items_selected = len(items)
@log
def _on_header_cancel_button_clicked(self, button):
"""Cancel selection mode callback."""
self._disc_listbox.props.selection_mode = False
self._header_bar.set_selection_mode(False)
self._header_bar.header_bar.title = self._album
self._header_bar.props.selection_mode = False
self._header_bar.props.title = self._album
@log
def _on_header_select_button_toggled(self, button):
......@@ -205,14 +199,12 @@ class AlbumWidget(Gtk.EventBox):
if button.get_active():
self._selection_mode = True
self._disc_listbox.props.selection_mode = True
self._header_bar.set_selection_mode(True)
self._header_bar.props.selection_mode = True
self._parent_view.set_player_visible(False)
self._header_bar.header_bar.set_custom_title(
self._header_bar._selection_menu_button)
else:
self._selection_mode = False
self._disc_listbox.props.selection_mode = False
self._header_bar.set_selection_mode(False)
self._header_bar.props.selection_mode = False
if self._player.get_playback_status() != Playback.STOPPED:
self._parent_view.set_player_visible(True)
......
......@@ -24,7 +24,6 @@
import logging
from gettext import gettext as _, ngettext
from gi.repository import GObject, Gtk
from gnomemusic import log
......@@ -207,13 +206,7 @@ class ArtistAlbumsWidget(Gtk.Box):
add_button = self._selection_toolbar.add_to_playlist_button
add_button.set_sensitive(selected_items > 0)
menu_label = self._header_bar._selection_menu_label
if selected_items > 0:
menu_label.set_text(ngettext("Selected %d item",
"Selected %d items",
selected_items) % selected_items)
else:
menu_label.set_text(_("Click on items to select them"))
self._header_bar.selected_items = selected_items
@log
def select_all(self):
......
......@@ -123,14 +123,12 @@ class ArtistAlbumWidget(Gtk.Box):
if button.get_active():
self._selection_mode = True
self._disc_listbox.props.selection_mode = True
self._header_bar.set_selection_mode(True)
self._header_bar.props.selection_mode = True
self._parent_view.set_player_visible(False)
self._header_bar.header_bar.set_custom_title(
self._header_bar._selection_menu_button)
else:
self._selection_mode = False
self._disc_listbox.props.selection_mode = False
self._header_bar.set_selection_mode(False)
self._header_bar.props.selection_mode = False
if self._player.get_playback_status() != Playback.STOPPED:
self._parent_view.set_player_visible(True)
......
# Copyright (c) 2013 Vadim Rutkovsky <vrutkovs@redhat.com>
# Copyright (c) 2013 Arnel A. Borja <kyoushuu@yahoo.com>
# Copyright (c) 2013 Eslam Mostafa <cseslam@gmail.com>
# Copyright (c) 2013 Sai Suman Prayaga <suman.sai14@gmail.com>
# Copyright (c) 2013 Seif Lotfy <seif@lotfy.com>
# Copyright (c) 2013 Guillaume Quintard <guillaume.quintard@gmail.com>
# Copyright 2018 The GNOME Music developers
#
# GNOME Music is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -27,7 +22,9 @@
# code, but you are not obligated to do so. If you do not wish to do so,
# delete this exception statement from your version.
from gettext import gettext as _
from enum import IntEnum
from gettext import gettext as _, ngettext
from gi.repository import GObject, Gtk
from gnomemusic import log
......@@ -35,20 +32,76 @@ from gnomemusic.searchbar import Searchbar, DropDown
from gnomemusic.utils import View
class ToolbarState:
MAIN = 0
CHILD_VIEW = 1
SEARCH_VIEW = 2
@Gtk.Template(resource_path="/org/gnome/Music/SelectionBarMenuButton.ui")
class SelectionBarMenuButton(Gtk.MenuButton):
"""Button for popup to select all or no items
The button label indicates the number of items selected.
"""
class Toolbar(GObject.GObject):
__gtype_name__ = "SelectionBarMenuButton"
__gsignals__ = {
'selection-mode-changed': (GObject.SignalFlags.RUN_FIRST, None, ()),
}
_menu_label = Gtk.Template.Child()
def __repr__(self):
return '<Toolbar>'
return "<SelectionBarMenuButton>"
@log
def __init__(self):
super().__init__()
self._items_selected = 0
@GObject.Property(type=int, default=0, minimum=0)
@log
def items_selected(self):
"""The number of items selected
:returns: Number of items selected
:rtype: int
"""
return self._items_selected
@items_selected.setter
@log
def items_selected(self, value):
"""Set the number of items selected
:param int value: The number of items selected
"""
self._items_selected = value
if value > 0:
text = ngettext(
"Selected {} item", "Selected {} items", value).format(value)
self._menu_label.props.label = text
else:
self._menu_label.props.label = _("Click on items to select them")
@Gtk.Template(resource_path="/org/gnome/Music/HeaderBar.ui")
class HeaderBar(Gtk.HeaderBar):
"""Headerbar of the application"""
class State(IntEnum):
"""States the Headerbar can have"""
MAIN = 0
CHILD = 1
SEARCH = 2
EMPTY = 3
__gtype_name__ = "HeaderBar"
_search_button = Gtk.Template.Child()
_select_button = Gtk.Template.Child()
_cancel_button = Gtk.Template.Child()
_back_button = Gtk.Template.Child()
items_selected = GObject.Property(type=int, default=0, minimum=0)
stack = GObject.Property(type=Gtk.Stack)
def __repr__(self):
return "<HeaderBar>"
@log
def __init__(self):
......@@ -56,109 +109,126 @@ class Toolbar(GObject.GObject):
self._selection_mode = False
self._stack_switcher = Gtk.StackSwitcher(can_focus=False,
halign="center")
self._stack_switcher = Gtk.StackSwitcher(
can_focus=False, halign="center")
self._stack_switcher.show()
self._ui = Gtk.Builder()
self._ui.add_from_resource('/org/gnome/Music/headerbar.ui')
self.header_bar = self._ui.get_object('header-bar')
self._search_button = self._ui.get_object('search-button')
self.dropdown = DropDown()
self.searchbar = Searchbar(
self._stack_switcher, self._search_button, self.dropdown)
self.dropdown.initialize_filters(self.searchbar)
self._select_button = self._ui.get_object('select-button')
self._cancel_button = self._ui.get_object('done-button')
self._back_button = self._ui.get_object('back-button')
self._selection_menu = self._ui.get_object('selection-menu')
self._selection_menu_button = self._ui.get_object(
'selection-menu-button')
self._selection_menu_label = self._ui.get_object(
'selection-menu-button-label')
self._back_button.connect('clicked', self.on_back_button_clicked)
self._window = self.header_bar.get_parent()
@GObject.Property
self._selection_menu = SelectionBarMenuButton()
self.bind_property(
"selection-mode", self, "show-close-button",
GObject.BindingFlags.INVERT_BOOLEAN |
GObject.BindingFlags.SYNC_CREATE)
self.bind_property(
"selection-mode", self._cancel_button, "visible")
self.bind_property(
"selection-mode", self._select_button, "visible",
GObject.BindingFlags.INVERT_BOOLEAN)
self.bind_property(
"stack", self._stack_switcher, "stack",
GObject.BindingFlags.BIDIRECTIONAL |
GObject.BindingFlags.SYNC_CREATE)
self.bind_property(
"items-selected", self._selection_menu, "items-selected")
@GObject.Property(type=bool, default=False)
@log
def selection_mode(self):
"""Selection mode
:returns: Selection mode
:rtype: bool
"""
return self._selection_mode
@selection_mode.setter
@log
def selection_mode(self, mode):
self.set_selection_mode(mode)
"""Set the selection mode
@log
def reset_header_title(self):
self.header_bar.set_title(_("Music"))
self.header_bar.set_custom_title(self._stack_switcher)
:param bool value: Selection mode
"""
self._selection_mode = mode
@log
def set_stack(self, stack):
self._stack_switcher.set_stack(stack)
if mode:
self.get_style_context().add_class("selection-mode")
else:
self.get_style_context().remove_class("selection-mode")
self._select_button.props.active = False
@log
def get_stack(self):
return self._stack_switcher.get_stack()
self._update()
@GObject.Property
@log
def hide_stack(self):
self._stack_switcher.hide()
def state(self):
"""State of the widget
@log
def show_stack(self):
self._stack_switcher.show()
:returns: Widget state
:rtype: HeaderBar.State
"""
return self._state
@state.setter
@log
def set_selection_mode(self, mode):
self._selection_mode = mode
if mode:
self._select_button.hide()
self._cancel_button.show()
self.header_bar.get_style_context().add_class('selection-mode')
self._cancel_button.get_style_context().remove_class(