This commit implements several UI/UX improvements for the "Backup Content" list view based on user feedback. - feat(ui): User backups are now grouped by their full/incremental chains, similar to system backups, for a more logical and organized view. - feat(ui): The color scheme for backup chains has been simplified. Each chain (a full backup and its incrementals) now shares a single color to improve visual grouping. - feat(ui): Incremental backups are now denoted by a ▲ icon in the Type column instead of a different color or font style, providing a clear and clean indicator. - fix(ui): Adjusted all column widths in the backup lists to ensure all data (especially Date and Time) is fully visible without truncation.
144 lines
5.5 KiB
Python
144 lines
5.5 KiB
Python
import tkinter as tk
|
|
from tkinter import ttk
|
|
import os
|
|
import shutil
|
|
|
|
from core.pbp_app_config import Msg
|
|
from pyimage_ui.comment_editor_dialog import CommentEditorDialog
|
|
from shared_libs.message import MessageDialog
|
|
|
|
|
|
class UserBackupContentFrame(ttk.Frame):
|
|
def __init__(self, master, backup_manager, actions, parent_view, **kwargs):
|
|
super().__init__(master, **kwargs)
|
|
self.backup_manager = backup_manager
|
|
self.actions = actions
|
|
self.parent_view = parent_view
|
|
self.user_backups_list = []
|
|
self.backup_path = None
|
|
|
|
columns = ("date", "time", "type", "size", "folder_name", "comment")
|
|
self.content_tree = ttk.Treeview(
|
|
self, columns=columns, show="headings")
|
|
self.content_tree.heading("date", text=Msg.STR["date"])
|
|
self.content_tree.heading("time", text=Msg.STR["time"])
|
|
self.content_tree.heading("type", text=Msg.STR["type"])
|
|
self.content_tree.heading("size", text=Msg.STR["size"])
|
|
self.content_tree.heading("folder_name", text=Msg.STR["folder"])
|
|
self.content_tree.heading("comment", text=Msg.STR["comment"])
|
|
|
|
self.content_tree.column("date", width=100, anchor="center")
|
|
self.content_tree.column("time", width=100, anchor="center")
|
|
self.content_tree.column("type", width=180, anchor="w")
|
|
self.content_tree.column("size", width=90, anchor="center")
|
|
self.content_tree.column("folder_name", width=130, anchor="center")
|
|
self.content_tree.column("comment", width=180, anchor="w")
|
|
|
|
self.content_tree.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
|
self.content_tree.bind("<<TreeviewSelect>>", self._on_item_select)
|
|
|
|
def show(self, backup_path, user_backups):
|
|
self.backup_path = backup_path
|
|
self.user_backups_list = user_backups
|
|
self._load_backup_content()
|
|
|
|
def _load_backup_content(self):
|
|
for i in self.content_tree.get_children():
|
|
self.content_tree.delete(i)
|
|
|
|
if not self.user_backups_list:
|
|
return
|
|
|
|
colors = ["#0078D7", "#E8740C", "#107C10", "#8B107C", "#005A9E"]
|
|
color_index = -1
|
|
current_color_tag = ""
|
|
|
|
for i, backup_info in enumerate(self.user_backups_list):
|
|
if backup_info.get("backup_type_base") == "Full":
|
|
color_index = (color_index + 1) % len(colors)
|
|
current_color_tag = f"color_{color_index}"
|
|
self.content_tree.tag_configure(
|
|
current_color_tag, foreground=colors[color_index])
|
|
|
|
backup_type_display = backup_info.get("type", "N/A")
|
|
if backup_info.get("backup_type_base") != "Full":
|
|
backup_type_display = f"▲ {backup_type_display}"
|
|
|
|
self.content_tree.insert("", "end", values=(
|
|
backup_info.get("date", "N/A"),
|
|
backup_info.get("time", "N/A"),
|
|
backup_type_display,
|
|
backup_info.get("size", "N/A"),
|
|
backup_info.get("source", "N/A"),
|
|
backup_info.get("comment", "")
|
|
), tags=(current_color_tag,), iid=backup_info.get("folder_name"))
|
|
self._on_item_select(None)
|
|
|
|
def _on_item_select(self, event):
|
|
is_selected = True if self.content_tree.focus() else False
|
|
self.parent_view.update_button_state(is_selected)
|
|
|
|
def _edit_comment(self):
|
|
selected_item_id = self.content_tree.focus()
|
|
if not selected_item_id:
|
|
return
|
|
|
|
selected_backup = next((b for b in self.user_backups_list if b.get(
|
|
"folder_name") == selected_item_id), None)
|
|
if not selected_backup:
|
|
return
|
|
|
|
info_file_path = selected_backup.get('info_file_path')
|
|
if not info_file_path or not os.path.isfile(info_file_path):
|
|
MessageDialog(self, message_type="error", title="Error",
|
|
text=f"Metadata file not found: {info_file_path}")
|
|
return
|
|
|
|
CommentEditorDialog(self, info_file_path, self.backup_manager)
|
|
|
|
self.parent_view.show(self.backup_path, self.user_backups_list)
|
|
|
|
def _restore_selected(self):
|
|
selected_item_id = self.content_tree.focus()
|
|
if not selected_item_id:
|
|
return
|
|
|
|
MessageDialog(master=self, message_type="info",
|
|
title="Info", text="User restore not implemented yet.")
|
|
|
|
def _delete_selected(self):
|
|
selected_item_id = self.content_tree.focus()
|
|
if not selected_item_id:
|
|
return
|
|
|
|
selected_backup = next((b for b in self.user_backups_list if b.get(
|
|
"folder_name") == selected_item_id), None)
|
|
|
|
if not selected_backup:
|
|
return
|
|
|
|
folder_to_delete = selected_backup['full_path']
|
|
is_encrypted = selected_backup['is_encrypted']
|
|
password = None
|
|
|
|
if is_encrypted:
|
|
password = self.backup_manager.encryption_manager.get_password(
|
|
confirm=False)
|
|
if not password:
|
|
self.actions.logger.log(
|
|
"Password entry cancelled, aborting deletion.")
|
|
return
|
|
|
|
self.actions._set_ui_state(False)
|
|
self.parent_view.show_deletion_status(
|
|
Msg.STR["deleting_backup_in_progress"])
|
|
|
|
self.backup_manager.start_delete_backup(
|
|
path_to_delete=folder_to_delete,
|
|
is_encrypted=is_encrypted,
|
|
is_system=False,
|
|
base_dest_path=self.backup_path,
|
|
password=password,
|
|
queue=self.winfo_toplevel().queue
|
|
)
|