diff --git a/share/gpodder/ui/gtk/gpodder.ui b/share/gpodder/ui/gtk/gpodder.ui index a14b8331..abb3df6d 100644 --- a/share/gpodder/ui/gtk/gpodder.ui +++ b/share/gpodder/ui/gtk/gpodder.ui @@ -175,7 +175,9 @@ True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC - True + True + True + GTK_SHADOW_IN GTK_CORNER_TOP_LEFT @@ -214,56 +216,6 @@ - - - True - vertical - True - - - Check for new episodes - True - True - win.update - True - - - - - - - 6 - horizontal - - - True - 0.10000000149 - True - PANGO_ELLIPSIZE_MIDDLE - - - - - - - True - True - - - - True - gtk-cancel - 4 - - - - - - - - - - False diff --git a/share/gpodder/ui/gtk/menus.ui b/share/gpodder/ui/gtk/menus.ui index c2c30093..4d891347 100644 --- a/share/gpodder/ui/gtk/menus.ui +++ b/share/gpodder/ui/gtk/menus.ui @@ -43,6 +43,10 @@ Check for new episodes <Primary>r + + win.cancelFeedUpdate + Cancel check for new episodes + win.downloadAllNew Download new episodes diff --git a/src/gpodder/gtkui/app.py b/src/gpodder/gtkui/app.py index cc525b28..710a9f37 100644 --- a/src/gpodder/gtkui/app.py +++ b/src/gpodder/gtkui/app.py @@ -145,7 +145,25 @@ class gPodderApplication(Gtk.Application): self.want_headerbar = ('GNOME' in xdg_current_desktops) and not gpodder.ui.osx and not csd_disabled + self.header_bar_refresh_button = Gtk.Button.new_from_icon_name('view-refresh-symbolic', Gtk.IconSize.SMALL_TOOLBAR) + + # TODO: In Gtk 4, Gtk.Button.set_icon_name() is a thing, so this can probably be removed once we migrate + def _set_icon_name_patch(icon_name): + self.header_bar_refresh_button.set_image(Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.SMALL_TOOLBAR)) + + self.header_bar_refresh_button.set_icon_name = _set_icon_name_patch + self.header_bar_refresh_button.set_action_name('win.update') + + self.header_bar_refresh_ui = Gtk.Grid() + + self.header_bar_menu_button = Gtk.Button.new_from_icon_name('open-menu-symbolic', Gtk.IconSize.SMALL_TOOLBAR) + self.header_bar_menu_button.set_action_name('app.menu') + self.app_menu = builder.get_object('app-menu') + + self.menu_popover = Gtk.Popover.new_from_model(self.header_bar_menu_button, self.app_menu) + self.menu_popover.set_position(Gtk.PositionType.BOTTOM) + if self.want_headerbar: # This is a dirty hack to remove the "Quit" item in the menu it = self.app_menu.iterate_item_links(2) @@ -153,14 +171,6 @@ class gPodderApplication(Gtk.Application): it.get_value().remove(2) # Use GtkHeaderBar for client-side decorations on recent GNOME 3 versions - self.header_bar_menu_button = Gtk.Button.new_from_icon_name('open-menu-symbolic', Gtk.IconSize.SMALL_TOOLBAR) - self.header_bar_menu_button.set_action_name('app.menu') - - self.header_bar_refresh_button = Gtk.Button.new_from_icon_name('view-refresh-symbolic', Gtk.IconSize.SMALL_TOOLBAR) - self.header_bar_refresh_button.set_action_name('win.updateChannel') - - self.menu_popover = Gtk.Popover.new_from_model(self.header_bar_menu_button, self.app_menu) - self.menu_popover.set_position(Gtk.PositionType.BOTTOM) for (accel, action) in parse_app_menu_for_accels(menu_filename): self.add_accelerator(accel, action, None) @@ -168,7 +178,6 @@ class gPodderApplication(Gtk.Application): # TODO: Move the menubar self.set_menubar(self.menubar) else: - self.menubar.insert_submenu(0, 'gPodder', self.app_menu) self.set_menubar(self.menubar) Gtk.Window.set_default_icon_name('gpodder') diff --git a/src/gpodder/gtkui/main.py b/src/gpodder/gtkui/main.py index 6ae19590..7974fff0 100644 --- a/src/gpodder/gtkui/main.py +++ b/src/gpodder/gtkui/main.py @@ -98,10 +98,22 @@ class gPodder(BuilderWidget, dbus.service.Object): self.stack_switcher = Gtk.StackSwitcher() self.stack_switcher.set_stack(self.wNotebook) + self.pbFeedUpdate = Gtk.ProgressBar() + self.pbFeedUpdate.set_show_text(True) + self.pbFeedUpdate.set_property('ellipsize', Pango.EllipsizeMode.END) + self.pbFeedUpdate.set_property('valign', Gtk.Align.CENTER) + self.pbFeedUpdate.set_property('vexpand', True) + + self.header_bar_refresh_ui_revealer = Gtk.Revealer() + self.header_bar_refresh_ui_revealer.set_transition_type(Gtk.RevealerTransitionType.CROSSFADE) + self.header_bar_refresh_ui_revealer.add(self.pbFeedUpdate) + self.application.header_bar_refresh_ui.attach(self.header_bar_refresh_ui_revealer, 0, 0, 1, 1) + if self.application.want_headerbar: self.header_bar = Gtk.HeaderBar() self.header_bar.pack_end(self.application.header_bar_menu_button) self.header_bar.pack_start(self.application.header_bar_refresh_button) + self.header_bar.pack_start(self.application.header_bar_refresh_ui) self.header_bar.set_custom_title(self.stack_switcher) self.header_bar.set_show_close_button(True) self.header_bar.show_all() @@ -111,10 +123,28 @@ class gPodder(BuilderWidget, dbus.service.Object): self.main_window.set_titlebar(self.header_bar) else: - self.vMain.pack_start(self.stack_switcher, False, True, 6) + hb = Gtk.HBox() + hb.set_border_width(6) + self.vMain.pack_start(hb, False, True, 0) + self.vMain.reorder_child(hb, 1) + + self.pbFeedUpdate.set_property('vexpand', False) + self.vMain.pack_start(self.application.header_bar_refresh_ui, False, False, 0) + + def _on_child_revealed(widget, prop=None): + widget.set_visible(widget.get_child_revealed()) + + self.header_bar_refresh_ui_revealer.connect('notify::child-revealed', _on_child_revealed) + self.application.header_bar_refresh_ui.show_all() + _on_child_revealed(self.header_bar_refresh_ui_revealer) + + self.vMain.reorder_child(self.application.header_bar_refresh_ui, 2) + + hb.pack_start(self.application.header_bar_refresh_button, False, False, 0) + hb.pack_start(self.stack_switcher, True, True, 6) + hb.pack_start(self.application.header_bar_menu_button, False, False, 0) self.stack_switcher.set_property('halign', Gtk.Align.CENTER) - self.vMain.reorder_child(self.stack_switcher, 1) - self.stack_switcher.show_all() + hb.show_all() gpodder.user_extensions.on_ui_object_available('gpodder-gtk', self) self.toolbar.set_property('visible', self.config.show_toolbar) @@ -225,8 +255,6 @@ class gPodder(BuilderWidget, dbus.service.Object): util.run_in_background(self.user_apps_reader.read) # Now, update the feed cache, when everything's in place - if not self.application.want_headerbar: - self.btnUpdateFeeds.show() self.feed_cache_update_cancelled = False self.update_podcast_list_model() @@ -240,6 +268,8 @@ class gPodder(BuilderWidget, dbus.service.Object): if self.config.auto_update_feeds: self.restart_auto_update_timer() + self._hide_refresh_ui_source_id = None + # Find expired (old) episodes and delete them old_episodes = list(common.get_expired_episodes(self.channels, self.config)) if len(old_episodes) > 0: @@ -307,6 +337,7 @@ class gPodder(BuilderWidget, dbus.service.Object): action_defs = [ ('update', self.on_itemUpdate_activate), + ('cancelFeedUpdate', self.on_btnCancelFeedUpdate_clicked), ('downloadAllNew', self.on_itemDownloadAllNew_activate), ('removeOldEpisodes', self.on_itemRemoveOldEpisodes_activate), ('discover', self.on_itemImportChannels_activate), @@ -336,6 +367,7 @@ class gPodder(BuilderWidget, dbus.service.Object): self.update_action = g.lookup_action('update') self.update_channel_action = g.lookup_action('updateChannel') + self.cancel_feed_update_action = g.lookup_action('cancelFeedUpdate') self.edit_channel_action = g.lookup_action('editChannel') self.play_action = g.lookup_action('play') self.open_action = g.lookup_action('open') @@ -345,6 +377,8 @@ class gPodder(BuilderWidget, dbus.service.Object): self.toggle_episode_new_action = g.lookup_action('toggleEpisodeNew') self.toggle_episode_lock_action = g.lookup_action('toggleEpisodeLock') + self.cancel_feed_update_action.set_enabled(False) + action = Gio.SimpleAction.new_stateful( 'showToolbar', None, GLib.Variant.new_boolean(self.config.show_toolbar)) action.connect('activate', self.on_itemShowToolbar_activate) @@ -2627,20 +2661,35 @@ class gPodder(BuilderWidget, dbus.service.Object): def show_update_feeds_buttons(self): # Make sure that the buttons for updating feeds # appear - this should happen after a feed update - self.hboxUpdateFeeds.hide() - if not self.application.want_headerbar: - self.btnUpdateFeeds.show() + self.header_bar_refresh_ui_revealer.set_reveal_child(False) + self._make_update_button_update() + + def _make_update_button_update(self): + self.cancel_feed_update_action.set_enabled(False) + self.application.header_bar_refresh_button.set_icon_name('view-refresh-symbolic') + self.application.header_bar_refresh_button.set_action_name('win.update') self.update_action.set_enabled(True) self.update_channel_action.set_enabled(True) - def on_btnCancelFeedUpdate_clicked(self, widget): + def _make_update_button_cancel(self): + self.update_action.set_enabled(False) + self.update_channel_action.set_enabled(False) + self.application.header_bar_refresh_button.set_icon_name('edit-undo-symbolic') + self.application.header_bar_refresh_button.set_action_name('win.cancelFeedUpdate') + self.cancel_feed_update_action.set_enabled(True) + + def on_btnCancelFeedUpdate_clicked(self, *args): if not self.feed_cache_update_cancelled: self.pbFeedUpdate.set_text(_('Cancelling...')) self.feed_cache_update_cancelled = True - self.btnCancelFeedUpdate.set_sensitive(False) else: self.show_update_feeds_buttons() + def _hide_header_bar_refresh_ui(self): + self.header_bar_refresh_ui_revealer.set_reveal_child(False) + self._hide_refresh_ui_source_id = None + return False + def update_feed_cache(self, channels=None, show_new_episodes_dialog=True): if self.config.check_connection and not util.connection_available(): @@ -2655,15 +2704,15 @@ class gPodder(BuilderWidget, dbus.service.Object): # Only update podcasts for which updates are enabled channels = [c for c in self.channels if not c.pause_subscription] - self.update_action.set_enabled(False) - self.update_channel_action.set_enabled(False) + self._make_update_button_cancel() self.feed_cache_update_cancelled = False - self.btnCancelFeedUpdate.show() - self.btnCancelFeedUpdate.set_sensitive(True) - self.btnCancelFeedUpdate.set_image(Gtk.Image.new_from_icon_name('process-stop', Gtk.IconSize.BUTTON)) - self.hboxUpdateFeeds.show_all() - self.btnUpdateFeeds.hide() + + if self._hide_refresh_ui_source_id is not None: + GObject.source_remove(self._hide_refresh_ui_source_id) + self._hide_refresh_ui_source_id = None + + self.header_bar_refresh_ui_revealer.set_reveal_child(True) count = len(channels) text = N_('Updating %(count)d feed...', 'Updating %(count)d feeds...', @@ -2759,10 +2808,8 @@ class gPodder(BuilderWidget, dbus.service.Object): self.pbFeedUpdate.set_fraction(1.0) self.pbFeedUpdate.set_text(_('No new episodes')) self.feed_cache_update_cancelled = True - self.btnCancelFeedUpdate.show() - self.btnCancelFeedUpdate.set_sensitive(True) - self.update_action.set_enabled(True) - self.btnCancelFeedUpdate.set_image(Gtk.Image.new_from_icon_name('edit-clear', Gtk.IconSize.BUTTON)) + self._hide_refresh_ui_source_id = GObject.timeout_add(3000, self._hide_header_bar_refresh_ui) + self._make_update_button_update() else: count = len(episodes) # New episodes are available