[gnome-music/wip/jfelder/searchview-new-style: 11/15] Add SearchHeaderBar



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]