Commit e4febd6e authored by Jean Felder's avatar Jean Felder

views: Merge empty views and InitialStateView

EmptyView, EmptySearchView and InitialStateView are almost
identical. The only differences are the displayed icon and two labels.
Create one EmptyView class which handles the three cases.

Update Window class to switch more easily between empty and player
states.
parent 68da53f7
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.0 -->
<object class="GtkBox" id="container">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<template class="EmptyView" parent="GtkStack">
<property name="transition_type">crossfade</property>
<child>
<object class="GtkImage" id="icon">
<object class="GtkBox" id="_container">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="pixel_size">128</property>
<property name="icon_name">emblem-music-symbolic</property>
<property name="icon-size">0</property>
<property name="margin-bottom">18</property>
<style>
<class name="grey-image"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box">
<property name="orientation">vertical</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<object class="GtkLabel" id="label">
<object class="GtkImage" id="_icon">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">No music found</property>
<property name="halign">center</property>
<property name="xalign">0</property>
<property name="margin-bottom">12</property>
<property name="valign">center</property>
<property name="pixel_size">128</property>
<property name="icon_name">emblem-music-symbolic</property>
<property name="icon_size">0</property>
<property name="margin_bottom">18</property>
<style>
<class name="no-music-found-label"/>
<class name="dim-label"/>
<class name="grey-image"/>
</style>
</object>
<packing>
......@@ -49,15 +32,43 @@
</packing>
</child>
<child>
<object class="GtkLabel" id="empty-state-label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes" comments="Translators: %s will be replaced with a link with text 'Music folder'">The contents of your %s will appear here.</property>
<property name="justify">left</property>
<property name="use_markup">true</property>
<style>
<class name="dim-label"/>
</style>
<object class="GtkBox" id="box">
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel" id="_main_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="xalign">0</property>
<property name="margin_bottom">12</property>
<style>
<class name="no-music-found-label"/>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="_information_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="justify">left</property>
<property name="use_markup">true</property>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
......@@ -66,11 +77,6 @@
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</template>
</interface>
......@@ -10,11 +10,11 @@
<file preprocess="xml-stripblanks">AlbumWidget.ui</file>
<file preprocess="xml-stripblanks">ArtistAlbumWidget.ui</file>
<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">SelectionToolbar.ui</file>
<file preprocess="xml-stripblanks">SongWidget.ui</file>
<file preprocess="xml-stripblanks">headerbar.ui</file>
<file preprocess="xml-stripblanks">NoMusic.ui</file>
<file preprocess="xml-stripblanks">PlaylistContextMenu.ui</file>
<file preprocess="xml-stripblanks">PlaylistControls.ui</file>
<file preprocess="xml-stripblanks">PlaylistDialog.ui</file>
......
......@@ -135,7 +135,7 @@ class Toolbar(GObject.GObject):
current_view = self._window.curr_view
if not ((current_view == self._window.views[View.SEARCH]
or current_view == self._window.views[View.EMPTY_SEARCH])
or current_view == self._window.views[View.EMPTY])
and visible_child != current_view._grid):
self.set_state(ToolbarState.MAIN)
else:
......
......@@ -29,12 +29,12 @@ from gettext import gettext as _
class View(IntEnum):
"""Enum for views"""
ALBUM = 0
ARTIST = 1
SONG = 2
PLAYLIST = 3
SEARCH = 4
EMPTY_SEARCH = 5
EMPTY = 0
ALBUM = 1
ARTIST = 2
SONG = 3
PLAYLIST = 4
SEARCH = 5
def get_album_title(item):
......
# Copyright (c) 2016 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
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# GNOME Music is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with GNOME Music; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# The GNOME Music authors hereby grant permission for non-GPL compatible
# GStreamer plugins to be used and distributed together with GStreamer
# and GNOME Music. This permission is above and beyond the permissions
# granted by the GPL license by which GNOME Music is covered. If you
# modify this code, you may extend this exception to your version of the
# 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 gi.repository import Gd, Gtk
from gnomemusic import log
from gnomemusic.toolbar import ToolbarState
from gnomemusic.utils import View
from gnomemusic.views.baseview import BaseView
class EmptySearchView(BaseView):
def __repr__(self):
return '<EmptySearchView>'
@log
def __init__(self, window, player):
super().__init__('emptysearch', None, window, Gd.MainViewType.LIST)
self._artist_albums_widget = None
self.player = player
builder = Gtk.Builder()
builder.add_from_resource('/org/gnome/Music/NoMusic.ui')
widget = builder.get_object('container')
widget.set_vexpand(True)
widget.set_hexpand(True)
widget.get_children()[1].get_children()[1].set_text(
_("Try a different search"))
widget.show_all()
self._box.add(widget)
@log
def _back_button_clicked(self, widget, data=None):
self._header_bar.searchbar.reveal(True, False)
if self.get_visible_child() == self._artist_albums_widget:
self._artist_albums_widget.destroy()
self._artist_albums_widget = None
elif self.get_visible_child() == self._grid:
self._window.views[View.ALBUM].set_visible_child(
self._window.views[View.ALBUM]._grid)
self._window.toolbar.set_state(ToolbarState.CHILD_VIEW)
self.set_visible_child(self._grid)
......@@ -22,31 +22,101 @@
# 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 enum import IntEnum
from gettext import gettext as _
from gi.repository import Gtk
from gi.repository import GObject, Gtk
from gnomemusic import log
from gnomemusic.albumartcache import Art
from gnomemusic.query import Query
@Gtk.Template(resource_path="/org/gnome/Music/EmptyView.ui")
class EmptyView(Gtk.Stack):
"""Empty view when there is no music to display
This view can have three states
INITIAL means that Music app has never been initialized and no music
has been found
EMPTY means that no music has been found at startup
SEARCH is the empty search view: no music found during a search
"""
class State(IntEnum):
"""Enum for EmptyView state."""
INITIAL = 0
EMPTY = 1
SEARCH = 2
__gtype_name__ = "EmptyView"
_information_label = Gtk.Template.Child()
_main_label = Gtk.Template.Child()
_icon = Gtk.Template.Child()
def __repr__(self):
return '<EmptyView>'
return "<EmptyView>"
@log
def __init__(self):
super().__init__()
href_text = "<a href='{}'>{}</a>".format(
Query.MUSIC_URI, _("Music folder"))
# TRANSLATORS: This is a label to display a link to open user's music
# folder. {} will be replaced with the translated text 'Music folder'
folder_text = _("The contents of your {} will appear here.")
self._content_text = folder_text.format(href_text)
self._state = EmptyView.State.INITIAL
@GObject.Property(type=int, default=0, minimum=0, maximum=2)
@log
def __init__(self, window, player):
super().__init__(transition_type=Gtk.StackTransitionType.CROSSFADE)
self.builder = Gtk.Builder()
self.builder.add_from_resource('/org/gnome/Music/NoMusic.ui')
widget = self.builder.get_object('container')
self.update_empty_state_link()
self.add(widget)
def state(self):
"""Get the state of the empty view
:returns: The view state
:rtype: int
"""
return self._state
@state.setter
@log
def state(self, value):
"""Set the state of the empty view
:param int value: new state
"""
self._state = value
if self._state == EmptyView.State.INITIAL:
self._set_initial_state()
elif self._state == EmptyView.State.EMPTY:
self._set_empty_state()
elif self._state == EmptyView.State.SEARCH:
self._set_search_state()
self.show_all()
def update_empty_state_link(self):
label = self.builder.get_object('empty-state-label')
href_text = '<a href="%s">%s</a>' % (Query.MUSIC_URI,
_("Music folder"))
label.set_label(label.get_label() % href_text)
@log
def _set_initial_state(self):
self._information_label.props.label = self._content_text
self._main_label.props.label = _("Hey DJ")
self._main_label.props.margin_bottom = 18
self._icon.props.resource = "/org/gnome/Music/initial-state.png"
self._icon.props.margin_bottom = 32
self._icon.props.height_request = Art.Size.LARGE.height
self._icon.props.width_request = Art.Size.LARGE.width
@log
def _set_empty_state(self):
self._main_label.props.label = _("No music found")
self._information_label.props.label = self._content_text
@log
def _set_search_state(self):
self._main_label.props.margin_bottom = 12
self._main_label.props.label = _("No music found")
self._icon.props.margin_bottom = 18
self._information_label.props.label = _("Try a different search")
# Copyright (c) 2016 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
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# GNOME Music is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with GNOME Music; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# The GNOME Music authors hereby grant permission for non-GPL compatible
# GStreamer plugins to be used and distributed together with GStreamer
# and GNOME Music. This permission is above and beyond the permissions
# granted by the GPL license by which GNOME Music is covered. If you
# modify this code, you may extend this exception to your version of the
# 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 gnomemusic import log
from gnomemusic.albumartcache import Art
from gnomemusic.views.emptyview import EmptyView
class InitialStateView(EmptyView):
def __repr__(self):
return '<InitialStateView>'
@log
def __init__(self, window, player):
super().__init__(window, player)
# Update image
icon = self.builder.get_object('icon')
icon.set_margin_bottom(32)
icon.set_opacity(1)
icon.set_from_resource('/org/gnome/Music/initial-state.png')
icon.set_size_request(Art.Size.LARGE.width, Art.Size.LARGE.height)
# Update label
label = self.builder.get_object('label')
label.set_label(_("Hey DJ"))
label.set_opacity(1)
label.set_margin_bottom(18)
......@@ -82,9 +82,7 @@ class SearchView(BaseView):
@log
def _no_music_found_callback(self, view):
# FIXME: call into private members
self._window._stack.set_visible_child_name('emptysearch')
emptysearch = self._window._stack.get_child_by_name('emptysearch')
emptysearch._artist_albums_widget = self._artist_albums_widget
self._window._stack.set_visible_child_name("emptyview")
@log
def _back_button_clicked(self, widget, data=None):
......@@ -215,7 +213,7 @@ class SearchView(BaseView):
self.emit('no-music-found')
# We need to remember the view before the search view
emptysearchview = self._window.views[View.EMPTY_SEARCH]
emptysearchview = self._window.views[View.EMPTY]
if (self._window.curr_view != emptysearchview
and self._window.prev_view != emptysearchview):
self.previous_view = self._window.prev_view
......
......@@ -40,8 +40,6 @@ from gnomemusic.utils import View
from gnomemusic.views.albumsview import AlbumsView
from gnomemusic.views.artistsview import ArtistsView
from gnomemusic.views.emptyview import EmptyView
from gnomemusic.views.emptysearchview import EmptySearchView
from gnomemusic.views.initialstateview import InitialStateView
from gnomemusic.views.searchview import SearchView
from gnomemusic.views.songsview import SongsView
from gnomemusic.views.playlistview import PlaylistView
......@@ -111,23 +109,20 @@ class Window(Gtk.ApplicationWindow):
def _on_changes_pending(self, data=None):
# FIXME: This is not working right.
def songs_available_cb(available):
if available:
if self.views[View.ALBUM] == self.views[-1]:
view = self.views.pop()
view.destroy()
self._switch_to_player_view()
self.toolbar._search_button.set_sensitive(True)
self.toolbar._select_button.set_sensitive(True)
self.toolbar.show_stack()
elif (self.toolbar.selection_mode is False
and len(self.views) != 1):
view_count = len(self.views)
if (available
and view_count == 1):
self._switch_to_player_view()
elif (not available
and not self.toolbar.selection_mode
and view_count != 1):
self._stack.disconnect(self._on_notify_model_id)
self.disconnect(self._key_press_event_id)
view_count = len(self.views)
for i in range(0, view_count):
for i in range(View.ALBUM, view_count):
view = self.views.pop()
view.destroy()
self.toolbar.hide_stack()
self._switch_to_empty_view()
grilo.songs_available(songs_available_cb)
......@@ -225,6 +220,13 @@ class Window(Gtk.ApplicationWindow):
visible=True,
can_focus=False)
# Create only the empty view at startup
# if no music, switch to empty view and hide stack
# if some music is available, populate stack with mainviews,
# show stack and set empty_view to empty_search_view
self.views[View.EMPTY] = EmptyView()
self._stack.add_named(self.views[View.EMPTY], "emptyview")
# Add the 'background' styleclass so it properly hides the
# bottom line of the searchbar
self._stack.get_style_context().add_class('background')
......@@ -265,14 +267,13 @@ class Window(Gtk.ApplicationWindow):
@log
def _switch_to_empty_view(self):
did_initial_state = self.settings.get_boolean('did-initial-state')
view_class = None
if did_initial_state:
view_class = EmptyView
self.views[View.EMPTY].props.state = EmptyView.State.EMPTY
else:
view_class = InitialStateView
self.views[View.ALBUM] = view_class(self, self.player)
self.views[View.EMPTY].props.state = EmptyView.State.INITIAL
self._stack.add_titled(self.views[View.ALBUM], _("Empty"), _("Empty"))
self.toolbar.hide_stack()
self.toolbar._search_button.set_sensitive(False)
self.toolbar._select_button.set_sensitive(False)
......@@ -290,20 +291,26 @@ class Window(Gtk.ApplicationWindow):
self.views[View.SONG] = SongsView(self, self.player)
self.views[View.PLAYLIST] = PlaylistView(self, self.player)
self.views[View.SEARCH] = SearchView(self, self.player)
self.views[View.EMPTY_SEARCH] = EmptySearchView(self, self.player)
for i in self.views:
# empty view has already been created in self._setup_view starting at
# View.ALBUM
# empty view state is changed once album view is visible to prevent it
# from being displayed during startup
for i in self.views[View.ALBUM:]:
if i.title:
self._stack.add_titled(i, i.name, i.title)
else:
self._stack.add_named(i, i.name)
GLib.idle_add(i.populate)
self._stack.set_visible_child(self.views[View.ALBUM])
self.views[View.EMPTY].props.state = EmptyView.State.SEARCH
self.toolbar.set_stack(self._stack)
self.toolbar.searchbar.show()
self.toolbar.dropdown.show()
for i in self.views:
GLib.idle_add(i.populate)
self.toolbar._search_button.set_sensitive(True)
self.toolbar._select_button.set_sensitive(True)
self.toolbar.show_stack()
@log
def _select_all(self, action=None, param=None):
......@@ -451,7 +458,7 @@ class Window(Gtk.ApplicationWindow):
# Switch to all albums view when we're clicking Albums
if (self.curr_view == self.views[View.ALBUM]
and not (self.prev_view == self.views[View.SEARCH]
or self.prev_view == self.views[View.EMPTY_SEARCH])):
or self.prev_view == self.views[View.EMPTY])):
self.curr_view.set_visible_child(self.curr_view._grid)
# Slide out sidebar on switching to Artists or Playlists view
......@@ -459,13 +466,14 @@ class Window(Gtk.ApplicationWindow):
self.curr_view == self.views[View.PLAYLIST]:
self.curr_view.stack.set_visible_child_name('dummy')
self.curr_view.stack.set_visible_child_name('sidebar')
if self.curr_view != self.views[View.SEARCH] and self.curr_view != self.views[View.EMPTY_SEARCH]:
if (self.curr_view != self.views[View.SEARCH]
and self.curr_view != self.views[View.EMPTY]):
self.toolbar.searchbar.reveal(False)
# Disable the selection button for the EmptySearch and Playlist
# view
no_selection_mode = [
self.views[View.EMPTY_SEARCH],
self.views[View.EMPTY],
self.views[View.PLAYLIST]
]
self.toolbar._select_button.set_sensitive(
......@@ -487,7 +495,7 @@ class Window(Gtk.ApplicationWindow):
button.get_active(), self.curr_view != self.views[View.SEARCH])
if (not button.get_active()
and (self.curr_view == self.views[View.SEARCH]
or self.curr_view == self.views[View.EMPTY_SEARCH])):
or self.curr_view == self.views[View.EMPTY])):
child = self.curr_view.get_visible_child()
if self.toolbar._state == ToolbarState.MAIN:
# We should get back to the view before the search
......
......@@ -17,9 +17,7 @@ gnomemusic/utils.py
gnomemusic/views/albumsview.py
gnomemusic/views/artistsview.py
gnomemusic/views/baseview.py
gnomemusic/views/emptysearchview.py
gnomemusic/views/emptyview.py
gnomemusic/views/initialstateview.py
gnomemusic/views/playlistview.py
gnomemusic/views/searchview.py
gnomemusic/views/songsview.py
......@@ -37,7 +35,6 @@ data/AlbumWidget.ui
data/app-menu.ui
data/headerbar.ui
data/help-overlay.ui
data/NoMusic.ui
data/PlayerToolbar.ui
data/PlaylistContextMenu.ui
data/PlaylistControls.ui
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment