Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions p3/app/icons/credits.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions p3/app/icons/report.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions p3/app/icons/sponsor.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions p3/app/icons/wiki.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
74 changes: 74 additions & 0 deletions p3/app/revealer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from .gtk_common import Gtk
from . import get_icon_path


class SupportFooter(Gtk.Box):
def __init__(self, translations):
super().__init__(orientation=Gtk.Orientation.HORIZONTAL, spacing=20)
self.set_margin_top(10)
self.translations = translations or {}
self.set_margin_bottom(10)
self.set_halign(Gtk.Align.CENTER)

self.urls_labels = [
("https://linux.toys/knowledgebase.html", "Wiki", "wiki.svg"),
("https://github.com/psygreg/linuxtoys/issues/new?template=bug_report.md", self.translations.get('report_label', 'Report Bug'), "report.svg"),
("https://linux.toys/credits.html", self.translations.get('credits_label', 'Credits'), "credits.svg"),
("https://ko-fi.com/psygreg", self.translations.get('support_footer', 'Support this project'), "sponsor.svg")
]

for i, (url, label, icon) in enumerate(self.urls_labels):
link_button = Gtk.LinkButton(uri=url, label=label)
if icon_path := get_icon_path(icon):
icon_img = Gtk.Image.new_from_file(icon_path)
link_button.set_image(icon_img)
self.pack_start(link_button, False, False, 0)

if i < len(self.urls_labels) - 1:
separator = Gtk.Separator(orientation=Gtk.Orientation.VERTICAL)
self.pack_start(separator, False, False, 0)


class RevealerFooter(Gtk.Revealer):
def __init__(self, parent):
super().__init__()

self.parent = parent
self.set_transition_type(Gtk.RevealerTransitionType.SLIDE_UP)
self.set_transition_duration(300)

container = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)

self.button_box = Gtk.ButtonBox(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
self.button_box.set_layout(Gtk.ButtonBoxStyle.CENTER)
self.button_box.set_margin_top(5)
self.button_box.set_margin_bottom(5)

self.button_next = Gtk.Button(label=self.parent.translations.get("next", "Next"))
self.button_next.set_image(Gtk.Image.new_from_icon_name("go-next", Gtk.IconSize.BUTTON))
self.button_next.set_always_show_image(True)
self.button_next.set_tooltip_text(self.parent.translations.get("next", "Next"))
self.button_next.set_size_request(125, 35)
self.button_next.connect("clicked", self._on_next_clicked)

self.button_cancel = Gtk.Button(label=self.parent.translations.get("cancel", "Cancel"))
self.button_cancel.set_image(Gtk.Image.new_from_icon_name("window-close", Gtk.IconSize.BUTTON))
self.button_cancel.set_always_show_image(True)
self.button_cancel.set_tooltip_text(self.parent.translations.get("cancel", "Cancel"))
self.button_cancel.set_size_request(125, 35)
self.button_cancel.connect("clicked", self._on_cancel_clicked)

self.button_box.add(self.button_cancel)
self.button_box.add(self.button_next)

self.support = SupportFooter(self.parent.translations)

container.pack_start(self.support, False, False, 0)
container.pack_start(self.button_box, False, False, 0)
self.add(container)

def _on_next_clicked(self, button):
self.parent.on_install_checklist(button)

def _on_cancel_clicked(self, button):
self.parent.on_cancel_checklist(button)
10 changes: 4 additions & 6 deletions p3/app/style.css
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
/* Defines the style for each item in our grid (FlowBox) */
* {
outline: none;
}
.script-item {
background-color: rgba(0,0,0,0.1);
border: 1px solid rgba(255, 255, 255, 0.3); /* borda branca semi-transparente */
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 16px;
/* Let GTK use system theme colors automatically */
}
.script-item-hover {
background-color: rgba(255, 255, 255, 0.05);
Expand All @@ -21,7 +22,4 @@
}
flowboxchild:selected {
border-radius: 16px;
}
flowboxchild {
outline: none;
}
96 changes: 40 additions & 56 deletions p3/app/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

from . import parser
from . import header
from . import footer
from . import compat
from . import head_menu
from . import reboot_helper
from . import search_helper
from . import get_icon_path
from . import term_view
from . import revealer

class AppWindow(Gtk.ApplicationWindow):
def __init__(self, application, translations, *args, **kwargs):
Expand Down Expand Up @@ -100,8 +100,8 @@ def __init__(self, application, translations, *args, **kwargs):
self.search_view.add(self.search_flowbox)
self.main_stack.add_named(self.search_view, "search")

self.footer_widget = footer.create_footer()
main_vbox.pack_start(self.footer_widget, False, False, 0)
self.reveal = revealer.RevealerFooter(self)
main_vbox.pack_start(self.reveal, False, False, 0)

# --- Load Data and Connect Signals ---
self.load_categories()
Expand Down Expand Up @@ -342,6 +342,18 @@ def create_flowbox(self):
flowbox.set_row_spacing(12) ## espaço entre linhas
return flowbox

def _on_toggled_check(self, button):
if button.get_active():
if button not in self.check_buttons:
self.check_buttons.append(button)
else:
if button in self.check_buttons:
self.check_buttons.remove(button)

self.reveal.button_box.show_all()
self.reveal.support.hide()
self.reveal.set_reveal_child(len(self.check_buttons) >= 2)

def create_item_widget(self, item_info, checklist: bool = False):
import os
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)
Expand Down Expand Up @@ -370,9 +382,9 @@ def create_item_widget(self, item_info, checklist: bool = False):

