From 68bde43eb0db94425a7decc973a7646624a011a3 Mon Sep 17 00:00:00 2001 From: Corbin Auriti Date: Tue, 11 Jun 2024 07:48:56 -0700 Subject: [PATCH] Add Banner Slideshow using Gtk.Stack with nice animations (#417) --- usr/lib/linuxmint/mintinstall/mintinstall.py | 134 +++++++++++++------ 1 file changed, 93 insertions(+), 41 deletions(-) diff --git a/usr/lib/linuxmint/mintinstall/mintinstall.py b/usr/lib/linuxmint/mintinstall/mintinstall.py index 3b63746..26849ed 100755 --- a/usr/lib/linuxmint/mintinstall/mintinstall.py +++ b/usr/lib/linuxmint/mintinstall/mintinstall.py @@ -58,6 +58,9 @@ #May not work on all mice MOUSE_BACK_BUTTON = 8 +#How many miliseconds between banner slides +BANNER_TIMER = 500 + # package type combobox columns # index, label, icon-name, tooltip, pkginfo PACKAGE_TYPE_COMBO_INDEX = 0 @@ -620,17 +623,17 @@ def set_fraction(self, fraction): class BannerTile(Gtk.FlowBoxChild): - def __init__(self, pkginfo, installer, name, is_flatpak, app_json, on_clicked_action): + def __init__(self, pkginfo, installer, name, background, color, is_flatpak, app_json, on_clicked_action): super(Gtk.FlowBoxChild, self).__init__() self.pkginfo = pkginfo self.installer = installer self.is_flatpak = is_flatpak self.init_name = name + self.background = background + self.color = color self.image_uri = (f"/usr/share/linuxmint/mintinstall/featured/{name}.svg") - background = app_json["background"] - color = app_json["text_color"] css = """ #BannerTile { @@ -660,27 +663,26 @@ def __init__(self, pkginfo, installer, name, is_flatpak, app_json, on_clicked_ac self.set_name("BannerTile") style_provider = Gtk.CssProvider() style_provider.load_from_data(str.encode(css)) - Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(), - style_provider, - Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) - self.box = None - self.repopulate_tile() - - def repopulate_tile(self): - if self.box is not None: - self.box.destroy() + """ Have to re-use .add_provider(style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) for every widget because + add_provider doesn't cascade to children, and cant set the whole screen context for multiple tiles + without making them uniform """ + self.get_style_context().add_provider(style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) + label_name = Gtk.Label(xalign=0) label_name.set_label(self.installer.get_display_name(self.pkginfo)) label_name.set_name("BannerTitle") + label_name.get_style_context().add_provider(style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) label_summary = Gtk.Label(xalign=0) label_summary.set_label(self.installer.get_summary(self.pkginfo)) label_summary.set_name("BannerSummary") + label_summary.get_style_context().add_provider(style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) image = Gtk.Image.new_from_file(self.image_uri) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12, halign=Gtk.Align.START) + vbox.get_style_context().add_provider(style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) vbox.set_border_width(6) vbox.pack_start(label_name, False, False, 0) @@ -688,9 +690,11 @@ def repopulate_tile(self): if self.is_flatpak: box_flatpak = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6) + box_flatpak.get_style_context().add_provider(style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) box_flatpak.pack_start(Gtk.Image.new_from_icon_name("mintinstall-package-flatpak-symbolic", Gtk.IconSize.MENU), False, False, 0) label_flatpak = Gtk.Label(label="Flathub") label_flatpak.set_name("BannerFlatpakLabel") + label_flatpak.get_style_context().add_provider(style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) box_flatpak.pack_start(label_flatpak, False, False, 0) vbox.pack_start(box_flatpak, False, False, 0) @@ -1414,24 +1418,30 @@ def load_banner(self): for child in box.get_children(): child.destroy() - flowbox = Gtk.FlowBox() - flowbox.set_min_children_per_line(1) - flowbox.set_max_children_per_line(1) - flowbox.set_row_spacing(0) - flowbox.set_column_spacing(0) - flowbox.set_homogeneous(True) - - flowbox.connect("child-activated", self.on_flowbox_child_activated, self.PAGE_LANDING) + stack = Gtk.Stack() + stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT) + stack.set_transition_duration(BANNER_TIMER) + dot_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) + dot_box.set_halign(Gtk.Align.CENTER) + json_array = json.load(open("/usr/share/linuxmint/mintinstall/featured/featured.json", "r")) + random.shuffle(json_array) + + selected_apps = set() + num_selected = 0 - tries = 0 - pkginfo = None - is_flatpak = False + for app_json in json_array: + if num_selected >= 5: + break - while True: - app_json = random.sample(json_array, 1)[0] name = app_json["name"] + background = app_json["background"] + color = app_json["text_color"] + + if name in selected_apps: + continue + if name.startswith("flatpak:"): name = name.replace("flatpak:", "") pkginfo = self.installer.find_pkginfo(name, installer.PKG_TYPE_FLATPAK) @@ -1444,26 +1454,68 @@ def load_banner(self): pkginfo = self.installer.find_pkginfo(name, installer.PKG_TYPE_APT) is_flatpak = False - if pkginfo is not None: - if self.installer.pkginfo_is_installed(pkginfo) and tries < 10: - tries += 1 - continue - break - else: - tries += 1 + selected_apps.add(name) + num_selected += 1 - if tries > 10: - print("Error while loading the banner.") - box.hide() - return + flowbox = Gtk.FlowBox() + flowbox.set_min_children_per_line(1) + flowbox.set_max_children_per_line(1) + flowbox.set_row_spacing(0) + flowbox.set_column_spacing(0) + flowbox.set_homogeneous(True) + flowbox.connect("child-activated", self.on_flowbox_child_activated, self.PAGE_LANDING) + + tile = BannerTile(pkginfo, self.installer, name, background, color, is_flatpak, app_json, self.on_banner_clicked) + flowbox.insert(tile, -1) + + stack.add_named(flowbox, str(len(stack.get_children()))) + + icon = Gtk.Image.new_from_icon_name("media-record-symbolic", Gtk.IconSize.MENU) + icon.set_pixel_size(10) + + dot_button = Gtk.Button() + dot_button.set_relief(Gtk.ReliefStyle.NONE) + dot_button.set_image(icon) + dot_button.connect("clicked", self.on_dot_clicked, stack, len(stack.get_children()) - 1) + dot_box.pack_start(dot_button, False, False, 0) + + box.pack_start(stack, True, True, 0) + box.pack_start(dot_box, False, False, 5) + + self.update_dot_buttons(dot_box, 0) + + self.slideshow_timeout_id = GLib.timeout_add_seconds(5, self.on_slideshow_timeout, stack, dot_box) - tile = BannerTile(pkginfo, self.installer, name, is_flatpak, app_json, self.on_banner_clicked) - self.banner_app_name = pkginfo.name - flowbox.insert(tile, -1) - self.banner_tile = tile - box.pack_start(flowbox, True, True, 0) box.show_all() + def on_dot_clicked(self, button, stack, index): + self.reset_slideshow_timer(stack, button.get_parent()) + stack.set_visible_child_name(str(index)) + self.update_dot_buttons(button.get_parent(), index) + + def reset_slideshow_timer(self, stack, dot_box): + GLib.source_remove(self.slideshow_timeout_id) + self.slideshow_timeout_id = GLib.timeout_add_seconds(5, self.on_slideshow_timeout, stack, dot_box) + + def on_slideshow_timeout(self, stack, dot_box): + visible_child = stack.get_visible_child() + index = stack.get_children().index(visible_child) + new_index = (index + 1) % len(stack.get_children()) + stack.set_visible_child_name(str(new_index)) + self.update_dot_buttons(dot_box, new_index) + return True + + def update_dot_buttons(self, dot_box, current_index): + for i, button in enumerate(dot_box.get_children()): + if i == current_index: #Bigger do if current slide + icon = Gtk.Image.new_from_icon_name("media-record-symbolic", Gtk.IconSize.MENU) + icon.set_pixel_size(15) + button.set_image(icon) + else: + icon = Gtk.Image.new_from_icon_name("media-record-symbolic", Gtk.IconSize.MENU) + icon.set_pixel_size(10) + button.set_image(icon) + def on_banner_clicked(self, button, pkginfo): self.show_package(pkginfo, self.PAGE_LANDING)