Usability GUI update (after Rafael Proença's proposal)

git-svn-id: svn://svn.berlios.de/gpodder/trunk@561 b0d088ad-0a06-0410-aad2-9ed5178a7e87
This commit is contained in:
Thomas Perl 2008-02-06 09:29:56 +00:00
parent b33eee3f66
commit 108ff7a5af
7 changed files with 288 additions and 104 deletions

View File

@ -1,3 +1,51 @@
Wed, 06 Feb 2008 10:18:30 +0100 <thp@perli.net>
Usability GUI update (after Rafael Proença's proposal)
* data/gpodder.glade: Add "Ctrl+L" hotkey to Add new channel; add
"View" menu with show/hide Toolbar (Ctrl+T) and show/hide episode
descriptions (Ctrl+D); remove "Update Feeds" button from toolbar and
add Check for updates button below channel navigator; move the add
episodes entry and buttons above the channel navigator; thanks to the
following people who shared their opinions on the mailing list: Rafael
Proença, Paul Rudkin, Ondrej Vesely, Leonid Ponomarev, Shane Donohoe,
Jérôme Chabod, Nick (nikosapi); the GUI mockups and the initial idea
were posted by Rafael Proença
* src/gpodder/config.py: Add "episode_list_descriptions" and
"show_toolbar" configuration options that modify the look of the main
window (both default to "True")
* src/gpodder/gui.py: Show feed description in seperate line (this
feature was suggested by narf@inode.at); make the episode list
treeview widget use the space more efficient; show/hide toolbar and
show/hide episode description GUI glue code
* src/gpodder/libgpodder.py: Add "digits" keyword argument to
format_filesize() (defaults to 2), this is a pass-thru function for
gpodder.util.format_filesize() (see below)
* src/gpodder/libpodcasts.py: Support for small- and large-sized
icons,modify liststore creation code to add description to the title
column
* src/gpodder/util.py: Add "digits" keyword argument to
format_filesize() (defaults to 2), this is used to define the format
of the filesize; add "icon_size" keyword argument (defaults to 32) to
get_tree_icon() and modify the function so that it dynamically
generates the correct icon with the correct sizes
Wed, 30 Jan 2008 19:16:27 +0100 <thp@perli.net>
####- Show feed description in seperate line (by narf at inode dot at)
In feeds where the description is crucial to decide whether to watch an
episode or not, it find it cumbersome to always scroll between the feed
details and the description. It would be nice if the description would be
shown directly beneath episode/size/published.
* data/gpodder.glade:
* src/gpodder/gui.py:
* src/gpodder/libgpodder.py:
* src/gpodder/libpodcasts.py:
* src/gpodder/util.py:
Mon, 04 Feb 2008 11:26:14 +0100 <thp@perli.net>
Apply patchset from Jérôme Chabod to fix tray icon behaviour

View File

@ -152,9 +152,10 @@
<property name="label" translatable="yes">_Subscribe to new channel</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_itemAddChannel_activate" last_modification_time="Sat, 29 Oct 2005 11:33:59 GMT"/>
<accelerator key="L" modifiers="GDK_CONTROL_MASK" signal="activate"/>
<child internal-child="image">
<widget class="GtkImage" id="image2850">
<widget class="GtkImage" id="image3014">
<property name="visible">True</property>
<property name="stock">gtk-add</property>
<property name="icon_size">1</property>
@ -616,6 +617,54 @@
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="menuView">
<property name="visible">True</property>
<property name="label" translatable="yes">_View</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_menuView_activate" last_modification_time="Sat, 02 Feb 2008 13:03:07 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image3031">
<property name="visible">True</property>
<property name="stock">gtk-zoom-fit</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
<child>
<widget class="GtkMenu" id="menuView_menu">
<child>
<widget class="GtkCheckMenuItem" id="itemShowToolbar">
<property name="visible">True</property>
<property name="label" translatable="yes">Show toolbar</property>
<property name="use_underline">True</property>
<property name="active">True</property>
<signal name="activate" handler="on_itemShowToolbar_activate" last_modification_time="Sat, 02 Feb 2008 13:03:07 GMT"/>
<accelerator key="T" modifiers="GDK_CONTROL_MASK" signal="activate"/>
</widget>
</child>
<child>
<widget class="GtkCheckMenuItem" id="itemShowDescription">
<property name="visible">True</property>
<property name="label" translatable="yes">Show episode description</property>
<property name="use_underline">True</property>
<property name="active">True</property>
<signal name="activate" handler="on_itemShowDescription_activate" last_modification_time="Sat, 02 Feb 2008 13:03:07 GMT"/>
<accelerator key="D" modifiers="GDK_CONTROL_MASK" signal="activate"/>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="menuHelp">
<property name="visible">True</property>
@ -623,7 +672,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image2868">
<widget class="GtkImage" id="image3032">
<property name="visible">True</property>
<property name="stock">gtk-help</property>
<property name="icon_size">1</property>
@ -754,43 +803,13 @@
</child>
<child>
<widget class="GtkToolbar" id="toolbar1">
<widget class="GtkToolbar" id="toolbar">
<property name="visible">True</property>
<property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
<property name="toolbar_style">GTK_TOOLBAR_BOTH</property>
<property name="tooltips">True</property>
<property name="show_arrow">True</property>
<child>
<widget class="GtkToolButton" id="toolUpdate">
<property name="visible">True</property>
<property name="label" translatable="yes">Update feeds</property>
<property name="use_underline">True</property>
<property name="stock_id">gtk-refresh</property>
<property name="visible_horizontal">True</property>
<property name="visible_vertical">True</property>
<property name="is_important">False</property>
<signal name="clicked" handler="on_itemUpdate_activate"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<widget class="GtkSeparatorToolItem" id="toolbutton1">
<property name="visible">True</property>
<property name="draw">True</property>
<property name="visible_horizontal">True</property>
<property name="visible_vertical">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="homogeneous">False</property>
</packing>
</child>
<child>
<widget class="GtkToolButton" id="toolDownload">
<property name="visible">True</property>
@ -951,38 +970,6 @@
<property name="homogeneous">False</property>
<property name="spacing">5</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow6">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTreeView" id="treeChannels">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">True</property>
<property name="rules_hint">False</property>
<property name="reorderable">False</property>
<property name="enable_search">True</property>
<property name="fixed_height_mode">False</property>
<property name="hover_selection">False</property>
<property name="hover_expand">False</property>
<signal name="row_activated" handler="on_treeChannels_row_activated" last_modification_time="Wed, 04 Jul 2007 13:23:55 GMT"/>
<signal name="cursor_changed" handler="on_treeChannels_cursor_changed" last_modification_time="Wed, 04 Jul 2007 13:25:12 GMT"/>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox24">
<property name="visible">True</property>
@ -1034,6 +1021,117 @@
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow6">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTreeView" id="treeChannels">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">True</property>
<property name="rules_hint">False</property>
<property name="reorderable">False</property>
<property name="enable_search">True</property>
<property name="fixed_height_mode">False</property>
<property name="hover_selection">False</property>
<property name="hover_expand">False</property>
<signal name="row_activated" handler="on_treeChannels_row_activated" last_modification_time="Wed, 04 Jul 2007 13:23:55 GMT"/>
<signal name="cursor_changed" handler="on_treeChannels_cursor_changed" last_modification_time="Wed, 04 Jul 2007 13:25:12 GMT"/>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btnUpdateFeeds">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_itemUpdate_activate" last_modification_time="Sat, 02 Feb 2008 13:24:51 GMT"/>
<child>
<widget class="GtkAlignment" id="alignment24">
<property name="visible">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<property name="top_padding">0</property>
<property name="bottom_padding">0</property>
<property name="left_padding">0</property>
<property name="right_padding">0</property>
<child>
<widget class="GtkHBox" id="hbox37">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image2982">
<property name="visible">True</property>
<property name="stock">gtk-refresh</property>
<property name="icon_size">4</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label120">
<property name="visible">True</property>
<property name="label" translatable="yes">Check for Updates</property>
<property name="use_underline">True</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
<packing>
<property name="shrink">False</property>
@ -5275,11 +5373,11 @@ Filesystem-based MP3 player</property>
<property name="justification">GTK_JUSTIFY_LEFT</property>
<property name="wrap_mode">GTK_WRAP_WORD</property>
<property name="cursor_visible">True</property>
<property name="pixels_above_lines">5</property>
<property name="pixels_below_lines">5</property>
<property name="pixels_above_lines">1</property>
<property name="pixels_below_lines">1</property>
<property name="pixels_inside_wrap">0</property>
<property name="left_margin">5</property>
<property name="right_margin">5</property>
<property name="left_margin">2</property>
<property name="right_margin">2</property>
<property name="indent">0</property>
<property name="text">Description of the episode currently selected in gPodder</property>
</widget>

View File

@ -64,6 +64,8 @@ gPodderSettings = {
'auto_remove_old_episodes': ( bool, False ),
'auto_update_feeds': (bool, False),
'auto_update_frequency': (int, 20),
'episode_list_descriptions': (bool, True),
'show_toolbar': (bool, True),
# Tray icon and notification settings
'display_tray_icon': (bool, False),

View File

@ -175,6 +175,11 @@ class gPodder(GladeWidget):
self.tray_icon = None
self.show_hide_tray_icon()
self.already_notified_new_episodes = []
self.episode_description_shown = gl.config.episode_list_descriptions
self.itemShowToolbar.set_active(gl.config.show_toolbar)
self.itemShowDescription.set_active(gl.config.episode_list_descriptions)
self.update_view_settings()
if self.tray_icon:
if gl.config.start_iconified:
@ -232,9 +237,10 @@ class gPodder(GladeWidget):
iconcolumn = gtk.TreeViewColumn( _("Status"), iconcell, pixbuf = 4)
namecell = gtk.CellRendererText()
#namecell.set_property('ellipsize', pango.ELLIPSIZE_END)
namecolumn = gtk.TreeViewColumn( _("Episode"), namecell, text = 1)
namecolumn.set_sizing( gtk.TREE_VIEW_COLUMN_AUTOSIZE)
namecell.set_property('ellipsize', pango.ELLIPSIZE_END)
namecolumn = gtk.TreeViewColumn(_("Episode"), namecell, markup=6)
namecolumn.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
namecolumn.set_expand(True)
sizecell = gtk.CellRendererText()
sizecolumn = gtk.TreeViewColumn( _("Size"), sizecell, text=2)
@ -242,14 +248,9 @@ class gPodder(GladeWidget):
releasecell = gtk.CellRendererText()
releasecolumn = gtk.TreeViewColumn( _("Released"), releasecell, text=5)
desccell = gtk.CellRendererText()
desccell.set_property('ellipsize', pango.ELLIPSIZE_END)
desccolumn = gtk.TreeViewColumn( _("Description"), desccell, text=6)
for itemcolumn in ( iconcolumn, namecolumn, sizecolumn, releasecolumn, desccolumn ):
itemcolumn.set_resizable( True)
itemcolumn.set_reorderable( True)
self.treeAvailable.append_column( itemcolumn)
for itemcolumn in (iconcolumn, namecolumn, sizecolumn, releasecolumn):
itemcolumn.set_reorderable(True)
self.treeAvailable.append_column(itemcolumn)
# enable search in treeavailable
self.treeAvailable.set_search_equal_func( self.treeAvailable_search_equal)
@ -1210,6 +1211,30 @@ class gPodder(GladeWidget):
elif self.tray_icon:
self.tray_icon.set_visible(True)
def update_view_settings(self):
gl = gPodderLib()
if gl.config.show_toolbar:
self.toolbar.show_all()
else:
self.toolbar.hide_all()
if self.episode_description_shown != gl.config.episode_list_descriptions:
for channel in self.channels:
channel.force_update_tree_model()
self.updateTreeView()
self.episode_description_shown = gl.config.episode_list_descriptions
def on_itemShowToolbar_activate(self, widget):
gl = gPodderLib()
gl.config.show_toolbar = self.itemShowToolbar.get_active()
self.update_view_settings()
def on_itemShowDescription_activate(self, widget):
gl = gPodderLib()
gl.config.episode_list_descriptions = self.itemShowDescription.get_active()
self.update_view_settings()
def update_item_device( self):
gl = gPodderLib()

View File

@ -129,8 +129,8 @@ class gPodderLibClass( object):
log( 'Warning: Called get_device_name() when no device was selected.', sender = self)
return '(unknown device)'
def format_filesize( self, bytesize):
return util.format_filesize( bytesize, self.config.use_si_units)
def format_filesize(self, bytesize, digits=2):
return util.format_filesize(bytesize, self.config.use_si_units, digits)
def clean_up_downloads( self, delete_partial = False):
# Clean up temporary files left behind by old gPodder versions

View File

@ -406,24 +406,30 @@ class podcastChannel(ListType):
local_filename = model.get_value( iter, 8)
played = not libgpodder.gPodderLib().history_is_played( url)
locked = libgpodder.gPodderLib().history_is_locked(url)
gl = libgpodder.gPodderLib()
if gl.config.episode_list_descriptions:
icon_size = 32
else:
icon_size = 16
if os.path.exists( local_filename):
file_type = util.file_type_by_extension( util.file_extension_from_url( url))
file_type = util.file_type_by_extension( util.file_extension_from_url(url))
if file_type == 'audio':
status_icon = util.get_tree_icon('audio-x-generic', played, locked, self.icon_cache)
status_icon = util.get_tree_icon('audio-x-generic', played, locked, self.icon_cache, icon_size)
elif file_type == 'video':
status_icon = util.get_tree_icon('video-x-generic', played, locked, self.icon_cache)
status_icon = util.get_tree_icon('video-x-generic', played, locked, self.icon_cache, icon_size)
elif file_type == 'torrent':
status_icon = util.get_tree_icon('applications-internet', played, locked, self.icon_cache)
status_icon = util.get_tree_icon('applications-internet', played, locked, self.icon_cache, icon_size)
else:
status_icon = util.get_tree_icon('unknown', played, locked, self.icon_cache)
status_icon = util.get_tree_icon('unknown', played, locked, self.icon_cache, icon_size)
elif services.download_status_manager.is_download_in_progress( url):
status_icon = util.get_tree_icon( gtk.STOCK_GO_DOWN, icon_cache = self.icon_cache)
elif libgpodder.gPodderLib().history_is_downloaded( url):
status_icon = util.get_tree_icon( gtk.STOCK_DELETE, icon_cache = self.icon_cache)
elif url in [ e.url for e in new_episodes ]:
status_icon = util.get_tree_icon( gtk.STOCK_NEW, icon_cache = self.icon_cache)
elif services.download_status_manager.is_download_in_progress(url):
status_icon = util.get_tree_icon(gtk.STOCK_GO_DOWN, icon_cache=self.icon_cache, icon_size=icon_size)
elif gl.history_is_downloaded(url):
status_icon = util.get_tree_icon(gtk.STOCK_DELETE, icon_cache=self.icon_cache, icon_size=icon_size)
elif url in [e.url for e in new_episodes]:
status_icon = util.get_tree_icon(gtk.STOCK_NEW, icon_cache=self.icon_cache, icon_size=icon_size)
else:
status_icon = None
@ -435,9 +441,14 @@ class podcastChannel(ListType):
"""
new_model = gtk.ListStore( gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_BOOLEAN, gtk.gdk.Pixbuf, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)
new_episodes = self.get_new_episodes()
gl = libgpodder.gPodderLib()
for item in self.get_all_episodes():
new_iter = new_model.append( ( item.url, item.title, libgpodder.gPodderLib().format_filesize( item.length), True, None, item.cute_pubdate(), item.one_line_description(), item.description, item.local_filename() ))
if gl.config.episode_list_descriptions:
description = '%s\n<small>%s</small>' % (saxutils.escape(item.title), saxutils.escape(item.one_line_description()))
else:
description = saxutils.escape(item.title)
new_iter = new_model.append((item.url, item.title, libgpodder.gPodderLib().format_filesize(item.length, 1), True, None, item.cute_pubdate(), description, item.description, item.local_filename()))
self.iter_set_downloading_columns( new_model, new_iter, new_episodes)
self.update_save_dir_size()

View File

@ -236,7 +236,7 @@ def get_free_disk_space(path):
return s.f_bavail * s.f_bsize
def format_filesize( bytesize, use_si_units = False):
def format_filesize(bytesize, use_si_units=False, digits=2):
"""
Formats the given size in bytes to be human-readable,
@ -275,7 +275,7 @@ def format_filesize( bytesize, use_si_units = False):
used_value = bytesize / float(value)
used_unit = unit
return '%.2f %s' % ( used_value, used_unit )
return ('%.'+str(digits)+'f %s') % (used_value, used_unit)
def delete_file( path):
@ -403,7 +403,7 @@ def file_type_by_extension( extension):
return None
def get_tree_icon(icon_name, add_bullet=False, add_padlock=False, icon_cache=None):
def get_tree_icon(icon_name, add_bullet=False, add_padlock=False, icon_cache=None, icon_size=32):
"""
Loads an icon from the current icon theme at the specified
size, suitable for display in a gtk.TreeView.
@ -419,23 +419,23 @@ def get_tree_icon(icon_name, add_bullet=False, add_padlock=False, icon_cache=Non
the cache.
"""
if icon_cache != None and (icon_name,add_bullet,add_padlock) in icon_cache:
return icon_cache[(icon_name,add_bullet,add_padlock)]
if icon_cache != None and (icon_name,add_bullet,add_padlock,icon_size) in icon_cache:
return icon_cache[(icon_name,add_bullet,add_padlock,icon_size)]
icon_theme = gtk.icon_theme_get_default()
try:
icon = icon_theme.load_icon( icon_name, 16, 0)
icon = icon_theme.load_icon(icon_name, icon_size, 0)
except:
log( '(get_tree_icon) Warning: Cannot load icon with name "%s", will use default icon.', icon_name)
icon = icon_theme.load_icon( gtk.STOCK_DIALOG_QUESTION, 16, 0)
icon = icon_theme.load_icon(gtk.STOCK_DIALOG_QUESTION, icon_size, 0)
if icon and (add_bullet or add_padlock):
# We'll modify the icon, so use .copy()
if add_bullet:
try:
icon = icon.copy()
emblem = icon_theme.load_icon(gtk.STOCK_YES, 10, 0)
emblem = icon_theme.load_icon(gtk.STOCK_YES, int(float(icon_size)*1.2/3.0), 0)
size = emblem.get_width()
pos = icon.get_width() - size
emblem.composite(icon, pos, pos, size, size, pos, pos, 1, 1, gtk.gdk.INTERP_BILINEAR, 255)
@ -444,14 +444,14 @@ def get_tree_icon(icon_name, add_bullet=False, add_padlock=False, icon_cache=Non
if add_padlock:
try:
icon = icon.copy()
emblem = icon_theme.load_icon('emblem-nowrite', 8, 0)
emblem = icon_theme.load_icon('emblem-nowrite', int(float(icon_size)/2.0), 0)
size = emblem.get_width()
emblem.composite(icon, 0, 0, size, size, 0, 0, 1, 1, gtk.gdk.INTERP_BILINEAR, 255)
except:
log('(get_tree_icon) Error adding emblem to icon "%s".', icon_name)
if icon_cache != None:
icon_cache[(icon_name,add_bullet,add_padlock)] = icon
icon_cache[(icon_name,add_bullet,add_padlock,icon_size)] = icon
return icon