if checklist:
check = Gtk.CheckButton()
check.connect('toggled', self._on_toggled_check)
check.script_info = item_info
box.pack_start(check, False, False, 0)
self.check_buttons.append(check)

if is_subcategory or (is_category_type and is_not_script) or (is_main_category and is_not_script):
# This is a category or subcategory - make it bold
Expand Down Expand Up @@ -512,17 +524,7 @@ def _load_scripts_into_flowbox(self, flowbox, category_info):
flowbox.add(widget)

if checklist_mode:
# Clear previous checklist buttons from footer
for child in self.footer_widget.checklist_button_box.get_children():
self.footer_widget.checklist_button_box.remove(child)

install_btn = Gtk.Button(label=self.translations.get('install_btn_label', 'Install'))
cancel_btn = Gtk.Button(label=self.translations.get('cancel_btn_label', 'Cancel'))
install_btn.connect("clicked", self.on_install_checklist)
cancel_btn.connect("clicked", self.on_cancel_checklist)
self.footer_widget.checklist_button_box.pack_start(install_btn, False, False, 0)
self.footer_widget.checklist_button_box.pack_start(cancel_btn, False, False, 0)
self.footer_widget.checklist_button_box.show_all()
self.reveal.set_reveal_child(len(self.check_buttons) >= 2)

def load_scripts(self, category_info):
"""Loads scripts for a category and connects their click event. Supports checklist mode."""
Expand All @@ -540,18 +542,14 @@ def on_install_checklist(self, button):
if not selected_scripts:
return

self.open_term_view(selected_scripts)
for cb in self.check_buttons[:]: cb.set_active(False)

for cb in self.check_buttons: cb.set_active(False)
self.open_term_view(selected_scripts)

def on_cancel_checklist(self, button):
"""Uncheck all boxes, remove checklist buttons from footer, and return to previous view."""
for cb in self.check_buttons:
cb.set_active(False)
# Remove checklist buttons from footer
for child in self.footer_widget.checklist_button_box.get_children():
self.footer_widget.checklist_button_box.remove(child)

for cb in self.check_buttons[:]: cb.set_active(False)

# Use back button logic to go to the appropriate previous view
self.on_back_button_clicked(None)

Expand Down Expand Up @@ -605,9 +603,9 @@ def open_term_view(self, infos):
run_box = term_view.TermRunScripts(infos, self, self.translations)

self.header_widget.hide()

self.reveal.set_reveal_child(False)
self.check_buttons.clear()
self.back_button.show()
self.footer_widget.hide()

child = self.main_stack.get_child_by_name("running_scripts")
if child is not None:
Expand Down Expand Up @@ -742,10 +740,6 @@ def _refresh_ui_with_new_translations(self):
if hasattr(self, 'menu_button'):
self.menu_button.refresh_menu_translations()

# Refresh the footer with new translations
if hasattr(self.footer_widget, 'refresh_translations'):
self.footer_widget.refresh_translations(self.translations)

# Always reload categories with new translations (so they're ready when user navigates back)
self.load_categories()

Expand All @@ -767,24 +761,14 @@ def _refresh_ui_with_new_translations(self):
# Update title bar with fresh category name
category_name = self.current_category_info.get('name', 'Unknown')
self.header_bar.props.title = f"LinuxToys: {category_name}"

# Reload the scripts view with new translations
self.load_scripts(self.current_category_info)

# Update footer if in checklist mode
if (self.current_category_info and
self.current_category_info.get('display_mode', 'menu') == 'checklist'):
# Clear and recreate checklist buttons with new translations
for child in self.footer_widget.checklist_button_box.get_children():
self.footer_widget.checklist_button_box.remove(child)

install_btn = Gtk.Button(label=self.translations.get('install_btn_label', 'Install'))
cancel_btn = Gtk.Button(label=self.translations.get('cancel_btn_label', 'Cancel'))
install_btn.connect("clicked", self.on_install_checklist)
cancel_btn.connect("clicked", self.on_cancel_checklist)
self.footer_widget.checklist_button_box.pack_start(install_btn, False, False, 0)
self.footer_widget.checklist_button_box.pack_start(cancel_btn, False, False, 0)
self.footer_widget.checklist_button_box.show_all()
self.reveal.set_reveal_child(len(self.check_buttons) >= 2)

def _get_fresh_category_info_with_translations(self):
"""Get fresh category info with updated translations"""
Expand Down Expand Up @@ -1413,6 +1397,8 @@ def _display_search_results(self):
# Ensure the back button is visible when in search mode
self.back_button.show()

self.reveal.set_reveal_child(False)

# Disable drag-and-drop in search mode
self._disable_drag_and_drop()

Expand Down Expand Up @@ -1461,6 +1447,9 @@ def _clear_search_results(self):
self.main_stack.set_visible_child(self.scripts_view)
# Ensure back button is visible for category views
self.back_button.show()

if self.current_category_info.get('display_mode', 'menu') == 'checklist':
self.reveal.set_reveal_child(len(self.check_buttons) >= 2)

# Restore drag-and-drop state based on current category
if self._is_local_scripts_category(self.current_category_info):
Expand Down Expand Up @@ -1498,6 +1487,8 @@ def on_back_button_clicked(self, widget):
self.search_entry.set_text("")
self._clear_search_results()
return

self.check_buttons.clear()

if self.navigation_stack:
# Store current view for cleanup
Expand Down Expand Up @@ -1545,11 +1536,9 @@ def on_back_button_clicked(self, widget):

# Show footer only if checklist mode
if previous_category.get('display_mode', 'menu') == 'checklist':
self.footer_widget.show()
self.footer_widget.show_checklist_footer()
self.footer_widget.set_margin_bottom(0)
self.reveal.set_reveal_child(len(self.check_buttons) >= 2)
else:
self.footer_widget.hide()
self.reveal.set_reveal_child(False)

# Clean up the old view after transition
def cleanup_old_view():
Expand Down Expand Up @@ -1589,11 +1578,9 @@ def show_categories_view(self):
self.back_button.hide()
self.header_bar.props.title = "LinuxToys"
self._update_header() # Reset to default header
self.header_widget.show()
self.footer_widget.show()
self.footer_widget.show_menu_footer()
# Ensure footer has proper spacing
self.footer_widget.set_margin_bottom(0)
self.reveal.set_reveal_child(True)
self.reveal.button_box.hide()
self.reveal.support.show_all()

# Disable drag-and-drop when viewing main categories
self._disable_drag_and_drop()
Expand Down Expand Up @@ -1629,9 +1616,6 @@ def show_scripts_view(self, category_info):

# Show footer only if checklist mode
if category_info and category_info.get('display_mode', 'menu') == 'checklist':
self.footer_widget.show()
self.footer_widget.show_checklist_footer()
# Ensure footer has proper spacing for checklist mode
self.footer_widget.set_margin_bottom(0)
self.reveal.set_reveal_child(len(self.check_buttons) >= 2)
else:
self.footer_widget.hide()
self.reveal.set_reveal_child(False)