diff --git a/rigo/data/ui/gtk3/css/softwarecenter.css b/rigo/data/ui/gtk3/css/rigo.css
similarity index 95%
rename from rigo/data/ui/gtk3/css/softwarecenter.css
rename to rigo/data/ui/gtk3/css/rigo.css
index a510ef69c..215f43696 100644
--- a/rigo/data/ui/gtk3/css/softwarecenter.css
+++ b/rigo/data/ui/gtk3/css/rigo.css
@@ -73,3 +73,8 @@ GtkViewport {
border-width: 0;
padding: 0;
}
+
+#rigoWindow {
+ background-color: white;
+ /* color: black; */
+}
diff --git a/rigo/data/ui/gtk3/css/softwarecenter.highcontrast.css b/rigo/data/ui/gtk3/css/rigo.highcontrast.css
similarity index 100%
rename from rigo/data/ui/gtk3/css/softwarecenter.highcontrast.css
rename to rigo/data/ui/gtk3/css/rigo.highcontrast.css
diff --git a/rigo/data/ui/gtk3/css/softwarecenter.highcontrastinverse.css b/rigo/data/ui/gtk3/css/rigo.highcontrastinverse.css
similarity index 100%
rename from rigo/data/ui/gtk3/css/softwarecenter.highcontrastinverse.css
rename to rigo/data/ui/gtk3/css/rigo.highcontrastinverse.css
diff --git a/rigo/data/ui/gtk3/rigo.ui b/rigo/data/ui/gtk3/rigo.ui
index 7f246ddb4..8c6bd144b 100644
--- a/rigo/data/ui/gtk3/rigo.ui
+++ b/rigo/data/ui/gtk3/rigo.ui
@@ -10,6 +10,8 @@
False
False
+ 8
0
@@ -275,11 +140,54 @@
0.019999999552965164
0.019999999552965164
Application markup
+ True
+
+
+ True
+ True
+ 1
+
+
+
+
+ True
+ False
+
+
+
+ False
+ False
+ 0
+
+
+
+
+ True
+ False
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ 0.98000001907348633
+ Downloaded times
+ True
+ True
+
+
+ False
+ False
+ 1
+
+
False
False
- 1
+ 5
+ 2
diff --git a/rigo/rigo/models/application.py b/rigo/rigo/models/application.py
index 1fdf4487a..75423f70b 100644
--- a/rigo/rigo/models/application.py
+++ b/rigo/rigo/models/application.py
@@ -134,6 +134,9 @@ class ApplicationMetadata(object):
"""
Thread executing generic (both rating and doc) metadata retrieval.
"""
+ cache_miss = WebService.CacheMiss
+ ws_exception = WebService.WebServiceException
+
while True:
sem.acquire()
discard_signal.set(False)
@@ -191,7 +194,7 @@ class ApplicationMetadata(object):
try:
outcome[(key, repo_id)] = request_map[request](
[key], cache=True, cached=True)[key]
- except WebService.CacheMiss:
+ except cache_miss:
uncached_keys.append(key)
for key in uncached_keys:
@@ -201,7 +204,7 @@ class ApplicationMetadata(object):
# FIXME, lxnay: work more instances in parallel?
outcome[(key, repo_id)] = request_map[request](
[key], cache = True)[key]
- except WebService.WebServiceException as wse:
+ except ws_exception as wse:
const_debug_write(
__name__,
"%s, WebServiceExc: %s" % (name, wse,)
@@ -541,7 +544,7 @@ class Application(object):
description = _("No description")
if len(description) > 79:
description = description[:80].strip() + "..."
- text = "%s %s\n%s" % (
+ text = "%s %s\n%s" % (
GObject.markup_escape_text(name),
GObject.markup_escape_text(version),
GObject.markup_escape_text(description))
diff --git a/rigo/rigo/ui/gtk3/models/appliststore.py b/rigo/rigo/ui/gtk3/models/appliststore.py
index e914ad8b3..a38c6b673 100644
--- a/rigo/rigo/ui/gtk3/models/appliststore.py
+++ b/rigo/rigo/ui/gtk3/models/appliststore.py
@@ -23,6 +23,15 @@ class AppListStore(Gtk.ListStore):
_MISSING_ICON_MUTEX = Lock()
_ICON_CACHE = {}
+ __gsignals__ = {
+ # Redraw signal, requesting UI update
+ # for given pkg_match object
+ "redraw-request" : (GObject.SignalFlags.RUN_LAST,
+ None,
+ (GObject.TYPE_PYOBJECT, ),
+ ),
+ }
+
def __init__(self, entropy_client, entropy_ws, view, icons):
Gtk.ListStore.__init__(self)
self._view = view
@@ -42,12 +51,6 @@ class AppListStore(Gtk.ListStore):
AppListStore._ICON_CACHE.clear()
return outcome
- def _ui_redraw_callback(self, *args):
- if const_debug_enabled():
- const_debug_write(__name__,
- "_ui_redraw_callback()")
- GLib.idle_add(self._view.queue_draw)
-
@property
def _missing_icon(self):
"""
@@ -63,14 +66,18 @@ class AppListStore(Gtk.ListStore):
AppListStore._MISSING_ICON = _missing_icon
return _missing_icon
- def get_icon(self, pkg_match, app=None):
+ def get_icon(self, pkg_match):
cached = AppListStore._ICON_CACHE.get(pkg_match)
if cached is not None:
return cached
- if app is None:
- app = Application(self._entropy, self._entropy_ws, pkg_match,
- redraw_callback=self._ui_redraw_callback)
+ def _ui_redraw_callback(*args):
+ if const_debug_enabled():
+ const_debug_write(__name__,
+ "_ui_redraw_callback()")
+ self.emit("redraw-request", pkg_match)
+ app = Application(self._entropy, self._entropy_ws, pkg_match,
+ redraw_callback=_ui_redraw_callback)
icon, cache_hit = app.get_details().icon
if icon is None:
if cache_hit:
@@ -113,28 +120,58 @@ class AppListStore(Gtk.ListStore):
return pixbuf
def is_installed(self, pkg_match):
+ def _ui_redraw_callback(*args):
+ if const_debug_enabled():
+ const_debug_write(__name__,
+ "_ui_redraw_callback()")
+ self.emit("redraw-request", pkg_match)
+
app = Application(self._entropy, self._entropy_ws, pkg_match,
- redraw_callback=self._ui_redraw_callback)
+ redraw_callback=_ui_redraw_callback)
return app.is_installed()
def is_available(self, pkg_match):
+ def _ui_redraw_callback(*args):
+ if const_debug_enabled():
+ const_debug_write(__name__,
+ "_ui_redraw_callback()")
+ self.emit("redraw-request", pkg_match)
+
app = Application(self._entropy, self._entropy_ws, pkg_match,
- redraw_callback=self._ui_redraw_callback)
+ redraw_callback=_ui_redraw_callback)
return app.is_available()
def get_markup(self, pkg_match):
+ def _ui_redraw_callback(*args):
+ if const_debug_enabled():
+ const_debug_write(__name__,
+ "_ui_redraw_callback()")
+ self.emit("redraw-request", pkg_match)
+
app = Application(self._entropy, self._entropy_ws, pkg_match,
- redraw_callback=self._ui_redraw_callback)
+ redraw_callback=_ui_redraw_callback)
return app.get_markup()
def get_review_stats(self, pkg_match):
+ def _ui_redraw_callback(*args):
+ if const_debug_enabled():
+ const_debug_write(__name__,
+ "_ui_redraw_callback()")
+ self.emit("redraw-request", pkg_match)
+
app = Application(self._entropy, self._entropy_ws, pkg_match,
- redraw_callback=self._ui_redraw_callback)
+ redraw_callback=_ui_redraw_callback)
return app.get_review_stats()
def get_application(self, pkg_match):
+ def _ui_redraw_callback(*args):
+ if const_debug_enabled():
+ const_debug_write(__name__,
+ "_ui_redraw_callback()")
+ self.emit("redraw-request", pkg_match)
+
app = Application(self._entropy, self._entropy_ws, pkg_match,
- redraw_callback=self._ui_redraw_callback)
+ redraw_callback=_ui_redraw_callback)
return app
def get_transaction_progress(self, pkg_match):
diff --git a/rigo/rigo/ui/gtk3/utils.py b/rigo/rigo/ui/gtk3/utils.py
index 244fdf276..ef5ecf56a 100644
--- a/rigo/rigo/ui/gtk3/utils.py
+++ b/rigo/rigo/ui/gtk3/utils.py
@@ -48,12 +48,12 @@ def init_sc_css_provider(toplevel, settings, screen, datadir):
# munge css path for theme-name
css_path = os.path.join(datadir,
- "ui/gtk3/css/softwarecenter.%s.css" % \
+ "ui/gtk3/css/rigo.%s.css" % \
theme_name)
# if no css for theme-name try fallback css
if not os.path.exists(css_path):
- css_path = os.path.join(datadir, "ui/gtk3/css/softwarecenter.css")
+ css_path = os.path.join(datadir, "ui/gtk3/css/rigo.css")
if not os.path.exists(css_path):
# check fallback exists as well... if not return None but warn
@@ -64,7 +64,7 @@ def init_sc_css_provider(toplevel, settings, screen, datadir):
LOG.warn(msg % css_path)
return None
- # things seem ok, now set the css provider for softwarecenter
+ # things seem ok, now set the css provider for Rigo
msg = "Rigo style provider for %s Gtk theme: %s"
LOG.info(msg % (theme_name, css_path))
diff --git a/rigo/rigo/ui/gtk3/widgets/apptreeview.py b/rigo/rigo/ui/gtk3/widgets/apptreeview.py
index 81229391b..565ab039d 100644
--- a/rigo/rigo/ui/gtk3/widgets/apptreeview.py
+++ b/rigo/rigo/ui/gtk3/widgets/apptreeview.py
@@ -8,7 +8,6 @@ from cellrenderers import CellRendererAppView, CellButtonRenderer, \
CellButtonIDs
from rigo.em import em, StockEms
-from rigo.utils import ExecutionTime
from rigo.enums import Icons
from rigo.models.application import CategoryRowReference
@@ -544,24 +543,18 @@ def on_entry_changed(widget, data):
new_text = widget.get_text()
(view, enquirer) = data
- with ExecutionTime("total time"):
- with ExecutionTime("enquire.set_query()"):
- # FIXME lxnay
- enquirer.set_query(get_query_from_search_entry(new_text),
- limit=100*1000,
- nonapps_visible=NonAppVisibility.ALWAYS_VISIBLE)
+ # FIXME lxnay
+ enquirer.set_query(get_query_from_search_entry(new_text),
+ limit=100*1000,
+ nonapps_visible=NonAppVisibility.ALWAYS_VISIBLE)
- store = view.tree_view.get_model()
- with ExecutionTime("store.clear()"):
- store.clear()
+ store = view.tree_view.get_model()
+ store.clear()
- with ExecutionTime("store.set_documents()"):
- store.set_from_matches(enquirer.matches)
+ store.set_from_matches(enquirer.matches)
- with ExecutionTime("model settle (size=%s)" % len(store)):
- while Gtk.events_pending():
- Gtk.main_iteration()
- return
+ while Gtk.events_pending():
+ Gtk.main_iteration()
if widget.stamp:
GObject.source_remove(widget.stamp)
diff --git a/rigo/rigo/utils.py b/rigo/rigo/utils.py
deleted file mode 100644
index 3af9b9fc1..000000000
--- a/rigo/rigo/utils.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright (C) 2009 Canonical
-#
-# Authors:
-# Michael Vogt
-#
-# This program 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; version 3.
-#
-# This program 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
-# this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-import logging
-import time
-
-class ExecutionTime(object):
- """
- Helper that can be used in with statements to have a simple
- measure of the timming of a particular block of code, e.g.
- with ExecutinTime("db flush"):
- db.flush()
- """
- def __init__(self, info=""):
- self.info = info
-
- def __enter__(self):
- self.now = time.time()
-
- def __exit__(self, type, value, stack):
- logger = logging.getLogger("softwarecenter.performance")
- logger.debug("%s: %s" % (self.info, time.time() - self.now))
diff --git a/rigo/rigo_app.py b/rigo/rigo_app.py
index 03e31cbd7..2b41c1878 100644
--- a/rigo/rigo_app.py
+++ b/rigo/rigo_app.py
@@ -2,6 +2,7 @@ import os
import sys
import copy
import tempfile
+import time
from threading import Lock
sys.path.insert(0, "../lib")
@@ -12,7 +13,7 @@ sys.path.insert(4, "/usr/lib/entropy/client")
sys.path.insert(5, "/usr/lib/entropy/rigo")
-from gi.repository import Gtk, Gdk, Gio, GLib, GObject, GdkPixbuf
+from gi.repository import Gtk, Gdk, Gio, GLib, GObject
from rigo.paths import DATA_DIR
from rigo.enums import Icons
@@ -22,6 +23,7 @@ from rigo.ui.gtk3.widgets.apptreeview import AppTreeView
from rigo.ui.gtk3.widgets.notifications import NotificationBox, \
RepositoriesUpdateNotificationBox, UpdatesNotificationBox
from rigo.ui.gtk3.widgets.welcome import WelcomeBox
+from rigo.ui.gtk3.widgets.stars import ReactiveStar
from rigo.ui.gtk3.models.appliststore import AppListStore
from rigo.ui.gtk3.utils import init_sc_css_provider, get_sc_icon_theme
@@ -312,11 +314,22 @@ class ApplicationViewController(GObject.Object):
self._entropy = entropy_client
self._entropy_ws = entropy_ws
self._app_store = None
+ self._last_pkg_match = None
self._image = self._builder.get_object("appViewImage")
self._app_name_lbl = self._builder.get_object("appViewNameLabel")
self._app_download_lbl = self._builder.get_object(
"appViewDownloadedLabel")
+ self._stars_container = self._builder.get_object("appViewStarsSelVbox")
+
+ self._stars = ReactiveStar()
+ self._stars_alignment = Gtk.Alignment.new(0.0, 0.5, 1.0, 1.0)
+ self._stars_alignment.set_padding(0, 5, 0, 0)
+ self._stars_alignment.add(self._stars)
+ self._stars.set_size_as_pixel_value(24)
+
+ self._stars_container.pack_start(self._stars_alignment, False, False, 0)
+
def set_store(self, store):
"""
@@ -326,6 +339,7 @@ class ApplicationViewController(GObject.Object):
def setup(self):
self.connect("application-activated", self._on_application_activated)
+ self._app_store.connect("redraw-request", self._on_redraw_request)
def _on_application_activated(self, avc, app):
"""
@@ -333,11 +347,22 @@ class ApplicationViewController(GObject.Object):
information. Once we're done loading the shit, we just emit
'application-show' and let others do the UI switch.
"""
+ self._last_pkg_match = app.get_details().pkg
task = ParallelTask(self.__application_activate, app)
task.name = "ApplicationActivate"
task.daemon = True
task.start()
+ def _on_redraw_request(self, widget, pkg_match):
+ """
+ Redraw request received from AppListStore for given package match.
+ We are required to update rating, number of downloads, icon.
+ """
+ if pkg_match == self._last_pkg_match:
+ stats = self._app_store.get_review_stats(pkg_match)
+ icon = self._app_store.get_icon(pkg_match)
+ self._setup_application_stats(stats, icon)
+
def __application_activate(self, app):
"""
Collect data from app, then call the UI setup in the main loop.
@@ -346,9 +371,28 @@ class ApplicationViewController(GObject.Object):
metadata = {}
metadata['name'] = app.get_markup()
metadata['stats'] = app.get_review_stats()
- metadata['icon'] = self._app_store.get_icon(details.pkg, app=app)
+ # using app store here because we cache the icon pixbuf
+ metadata['icon'] = self._app_store.get_icon(details.pkg)
GLib.idle_add(self._setup_application_info, app, metadata)
+ def _setup_application_stats(self, stats, icon):
+ """
+ Setup widgets related to Application statistics (and icon).
+ """
+ total_downloads = stats.downloads_total
+ if not total_downloads:
+ down_msg = _("Never downloaded")
+ else:
+ down_msg = ngettext("%d download",
+ "%d downloads",
+ total_downloads)
+ down_msg = down_msg % (total_downloads,)
+ self._app_download_lbl.set_markup(down_msg)
+ if icon:
+ self._image.set_from_pixbuf(icon)
+ self._stars.set_rating(stats.ratings_average - 1)
+ self._stars_alignment.show_all()
+
def _setup_application_info(self, app, metadata):
"""
Setup the actual UI widgets content and emit 'application-show'
@@ -356,18 +400,12 @@ class ApplicationViewController(GObject.Object):
# FIXME, lxnay complete
self._app_name_lbl.set_markup(metadata['name'])
stats = metadata['stats']
- total_downloads = stats.downloads_total
- if not total_downloads:
- down_msg = _("Never downloaded")
- else:
- down_msg = ngettext("%d download",
- "%d downloads",
- total_downloads)
- self._app_download_lbl.set_markup(down_msg % (total_downloads,))
- self._image.set_from_pixbuf(metadata['icon'])
+ icon = metadata['icon']
+ self._setup_application_stats(stats, icon)
self.emit("application-show", app)
+
class Rigo(Gtk.Application):
class RigoHandler:
@@ -395,6 +433,7 @@ class Rigo(Gtk.Application):
self._builder.add_from_file(os.path.join(DATA_DIR, "ui/gtk3/rigo.ui"))
self._builder.connect_signals(Rigo.RigoHandler())
self._window = self._builder.get_object("rigoWindow")
+ self._window.set_name("rigoWindow")
self._apps_view = self._builder.get_object("appsViewVbox")
self._scrolled_view = self._builder.get_object("appsViewScrolledWindow")
self._app_view = self._builder.get_object("appViewVbox")
@@ -412,6 +451,10 @@ class Rigo(Gtk.Application):
self._app_store = AppListStore(
self._entropy, self._entropy_ws,
self._view, icons)
+ def _queue_draw(*args):
+ self._view.queue_draw()
+ self._app_store.connect("redraw-request", _queue_draw)
+
self._app_view_c.set_store(self._app_store)
self._app_view_c.connect("application-show",
self._on_application_show)