def __init__(self, icons, layout, show_ratings, overlay_icon_name): GObject.GObject.__init__(self) # geometry-state values self.pixbuf_width = 0 self.apptitle_width = 0 self.apptitle_height = 0 self.normal_height = 0 self.selected_height = 0 self.show_ratings = show_ratings # button packing self.button_spacing = 0 self._buttons = {Gtk.PackType.START: [], Gtk.PackType.END: []} self._all_buttons = {} # cache a layout self._layout = layout # star painter, paints stars self._stars = StarRenderer() self._stars.size = StarSize.SMALL # icon/overlay jazz try: self._installed = icons.load_icon(overlay_icon_name, self.OVERLAY_SIZE, 0) except GObject.GError: # icon not present in theme, probably because running uninstalled self._installed = icons.load_icon('emblem-system', self.OVERLAY_SIZE, 0) return
def __init__(self, icons, show_ratings, overlay_icon_name): GObject.GObject.__init__(self) # geometry-state values self.pixbuf_width = 0 self.apptitle_width = 0 self.apptitle_height = 0 self.normal_height = 0 self.selected_height = 0 self.show_ratings = show_ratings # button packing self.button_spacing = 0 self._buttons = {Gtk.PackType.START: [], Gtk.PackType.END: []} self._all_buttons = {} # cache a layout self._layout = None # star painter, paints stars self._stars = StarRenderer() self._stars.size = StarSize.SMALL # icon/overlay jazz try: self._installed = icons.load_icon(overlay_icon_name, self.OVERLAY_SIZE, 0) except GObject.GError: # icon not present in theme, probably because running uninstalled self._installed = icons.load_icon('emblem-system', self.OVERLAY_SIZE, 0) return
class CellRendererAppView(Gtk.CellRendererText): # x, y offsets for the overlay icon OVERLAY_XO = OVERLAY_YO = 2 # size of the install overlay icon OVERLAY_SIZE = 16 # ratings MAX_STARS = 5 STAR_SIZE = EM # initialize declared properties (LP: #965937) application = GObject.Property( type=GObject.TYPE_PYOBJECT, nick='document', blurb='a xapian document containing pkg information', flags=(GObject.PARAM_READWRITE | GObject.PARAM_CONSTRUCT), default=None) isactive = GObject.Property(type=bool, nick='isactive', blurb='is cell active/selected', flags=(GObject.PARAM_READWRITE | GObject.PARAM_CONSTRUCT), default=False) def __init__(self, icons, layout, show_ratings, overlay_icon_name): Gtk.CellRendererText.__init__(self) # the icon pixbuf to be displayed in the row self.icon = None # geometry-state values self.pixbuf_width = 0 self.apptitle_width = 0 self.apptitle_height = 0 self.normal_height = 0 self.selected_height = 0 self.show_ratings = show_ratings self.icon_x_offset = 0 self.icon_y_offset = 0 # button packing self.button_spacing = 0 self._buttons = {Gtk.PackType.START: [], Gtk.PackType.END: []} self._all_buttons = {} # cache a layout self._layout = layout # star painter, paints stars self._stars = StarRenderer() self._stars.size = StarSize.SMALL # icon/overlay jazz try: self._installed = icons.load_icon(overlay_icon_name, self.OVERLAY_SIZE, 0) except GObject.GError: # icon not present in theme, probably because running uninstalled self._installed = icons.load_icon('emblem-system', self.OVERLAY_SIZE, 0) def _layout_get_pixel_width(self, layout): return layout.get_size()[0] / Pango.SCALE def _layout_get_pixel_height(self, layout): return layout.get_size()[1] / Pango.SCALE def _render_category(self, context, cr, app, cell_area, layout, xpad, ypad, is_rtl): layout.set_markup('<b>%s</b>' % app.display_name, -1) # work out max allowable layout width lw = self._layout_get_pixel_width(layout) lh = self._layout_get_pixel_height(layout) if not is_rtl: x = cell_area.x else: x = cell_area.x + cell_area.width - lw y = cell_area.y + (cell_area.height - lh) / 2 Gtk.render_layout(context, cr, x, y, layout) def _render_price(self, context, cr, app, layout, cell_area, xpad, ypad, is_rtl): layout.set_markup("%s" % self.model.get_display_price(app)) if is_rtl: x = cell_area.x + xpad else: x = (cell_area.x + cell_area.width - xpad - self._layout_get_pixel_width(layout)) Gtk.render_layout(context, cr, x, ypad + cell_area.y, layout) def _render_icon(self, cr, app, cell_area, xpad, ypad, is_rtl): # calc offsets so icon is nicely centered self.icon = self.model.get_icon(app) self.icon_x_offset = xpad + cell_area.x self.icon_y_offset = ypad + cell_area.y xo = (self.pixbuf_width - self.icon.get_width()) / 2 if not is_rtl: x = cell_area.x + xo + xpad else: x = cell_area.x + cell_area.width + xo - self.pixbuf_width - xpad y = cell_area.y + ypad # draw appicon pixbuf Gdk.cairo_set_source_pixbuf(cr, self.icon, x, y) cr.paint() # draw overlay if application is installed if self.model.is_installed(app): if not is_rtl: x += (self.pixbuf_width - self.OVERLAY_SIZE + self.OVERLAY_XO) else: x -= self.OVERLAY_XO y += (self.pixbuf_width - self.OVERLAY_SIZE + self.OVERLAY_YO) Gdk.cairo_set_source_pixbuf(cr, self._installed, x, y) cr.paint() def _render_summary(self, context, cr, app, cell_area, layout, xpad, ypad, star_width, is_rtl): layout.set_markup(self.model.get_markup(app), -1) # work out max allowable layout width layout.set_width(-1) lw = self._layout_get_pixel_width(layout) max_layout_width = (cell_area.width - self.pixbuf_width - 3 * xpad - star_width) max_layout_width = cell_area.width - self.pixbuf_width - 3 * xpad stats = self.model.get_review_stats(app) if self.show_ratings and stats: max_layout_width -= star_width + 6 * xpad if (self.props.isactive and self.model.get_transaction_progress(app) > 0): action_btn = self.get_button_by_name(CellButtonIDs.ACTION) max_layout_width -= (xpad + action_btn.width) if lw >= max_layout_width: layout.set_width((max_layout_width) * Pango.SCALE) layout.set_ellipsize(Pango.EllipsizeMode.END) lw = max_layout_width apptitle_extents = layout.get_line_readonly(0).get_pixel_extents()[1] self.apptitle_width = apptitle_extents.width self.apptitle_height = apptitle_extents.height if not is_rtl: x = cell_area.x + 2 * xpad + self.pixbuf_width else: x = (cell_area.x + cell_area.width - lw - self.pixbuf_width - 2 * xpad) y = cell_area.y + ypad Gtk.render_layout(context, cr, x, y, layout) def _render_rating(self, context, cr, app, cell_area, layout, xpad, ypad, star_width, star_height, is_rtl): stats = self.model.get_review_stats(app) if not stats: return sr = self._stars if not is_rtl: x = (cell_area.x + 3 * xpad + self.pixbuf_width + self.apptitle_width) else: x = (cell_area.x + cell_area.width - 3 * xpad - self.pixbuf_width - self.apptitle_width - star_width) y = cell_area.y + ypad + (self.apptitle_height - self.STAR_SIZE) / 2 sr.rating = stats.ratings_average sr.render_star(context, cr, x, y) # and nr-reviews in parenthesis to the right of the title nreviews = stats.ratings_total s = "(%i)" % nreviews layout.set_markup("<small>%s</small>" % s, -1) if not is_rtl: x += xpad + star_width else: x -= xpad + self._layout_get_pixel_width(layout) context.save() context.add_class("cellrenderer-avgrating-label") Gtk.render_layout(context, cr, x, y, layout) context.restore() def _render_progress(self, context, cr, progress, cell_area, ypad, is_rtl): percent = progress * 0.01 # per the spec, the progressbar should be the width of the action # button action_btn = self.get_button_by_name(CellButtonIDs.ACTION) x, _, w, h = action_btn.allocation # shift the bar to the top edge y = cell_area.y + ypad context.save() context.add_class("trough") Gtk.render_background(context, cr, x, y, w, h) Gtk.render_frame(context, cr, x, y, w, h) context.restore() bar_size = w * percent context.save() context.add_class("progressbar") if (bar_size > 0): if is_rtl: x += (w - bar_size) Gtk.render_activity(context, cr, x, y, bar_size, h) context.restore() def _render_buttons(self, context, cr, cell_area, layout, xpad, ypad, is_rtl): # layout buttons and paint y = cell_area.y + cell_area.height - ypad spacing = self.button_spacing if not is_rtl: start = Gtk.PackType.START end = Gtk.PackType.END xs = cell_area.x + 2 * xpad + self.pixbuf_width xb = cell_area.x + cell_area.width - xpad else: start = Gtk.PackType.END end = Gtk.PackType.START xs = cell_area.x + xpad xb = cell_area.x + cell_area.width - 2 * xpad - self.pixbuf_width for btn in self._buttons[start]: btn.set_position(xs, y - btn.height) btn.render(context, cr, layout) xs += btn.width + spacing for btn in self._buttons[end]: xb -= btn.width btn.set_position(xb, y - btn.height) btn.render(context, cr, layout) xb -= spacing def set_pixbuf_width(self, w): self.pixbuf_width = w def set_button_spacing(self, spacing): self.button_spacing = spacing def get_button_by_name(self, name): if name in self._all_buttons: return self._all_buttons[name] def get_buttons(self): btns = () for k, v in self._buttons.items(): btns += tuple(v) return btns def button_pack(self, btn, pack_type=Gtk.PackType.START): self._buttons[pack_type].append(btn) self._all_buttons[btn.name] = btn def button_pack_start(self, btn): self.button_pack(btn, Gtk.PackType.START) def button_pack_end(self, btn): self.button_pack(btn, Gtk.PackType.END) def do_set_property(self, pspec, value): setattr(self, pspec.name, value) def do_get_property(self, pspec): return getattr(self, pspec.name) def do_get_preferred_height_for_width(self, treeview, width): if not self.get_properties("isactive")[0]: return self.normal_height, self.normal_height return self.selected_height, self.selected_height def do_render(self, cr, widget, bg_area, cell_area, flags): app = self.props.application if not app: return self.model = widget.appmodel context = widget.get_style_context() xpad = self.get_property('xpad') ypad = self.get_property('ypad') star_width, star_height = self._stars.get_visible_size(context) is_rtl = widget.get_direction() == Gtk.TextDirection.RTL layout = self._layout # important! ensures correct text rendering, esp. when using hicolor # theme #~ if (flags & Gtk.CellRendererState.SELECTED) != 0: #~ # this follows the behaviour that gtk+ uses for states in #~ # treeviews #~ if widget.has_focus(): #~ state = Gtk.StateFlags.SELECTED #~ else: #~ state = Gtk.StateFlags.ACTIVE #~ else: #~ state = Gtk.StateFlags.NORMAL context.save() #~ context.set_state(state) if isinstance(app, CategoryRowReference): self._render_category(context, cr, app, cell_area, layout, xpad, ypad, is_rtl) return self._render_icon(cr, app, cell_area, xpad, ypad, is_rtl) self._render_summary(context, cr, app, cell_area, layout, xpad, ypad, star_width, is_rtl) # only show ratings if we have one if self.show_ratings: self._render_rating(context, cr, app, cell_area, layout, xpad, ypad, star_width, star_height, is_rtl) progress = self.model.get_transaction_progress(app) if progress > 0: self._render_progress(context, cr, progress, cell_area, ypad, is_rtl) elif self.model.is_purchasable(app): self._render_price(context, cr, app, layout, cell_area, xpad, ypad, is_rtl) # below is the stuff that is only done for the active cell if not self.props.isactive: return self._render_buttons(context, cr, cell_area, layout, xpad, ypad, is_rtl) context.restore()
class CellRendererAppView(Gtk.CellRendererText): # x, y offsets for the overlay icon OVERLAY_XO = OVERLAY_YO = 2 # size of the install overlay icon OVERLAY_SIZE = 16 # ratings MAX_STARS = 5 STAR_SIZE = EM __gproperties__ = { 'application' : (GObject.TYPE_PYOBJECT, 'document', 'a xapian document containing pkg information', GObject.PARAM_READWRITE), 'isactive' : (bool, 'isactive', 'is cell active/selected', False, GObject.PARAM_READWRITE), } def __init__(self, icons, show_ratings, overlay_icon_name): GObject.GObject.__init__(self) # geometry-state values self.pixbuf_width = 0 self.apptitle_width = 0 self.apptitle_height = 0 self.normal_height = 0 self.selected_height = 0 self.show_ratings = show_ratings # button packing self.button_spacing = 0 self._buttons = {Gtk.PackType.START: [], Gtk.PackType.END: []} self._all_buttons = {} # cache a layout self._layout = None # star painter, paints stars self._stars = StarRenderer() self._stars.size = StarSize.SMALL # icon/overlay jazz try: self._installed = icons.load_icon(overlay_icon_name, self.OVERLAY_SIZE, 0) except GObject.GError: # icon not present in theme, probably because running uninstalled self._installed = icons.load_icon('emblem-system', self.OVERLAY_SIZE, 0) return def _layout_get_pixel_width(self, layout): return layout.get_size()[0] / Pango.SCALE def _layout_get_pixel_height(self, layout): return layout.get_size()[1] / Pango.SCALE def _render_category(self, context, cr, app, cell_area, layout, xpad, ypad, is_rtl): layout.set_markup('<b>%s</b>' % app.display_name, -1) # work out max allowable layout width lw = self._layout_get_pixel_width(layout) lh = self._layout_get_pixel_height(layout) if not is_rtl: x = cell_area.x else: x = cell_area.x + cell_area.width - lw y = cell_area.y + (cell_area.height - lh)/2 #w = cell_area.width #h = cell_area.height Gtk.render_layout(context, cr, x, y, layout) return def _render_icon(self, cr, app, cell_area, xpad, ypad, is_rtl): # calc offsets so icon is nicely centered icon = self.model.get_icon(app) xo = (self.pixbuf_width - icon.get_width())/2 if not is_rtl: x = cell_area.x + xo + xpad else: x = cell_area.x + cell_area.width + xo - self.pixbuf_width - xpad y = cell_area.y + ypad # draw appicon pixbuf Gdk.cairo_set_source_pixbuf(cr, icon, x, y) cr.paint() # draw overlay if application is installed if self.model.is_installed(app): if not is_rtl: x += (self.pixbuf_width - self.OVERLAY_SIZE + self.OVERLAY_XO) else: x -= self.OVERLAY_XO y += (self.pixbuf_width - self.OVERLAY_SIZE + self.OVERLAY_YO) Gdk.cairo_set_source_pixbuf(cr, self._installed, x, y) cr.paint() return def _render_summary(self, context, cr, app, cell_area, layout, xpad, ypad, star_width, is_rtl): layout.set_markup(self.model.get_markup(app), -1) # work out max allowable layout width layout.set_width(-1) lw = self._layout_get_pixel_width(layout) max_layout_width = (cell_area.width - self.pixbuf_width - 3*xpad - star_width) max_layout_width = cell_area.width - self.pixbuf_width - 3*xpad stats = self.model.get_review_stats(app) if self.show_ratings and stats: max_layout_width -= star_width+6*xpad if self.props.isactive and self.model.get_transaction_progress(app) > 0: action_btn = self.get_button_by_name(CellButtonIDs.ACTION) max_layout_width -= (xpad + action_btn.width) if lw >= max_layout_width: layout.set_width((max_layout_width)*Pango.SCALE) layout.set_ellipsize(Pango.EllipsizeMode.MIDDLE) lw = max_layout_width apptitle_extents = layout.get_line_readonly(0).get_pixel_extents()[1] self.apptitle_width = apptitle_extents.width self.apptitle_height = apptitle_extents.height if not is_rtl: x = cell_area.x+2*xpad+self.pixbuf_width else: x = cell_area.x+cell_area.width-lw-self.pixbuf_width-2*xpad y = cell_area.y+ypad Gtk.render_layout(context, cr, x, y, layout) return def _render_rating(self, context, cr, app, cell_area, layout, xpad, ypad, star_width, star_height, is_rtl): stats = self.model.get_review_stats(app) if not stats: return sr = self._stars if not is_rtl: x = cell_area.x+3*xpad+self.pixbuf_width+self.apptitle_width else: x = (cell_area.x + cell_area.width - 3*xpad - self.pixbuf_width - self.apptitle_width - star_width) y = cell_area.y + ypad + (self.apptitle_height-self.STAR_SIZE)/2 sr.rating = stats.ratings_average sr.render_star(context, cr, x, y) # and nr-reviews in parenthesis to the right of the title nreviews = stats.ratings_total s = "(%i)" % nreviews layout.set_markup("<small>%s</small>" % s, -1) lw = self._layout_get_pixel_width(layout) w = star_width if not is_rtl: x += xpad+w else: x -= xpad+lw context.save() context.add_class("cellrenderer-avgrating-label") Gtk.render_layout(context, cr, x, y, layout) context.restore() return def _render_progress(self, context, cr, progress, cell_area, ypad, is_rtl): percent = progress * 0.01 # per the spec, the progressbar should be the width of the action button action_btn = self.get_button_by_name(CellButtonIDs.ACTION) x, _, w, h = action_btn.allocation # shift the bar to the top edge y = cell_area.y + ypad context.save() context.add_class("trough") Gtk.render_background(context, cr, x, y, w, h) Gtk.render_frame(context, cr, x, y, w, h) context.restore () bar_size = w * percent context.save () context.add_class ("progressbar") if (bar_size > 0): if is_rtl: x += (w - bar_size) Gtk.render_activity(context, cr, x, y, bar_size, h) context.restore () return def _render_buttons(self, context, cr, cell_area, layout, xpad, ypad, is_rtl, is_available): # layout buttons and paint y = cell_area.y+cell_area.height-ypad spacing = self.button_spacing if not is_rtl: start = Gtk.PackType.START end = Gtk.PackType.END xs = cell_area.x + 2*xpad + self.pixbuf_width xb = cell_area.x + cell_area.width - xpad else: start = Gtk.PackType.END end = Gtk.PackType.START xs = cell_area.x + xpad xb = cell_area.x + cell_area.width - 2*xpad - self.pixbuf_width for btn in self._buttons[start]: btn.set_position(xs, y-btn.height) btn.render(context, cr, layout) xs += btn.width + spacing for btn in self._buttons[end]: xb -= btn.width btn.set_position(xb, y-btn.height) #~ if not is_available: #~ btn.set_sensitive(False) btn.render(context, cr, layout) xb -= spacing return def set_pixbuf_width(self, w): self.pixbuf_width = w return def set_button_spacing(self, spacing): self.button_spacing = spacing return def get_button_by_name(self, name): if name in self._all_buttons: return self._all_buttons[name] return None def get_buttons(self): btns = () for k, v in self._buttons.items(): btns += tuple(v) return btns def button_pack(self, btn, pack_type=Gtk.PackType.START): self._buttons[pack_type].append(btn) self._all_buttons[btn.name] = btn return def button_pack_start(self, btn): self.button_pack(btn, Gtk.PackType.START) return def button_pack_end(self, btn): self.button_pack(btn, Gtk.PackType.END) return def do_set_property(self, pspec, value): setattr(self, pspec.name, value) def do_get_property(self, pspec): return getattr(self, pspec.name) def do_get_preferred_height_for_width(self, treeview, width): if not self.get_properties("isactive")[0]: return self.normal_height, self.normal_height return self.selected_height, self.selected_height def do_render(self, cr, widget, bg_area, cell_area, flags): app = self.props.application if not app: return self.model = widget.appmodel context = widget.get_style_context() xpad = self.get_property('xpad') ypad = self.get_property('ypad') star_width, star_height = self._stars.get_visible_size(context) is_rtl = widget.get_direction() == Gtk.TextDirection.RTL if not self._layout: self._layout = widget.create_pango_layout('') layout = self._layout # important! ensures correct text rendering, esp. when using hicolor theme #~ if (flags & Gtk.CellRendererState.SELECTED) != 0: #~ # this follows the behaviour that gtk+ uses for states in treeviews #~ if widget.has_focus(): #~ state = Gtk.StateFlags.SELECTED #~ else: #~ state = Gtk.StateFlags.ACTIVE #~ else: #~ state = Gtk.StateFlags.NORMAL context.save() #~ context.set_state(state) if isinstance(app, CategoryRowReference): self._render_category(context, cr, app, cell_area, layout, xpad, ypad, is_rtl) return self._render_icon(cr, app, cell_area, xpad, ypad, is_rtl) self._render_summary(context, cr, app, cell_area, layout, xpad, ypad, star_width, is_rtl) # only show ratings if we have one if self.show_ratings: self._render_rating(context, cr, app, cell_area, layout, xpad, ypad, star_width, star_height, is_rtl) progress = self.model.get_transaction_progress(app) #~ print progress if progress > 0: self._render_progress(context, cr, progress, cell_area, ypad, is_rtl) # below is the stuff that is only done for the active cell if not self.props.isactive: return is_available = self.model.is_available(app) self._render_buttons(context, cr, cell_area, layout, xpad, ypad, is_rtl, is_available) context.restore() return