diff --git a/docs/TODO b/docs/TODO
index 6ac988239..2983cfd75 100644
--- a/docs/TODO
+++ b/docs/TODO
@@ -6,16 +6,13 @@ Backlog (raw)
- requires pygobject[cairo]
- uniform license header
- who clears the vote cache (and document cache) at some point?
+ - redesign entropy locking: use readers/writer file lock
+ - update equo, sulfur, magneto, eit code to use it
- eit remote (for handling repos in server.conf)
- rework client/ code, as done with eit
- ditch etpUi
- - fine grained locking support
- (acquire entropy resources lock only when really needed
- [eit add vs eit graph --> needed? pkg_id no more avail])
- kill the Big Entropy Lock :-)
-
- conditional deps support, implement in entropy.spm,
class is there (add unit test)
diff --git a/rigo/data/ui/gtk3/art/rigo.png b/rigo/data/ui/gtk3/art/rigo.png
new file mode 100644
index 000000000..45125008c
Binary files /dev/null and b/rigo/data/ui/gtk3/art/rigo.png differ
diff --git a/rigo/data/ui/gtk3/art/rigo.svg b/rigo/data/ui/gtk3/art/rigo.svg
new file mode 100644
index 000000000..612568674
--- /dev/null
+++ b/rigo/data/ui/gtk3/art/rigo.svg
@@ -0,0 +1,77 @@
+
+
+
+
diff --git a/rigo/data/ui/gtk3/rigo.ui b/rigo/data/ui/gtk3/rigo.ui
index 1b77eb73b..34ba831cd 100644
--- a/rigo/data/ui/gtk3/rigo.ui
+++ b/rigo/data/ui/gtk3/rigo.ui
@@ -48,7 +48,6 @@
False
+
+
+
+ True
+ True
+ 2
+
+
True
diff --git a/rigo/rigo/models/application.py b/rigo/rigo/models/application.py
index 6ca95d202..42e95bd51 100644
--- a/rigo/rigo/models/application.py
+++ b/rigo/rigo/models/application.py
@@ -333,7 +333,10 @@ class Application(object):
def name(self):
"""Show user visible name"""
repo = self._entropy.open_repository(self._repo_id)
- return repo.retrieveName(self._pkg_id)
+ name = repo.retrieveName(self._pkg_id)
+ if name is None:
+ return _("N/A")
+ return name.capitalize()
def is_installed(self):
inst_repo = self._entropy.installed_repository()
@@ -351,9 +354,7 @@ class Application(object):
def get_markup(self):
repo = self._entropy.open_repository(self._repo_id)
- name = repo.retrieveName(self._pkg_id)
- if name is None:
- name = "N/A"
+ name = self.name
version = repo.retrieveVersion(self._pkg_id)
if version is None:
version = "N/A"
diff --git a/rigo/rigo_app.py b/rigo/rigo_app.py
index 2a1fcbf4a..189a0e001 100644
--- a/rigo/rigo_app.py
+++ b/rigo/rigo_app.py
@@ -9,6 +9,9 @@ sys.path.insert(3, "/usr/lib/entropy/lib")
sys.path.insert(4, "/usr/lib/entropy/client")
sys.path.insert(5, "/usr/lib/entropy/rigo")
+from threading import Lock
+
+
from gi.repository import Gtk, Gdk, Gio, GLib, GObject
from rigo.paths import DATA_DIR
@@ -176,13 +179,28 @@ class AppListStore(Gtk.ListStore):
class PackagesViewController(object):
- def __init__(self, entropy_client, icons, entropy_ws, search_entry, view):
+ def __init__(self, entropy_client, icons, entropy_ws, rigo_sm,
+ search_entry, view):
self._entropy = entropy_client
self._icons = icons
+ self._rigo_sm = rigo_sm
self._entropy_ws = entropy_ws
self._search_entry = search_entry
self._view = view
+ def _search_icon_release(self, search_entry, icon_pos, _other):
+ """
+ Event associated to the Search bar icon click.
+ Here we catch secondary icon click to reset the search entry text.
+ """
+ if search_entry is not self._search_entry:
+ return
+ if icon_pos != Gtk.EntryIconPosition.SECONDARY:
+ return
+ search_entry.set_text("")
+ self.clear()
+ search_entry.emit("changed")
+
def _search_changed(self, search_entry):
GLib.timeout_add(700, self._search, search_entry.get_text())
@@ -208,34 +226,63 @@ class PackagesViewController(object):
def setup(self):
self._store = AppListStore(self._entropy, self._view,
self._icons)
- #Gtk.ListStore(GObject.TYPE_PYOBJECT)
self._view.set_model(self._store)
# setup searchEntry event
self._search_entry.connect(
"changed", self._search_changed)
+ # connect icon click event
+ self._search_entry.connect("icon-release",
+ self._search_icon_release)
self._view.show()
def clear(self):
+ self._rigo_sm.change_view_state(
+ Rigo.STATIC_VIEW_STATE)
self._store.clear()
ApplicationMetadata.discard()
def append(self, opaque):
self._store.append([opaque])
+ self._rigo_sm.change_view_state(
+ Rigo.BROWSER_VIEW_STATE)
def append_many(self, opaque_list):
for opaque in opaque_list:
self._store.append([opaque])
+ self._rigo_sm.change_view_state(
+ Rigo.BROWSER_VIEW_STATE)
def clear_safe(self):
ApplicationMetadata.discard()
+ self._rigo_sm.change_view_state_safe(
+ Rigo.STATIC_VIEW_STATE)
GLib.idle_add(self._store.clear)
def append_safe(self, opaque):
GLib.idle_add(self.append, opaque)
+ self._rigo_sm.change_view_state_safe(
+ Rigo.BROWSER_VIEW_STATE)
def append_many_safe(self, opaque_list):
GLib.idle_add(self.append_many, opaque_list)
+ self._rigo_sm.change_view_state_safe(
+ Rigo.BROWSER_VIEW_STATE)
+
+class WelcomeBox(Gtk.VBox):
+
+ def __init__(self):
+ Gtk.VBox.__init__(self)
+ self._image_path = os.path.join(DATA_DIR, "ui/gtk3/art/rigo.png")
+
+ def render(self):
+ image = Gtk.Image.new_from_file(self._image_path)
+ label = Gtk.Label()
+ label.set_markup(_("Browse Applications with ease"))
+ self.pack_start(image, False, False, False)
+ self.pack_start(label, False, False, 0)
+ label.show()
+ image.show()
class NotificationBox(Gtk.VBox):
@@ -247,7 +294,7 @@ class NotificationBox(Gtk.VBox):
def render(self):
label = Gtk.Label()
label.set_markup(self._message)
- self.pack_start(label, False, False, False)
+ self.pack_start(label, False, False, 0)
label.show()
@@ -291,7 +338,6 @@ class Rigo(Gtk.Application):
def onDeleteWindow(self, *args):
Gtk.main_quit(*args)
-
def __init__(self):
self._builder = Gtk.Builder()
self._builder.add_from_file(os.path.join(DATA_DIR, "ui/gtk3/rigo.ui"))
@@ -300,10 +346,13 @@ class Rigo(Gtk.Application):
self._app_vbox = self._builder.get_object("appVbox")
self._search_entry = self._builder.get_object("searchEntry")
self._scrolled_view = self._builder.get_object("scrolledView")
+ self._static_view = self._builder.get_object("staticViewVbox")
icons = get_sc_icon_theme(DATA_DIR)
self._view = AppTreeView(self._app_vbox, icons, True, store=None)
self._scrolled_view.add(self._view)
+
self._notification = self._builder.get_object("notificationBox")
+ self._welcome_box = WelcomeBox()
settings = Gtk.Settings.get_default()
settings.set_property("gtk-error-bell", False)
@@ -320,17 +369,70 @@ class Rigo(Gtk.Application):
self._entropy_ws = EntropyWebService(self._entropy)
- # Setup Packages View Controller class
+ self._state_mutex = Lock()
+ self._current_state = Rigo.STATIC_VIEW_STATE
self._pvc = PackagesViewController(
self._entropy, icons, self._entropy_ws,
- self._search_entry, self._view)
+ self, self._search_entry, self._view)
self._nc = NotificationController(
self._entropy, self._notification)
+ BROWSER_VIEW_STATE = 1
+ STATIC_VIEW_STATE = 2
+
+ def change_view_state(self, state, child_widget=None):
+ """
+ Change Rigo Application UI state.
+ You can pass a custom widget that will be shown in case
+ of static view state.
+ """
+ with self._state_mutex:
+ if state == Rigo.BROWSER_VIEW_STATE:
+ self._static_view.set_visible(False)
+ # release all the childrens of static_view
+ for child in self._static_view.get_children():
+ self._static_view.remove(child)
+ self._scrolled_view.set_visible(True)
+ self._scrolled_view.show()
+
+ elif state == Rigo.STATIC_VIEW_STATE:
+ self._scrolled_view.set_visible(False)
+ if child_widget is not None:
+ for child in self._static_view.get_children():
+ self._static_view.remove(child)
+ self._static_view.pack_start(child_widget,
+ False, False, 0)
+ child_widget.show()
+ else:
+ # keep the current widget if any, or add the
+ # welcome widget
+ if not self._static_view.get_children():
+ self._welcome_box.show()
+ self._static_view.pack_start(self._welcome_box,
+ False, False, 0)
+
+ self._static_view.set_visible(True)
+ self._static_view.show()
+
+ else:
+ raise AttributeError("wrong view state")
+
+ self._current_state = state
+
+ def change_view_state_safe(self, state, child_widget=None):
+ """
+ Thread-safe version of change_view_state().
+ """
+ def _do_change():
+ return self.change_view_state(state, child_widget=child_widget)
+ GLib.idle_add(_do_change)
+
def _on_style_updated(self, widget, init_css_callback, *args):
init_css_callback(widget, *args)
def run(self):
+ self._welcome_box.render()
+ self.change_view_state(self._current_state)
self._pvc.setup()
self._nc.setup()
self._window.show()