[gnome-music/wip/jfelder/searchview-new-style: 11/15] Add SearchHeaderBar
- From: Marinus Schraal <mschraal src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-music/wip/jfelder/searchview-new-style: 11/15] Add SearchHeaderBar
- Date: Mon, 5 Aug 2019 21:39:01 +0000 (UTC)
commit b8aa7522a3bb7ba59b1fe790c4942227344713bc
Author: Marinus Schraal <mschraal gnome org>
Date: Mon Aug 5 14:53:48 2019 +0200
Add SearchHeaderBar
A Gtk.HeaderBar for search only.
data/org.gnome.Music.gresource.xml | 1 +
data/ui/SearchHeaderBar.ui | 79 ++++++++++++++
gnomemusic/widgets/searchheaderbar.py | 200 ++++++++++++++++++++++++++++++++++
gnomemusic/window.py | 43 ++++++--
4 files changed, 316 insertions(+), 7 deletions(-)
---
diff --git a/data/org.gnome.Music.gresource.xml b/data/org.gnome.Music.gresource.xml
index 2b45aaf8..cf892def 100644
--- a/data/org.gnome.Music.gresource.xml
+++ b/data/org.gnome.Music.gresource.xml
@@ -24,6 +24,7 @@
<file preprocess="xml-stripblanks">ui/PlaylistDialogRow.ui</file>
<file preprocess="xml-stripblanks">ui/PlaylistTile.ui</file>
<file preprocess="xml-stripblanks">ui/SearchBar.ui</file>
+ <file preprocess="xml-stripblanks">ui/SearchHeaderBar.ui</file>
<file preprocess="xml-stripblanks">ui/SearchView.ui</file>
<file preprocess="xml-stripblanks">ui/SelectionBarMenuButton.ui</file>
<file preprocess="xml-stripblanks">ui/SelectionToolbar.ui</file>
diff --git a/data/ui/SearchHeaderBar.ui b/data/ui/SearchHeaderBar.ui
new file mode 100644
index 00000000..f837dae3
--- /dev/null
+++ b/data/ui/SearchHeaderBar.ui
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.10 -->
+ <template class="SearchHeaderBar" parent="GtkHeaderBar">
+ <property name="visible">True</property>
+ <property name="vexpand">False</property>
+ <child>
+ <object class="GtkToggleButton" id="_select_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">Select</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <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>
+ <property name="icon-size">1</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="pack_type">end</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="_cancel_button">
+ <property name="visible">False</property>
+ <property name="no_show_all">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Cancel</property>
+ <property name="use_underline">True</property>
+ <property name="valign">center</property>
+ <property name="sensitive">True</property>
+ <signal name="clicked" handler="_on_cancel_button_clicked" swapped="no"/>
+ <style>
+ <class name="text-button"/>
+ </style>
+ </object>
+ <packing>
+ <property name="pack_type">end</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_search_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">Search</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <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>
+ <property name="icon-size">1</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="pack_type">end</property>
+ </packing>
+ </child>
+ </template>
+ <object class="GtkSizeGroup" id="size1">
+ <property name="mode">vertical</property>
+ <widgets>
+ <widget name="_search_button"/>
+ <widget name="_cancel_button"/>
+ </widgets>
+ </object>
+</interface>
diff --git a/gnomemusic/widgets/searchheaderbar.py b/gnomemusic/widgets/searchheaderbar.py
new file mode 100644
index 00000000..cbbf8839
--- /dev/null
+++ b/gnomemusic/widgets/searchheaderbar.py
@@ -0,0 +1,200 @@
+# Copyright 2019 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 enum import IntEnum
+
+from gi.repository import GLib, GObject, Gd, Gtk
+
+from gnomemusic import log
+from gnomemusic.widgets.headerbar import HeaderBar, SelectionBarMenuButton
+
+
+@Gtk.Template(resource_path="/org/gnome/Music/ui/SearchHeaderBar.ui")
+class SearchHeaderBar(Gtk.HeaderBar):
+ """SearcnHeaderbar of the application"""
+
+ class State(IntEnum):
+ """States the SearchHeaderbar can have"""
+ MAIN = 0
+ CHILD = 1
+ SEARCH = 2
+ EMPTY = 3
+
+ __gtype_name__ = "SearchHeaderBar"
+
+ _search_button = Gtk.Template.Child()
+ _select_button = Gtk.Template.Child()
+ _cancel_button = Gtk.Template.Child()
+
+ search_mode_active = GObject.Property(type=bool, default=False)
+ selected_items_count = GObject.Property(type=int, default=0, minimum=0)
+ selection_mode_allowed = GObject.Property(type=bool, default=True)
+ stack = GObject.Property(type=Gtk.Stack)
+
+ def __repr__(self):
+ return "<SearchHeaderBar>"
+
+ @log
+ def __init__(self, application):
+ super().__init__()
+
+ self._application = application
+ self._selection_mode = False
+ self._timeout = None
+
+ self._entry = Gd.TaggedEntry()
+ self._entry.props.halign = Gtk.Align.CENTER
+ self._entry.props.visible = True
+ self._entry.props.width_request = 500
+
+ 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(
+ "selection-mode", self._select_button, "active",
+ GObject.BindingFlags.BIDIRECTIONAL)
+ self.bind_property(
+ "selected-items-count", self._selection_menu,
+ "selected-items-count")
+ self.bind_property(
+ "search-mode-active", self._search_button, "active",
+ GObject.BindingFlags.BIDIRECTIONAL
+ | GObject.BindingFlags.SYNC_CREATE)
+
+ self.connect(
+ "notify::selection-mode-allowed",
+ self._on_selection_mode_allowed_changed)
+
+ self.connect(
+ "notify::search-mode-active", self._on_search_mode_changed)
+
+ self._entry.connect("changed", self._search_entry_timeout)
+
+ @GObject.Property(type=bool, default=False)
+ def selection_mode(self):
+ """Selection mode
+
+ :returns: Selection mode
+ :rtype: bool
+ """
+ return self._selection_mode
+
+ @selection_mode.setter
+ def selection_mode(self, mode):
+ """Set the selection mode
+
+ :param bool value: Selection mode
+ """
+ self._selection_mode = mode
+
+ 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
+
+ self._update()
+
+ @GObject.Property
+ def state(self):
+ """State of the widget
+
+ :returns: Widget state
+ :rtype: HeaderBar.State
+ """
+ return self._state
+
+ @state.setter
+ def state(self, value):
+ """Set state of the of widget
+
+ This influences the look and functionality of the headerbar.
+
+ :param HeaderBar.State value: Widget state
+ """
+ self._state = value
+ self._update()
+
+ search_visible = self.props.state != HeaderBar.State.SEARCH
+ self._search_button.props.visible = search_visible
+
+ if value == HeaderBar.State.EMPTY:
+ self._search_button.props.sensitive = False
+ self._select_button.props.sensitive = False
+ self._entry.props.visible = False
+ else:
+ self._search_button.props.sensitive = True
+ self._select_button.props.sensitive = True
+ self._entry.props.visible = False
+
+ @Gtk.Template.Callback()
+ @log
+ def _on_cancel_button_clicked(self, button):
+ self.props.selection_mode = False
+
+ @log
+ def _update(self):
+ if self.props.selection_mode:
+ self.props.custom_title = self._selection_menu
+ else:
+ self.props.custom_title = self._entry
+
+ @log
+ def _on_selection_mode_allowed_changed(self, widget, data):
+ if self.props.selection_mode_allowed:
+ self._select_button.props.sensitive = True
+ else:
+ self._select_button.props.sensitive = False
+
+ def _search_entry_timeout(self, widget):
+ if self._timeout:
+ GLib.source_remove(self._timeout)
+
+ self._timeout = GLib.timeout_add(
+ 500, self._search_entry_changed, widget)
+
+ def _search_entry_changed(self, widget):
+ self._timeout = None
+
+ search_term = self._entry.get_text()
+ if search_term != "":
+ self.props.stack.set_visible_child_name("search")
+ self._application.props.coremodel.search(search_term)
+ else:
+ self._set_error_style(False)
+
+ return False
+
+ def _on_search_mode_changed(self, klass, data):
+ if self.props.search_mode_active:
+ # self._search_entry.realize()
+ self._entry.grab_focus()
diff --git a/gnomemusic/window.py b/gnomemusic/window.py
index 582356a4..3d027f3e 100644
--- a/gnomemusic/window.py
+++ b/gnomemusic/window.py
@@ -43,6 +43,7 @@ from gnomemusic.widgets.notificationspopup import NotificationsPopup # noqa
from gnomemusic.widgets.playertoolbar import PlayerToolbar
from gnomemusic.widgets.playlistdialog import PlaylistDialog
from gnomemusic.widgets.searchbar import SearchBar
+from gnomemusic.widgets.searchheaderbar import SearchHeaderBar
from gnomemusic.widgets.selectiontoolbar import SelectionToolbar # noqa: F401
from gnomemusic.windowplacement import WindowPlacement
@@ -108,7 +109,15 @@ class Window(Gtk.ApplicationWindow):
self._search = Search()
self._searchbar = SearchBar(self._app)
self._searchbar.props.stack = self._stack
+ self._headerbar_stack = Gtk.Stack()
+ transition_type = Gtk.StackTransitionType.CROSSFADE
+ self._headerbar_stack.props.transition_type = transition_type
self._headerbar = HeaderBar()
+ self._search_headerbar = SearchHeaderBar(self._app)
+ self._search_headerbar.props.stack = self._stack
+ self._headerbar_stack.add_named(self._headerbar, "main")
+ self._headerbar_stack.add_named(self._search_headerbar, "search")
+ self._headerbar_stack.props.name = "search"
self._search.bind_property(
"search-mode-active", self._headerbar, "search-mode-active",
@@ -117,10 +126,17 @@ class Window(Gtk.ApplicationWindow):
self._search.bind_property(
"search-mode-active", self._searchbar, "search-mode-enabled",
GObject.BindingFlags.SYNC_CREATE)
+ self._search.bind_property(
+ "search-mode-active", self._search_headerbar, "search-mode-active",
+ GObject.BindingFlags.BIDIRECTIONAL
+ | GObject.BindingFlags.SYNC_CREATE)
self._search.bind_property(
"state", self._searchbar, "search-state",
GObject.BindingFlags.SYNC_CREATE)
+ self._search.connect(
+ "notify::search-mode-active", self._on_search_mode_changed)
+
self._player_toolbar = PlayerToolbar()
self._player_toolbar.props.player = self._player
@@ -136,6 +152,13 @@ class Window(Gtk.ApplicationWindow):
'selection-mode', self._headerbar, 'selection-mode',
GObject.BindingFlags.BIDIRECTIONAL
| GObject.BindingFlags.SYNC_CREATE)
+ self.bind_property(
+ "selected-items-count", self._search_headerbar,
+ "selected-items-count")
+ self.bind_property(
+ "selection-mode", self._search_headerbar, "selection-mode",
+ GObject.BindingFlags.BIDIRECTIONAL
+ | GObject.BindingFlags.SYNC_CREATE)
self.bind_property(
"selection-mode", self._player_toolbar, "visible",
GObject.BindingFlags.INVERT_BOOLEAN)
@@ -156,20 +179,20 @@ class Window(Gtk.ApplicationWindow):
self._stack.get_style_context().add_class('background')
# FIXME: Need to find a proper way to do this.
- self._overlay.add_overlay(self._searchbar._dropdown)
+ # self._overlay.add_overlay(self._searchbar._dropdown)
- self._box.pack_start(self._searchbar, False, False, 0)
- self._box.reorder_child(self._searchbar, 0)
+ # self._box.pack_start(self._searchbar, False, False, 0)
+ # self._box.reorder_child(self._searchbar, 0)
self._box.pack_end(self._player_toolbar, False, False, 0)
- self.set_titlebar(self._headerbar)
+ self.set_titlebar(self._headerbar_stack)
self._selection_toolbar.connect(
'add-to-playlist', self._on_add_to_playlist)
self._search.connect("notify::state", self._on_search_state_changed)
self._headerbar.props.state = HeaderBar.State.MAIN
- self._headerbar.show()
+ self._headerbar_stack.show_all()
self._app.props.coremodel.connect(
"notify::songs-available", self._on_songs_available)
@@ -200,6 +223,12 @@ class Window(Gtk.ApplicationWindow):
self._headerbar.props.state = HeaderBar.State.EMPTY
+ def _on_search_mode_changed(self, search, value):
+ if self._search.props.search_mode_active:
+ self._headerbar_stack.set_visible_child_name("search")
+ else:
+ self._headerbar_stack.set_visible_child_name("main")
+
def _on_songs_available(self, klass, value):
if self._app.props.coremodel.props.songs_available:
self._switch_to_player_view()
@@ -241,7 +270,7 @@ class Window(Gtk.ApplicationWindow):
# This is a bit of circular logic that needs to be fixed.
self._headerbar.props.state = HeaderBar.State.MAIN
self._headerbar.props.stack = self._stack
- self._searchbar.show()
+ # self._searchbar.show()
self.views[View.ALBUM] = AlbumsView(self._app, self._player)
self.views[View.ARTIST] = ArtistsView(self._app, self._player)
@@ -393,7 +422,7 @@ class Window(Gtk.ApplicationWindow):
# Open the search bar when typing printable chars.
key_unic = Gdk.keyval_to_unicode(keyval)
- if ((not self._searchbar.get_search_mode()
+ if ((not self._search.props.search_mode_active
and not keyval == Gdk.KEY_space)
and GLib.unichar_isprint(chr(key_unic))
and (modifiers == shift_mask
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]