mirror of
https://github.com/narrrl/proton-drive-linux.git
synced 2026-11-04 00:47:00 +01:00
feat(gui): polish pass over wording, icons and responsiveness
The Sync page gives each folder one card with its state, a Synced / Online only switch and a menu for the rest; switching either way asks first and the download prompt shows the free space on the disk. Enabling, disabling and restarting the Proton Drive service ran systemctl on GTK's main thread and froze the window. They now run in the background, and every Retry button goes through one shared restart helper. Ten icons are bundled under resources/icons for glyphs no stock theme has or that read ambiguously (sync, share, people, link, menu, map and so on). A new test fails when the app or search prompt asks for a symbolic icon that is neither bundled nor on a list checked against Adwaita 50, libadwaita and GTK, or when a bundled icon is missing from the resource manifest. The tray's sync icon, which Adwaita 50 no longer ships, is now view-refresh. Wording follows one glossary, recorded in docs/TRANSLATING.md: "Proton Drive service" instead of "mount service" or "daemon", "Online only" instead of "On-demand", "Proton Drive folder" instead of "mountpoint". Dialog headings and buttons use title case and page titles match the sidebar. Dates go through one formatter (app/dates.rs) and sizes through i18n::human_bytes, and the cache label is translated. The status tick stops while the window is hidden or minimized and runs every fifth tick while it is unfocused, refreshing as soon as it gains focus. "Sync complete" is announced only for large batches and only when the window isn't focused. Smaller fixes: Empty Trash hides when there is nothing to empty, copy buttons confirm with a check mark, the sidebar status spins until the first report, and Photos moves Import and Find Duplicates into a menu beside a labelled Select toggle. All catalogs are updated and complete.
This commit is contained in:
1 parent
e872cad058
commit
bd4f797d1f
48 files changed
+12684
-10991
No files matched your search
@@ -0,0 +1,160 @@
|
||||
//! Every symbolic icon the app window and the search prompt ask for must
|
||||
//! exist on a stock GNOME desktop.
|
||||
//!
|
||||
//! An icon name that no theme ships shows up as a missing-image glyph, and
|
||||
//! nothing fails until someone looks. So each `"…-symbolic"` literal in the
|
||||
//! sources has to be either one of the `pdfs-*` icons bundled in
|
||||
//! `resources/icons` (and listed in the gresource manifest), or on
|
||||
//! [`STOCK`], a list of names checked against Adwaita 50, libadwaita and
|
||||
//! GTK's own built-in icons.
|
||||
//!
|
||||
//! The tray is left out: its icons are drawn by the panel, from the panel's
|
||||
//! theme, not from our resources.
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// Stock symbolic icons the app may use. Before adding a name, check that
|
||||
/// Adwaita has it outside its `legacy` folder, or that libadwaita or GTK
|
||||
/// ship it themselves.
|
||||
const STOCK: &[&str] = &[
|
||||
// libadwaita
|
||||
"adw-external-link-symbolic",
|
||||
// GTK's built-in icons
|
||||
"info-outline-symbolic",
|
||||
// Adwaita
|
||||
"checkbox-checked-symbolic",
|
||||
"checkbox-symbolic",
|
||||
"computer-symbolic",
|
||||
"dialog-error-symbolic",
|
||||
"dialog-warning-symbolic",
|
||||
"document-edit-symbolic",
|
||||
"document-open-recent-symbolic",
|
||||
"document-open-symbolic",
|
||||
"document-save-symbolic",
|
||||
"drive-harddisk-symbolic",
|
||||
"edit-copy-symbolic",
|
||||
"edit-delete-symbolic",
|
||||
"edit-find-symbolic",
|
||||
"edit-undo-symbolic",
|
||||
"folder-download-symbolic",
|
||||
"folder-new-symbolic",
|
||||
"folder-open-symbolic",
|
||||
"folder-remote-symbolic",
|
||||
"folder-symbolic",
|
||||
"go-down-symbolic",
|
||||
"go-jump-symbolic",
|
||||
"go-next-symbolic",
|
||||
"go-previous-symbolic",
|
||||
"go-up-symbolic",
|
||||
"image-missing-symbolic",
|
||||
"image-x-generic-symbolic",
|
||||
"list-add-symbolic",
|
||||
"mark-location-symbolic",
|
||||
"media-playback-pause-symbolic",
|
||||
"media-playback-start-symbolic",
|
||||
"network-idle-symbolic",
|
||||
"network-offline-symbolic",
|
||||
"non-starred-symbolic",
|
||||
"object-select-symbolic",
|
||||
"preferences-system-symbolic",
|
||||
"process-stop-symbolic",
|
||||
"starred-symbolic",
|
||||
"system-search-symbolic",
|
||||
"text-x-generic-symbolic",
|
||||
"user-trash-symbolic",
|
||||
"view-grid-symbolic",
|
||||
"view-list-symbolic",
|
||||
"view-more-symbolic",
|
||||
"view-paged-symbolic",
|
||||
"view-pin-symbolic",
|
||||
"view-refresh-symbolic",
|
||||
"window-close-symbolic",
|
||||
"zoom-fit-best-symbolic",
|
||||
"zoom-in-symbolic",
|
||||
"zoom-out-symbolic",
|
||||
];
|
||||
|
||||
fn crate_dir() -> PathBuf {
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
}
|
||||
|
||||
fn rust_files(dir: &Path, out: &mut Vec<PathBuf>) {
|
||||
for entry in fs::read_dir(dir).unwrap() {
|
||||
let path = entry.unwrap().path();
|
||||
if path.is_dir() {
|
||||
rust_files(&path, out);
|
||||
} else if path.extension().is_some_and(|e| e == "rs") {
|
||||
out.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Every `"name-symbolic"` string literal in `source`. Found by suffix rather
|
||||
/// than by pairing quotes, so an escaped quote or a `'"'` elsewhere in the
|
||||
/// file can't hide one.
|
||||
fn symbolic_literals(source: &str) -> Vec<String> {
|
||||
const SUFFIX: &str = "-symbolic\"";
|
||||
let is_name = |c: char| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-';
|
||||
source
|
||||
.match_indices(SUFFIX)
|
||||
.filter_map(|(at, _)| {
|
||||
let head = &source[..at];
|
||||
let start = head.trim_end_matches(is_name).len();
|
||||
(start > 0 && head[..start].ends_with('"'))
|
||||
.then(|| format!("{}-symbolic", &head[start..]))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// The `pdfs-*` icons in `resources/icons`, by name.
|
||||
fn bundled() -> BTreeSet<String> {
|
||||
let mut names = BTreeSet::new();
|
||||
let root = crate_dir().join("resources/icons/scalable");
|
||||
for context in fs::read_dir(root).unwrap() {
|
||||
for icon in fs::read_dir(context.unwrap().path()).unwrap() {
|
||||
let path = icon.unwrap().path();
|
||||
if let Some(name) = path.file_stem().and_then(|s| s.to_str()) {
|
||||
names.insert(name.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
names
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_symbolic_icon_exists_on_a_stock_desktop() {
|
||||
let mut files = vec![crate_dir().join("src/prompt.rs")];
|
||||
rust_files(&crate_dir().join("src/app"), &mut files);
|
||||
let bundled = bundled();
|
||||
let mut unknown = BTreeSet::new();
|
||||
for file in files {
|
||||
let source = fs::read_to_string(&file).unwrap();
|
||||
for name in symbolic_literals(&source) {
|
||||
if !bundled.contains(&name) && !STOCK.contains(&name.as_str()) {
|
||||
let file = file
|
||||
.strip_prefix(crate_dir())
|
||||
.unwrap()
|
||||
.display()
|
||||
.to_string();
|
||||
unknown.insert(format!("{name} ({file})"));
|
||||
}
|
||||
}
|
||||
}
|
||||
assert!(
|
||||
unknown.is_empty(),
|
||||
"icons that are neither bundled nor on the stock list:\n {}",
|
||||
unknown.into_iter().collect::<Vec<_>>().join("\n ")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_bundled_icon_is_in_the_resource_manifest() {
|
||||
let manifest = fs::read_to_string(crate_dir().join("resources/pdfs.gresource.xml")).unwrap();
|
||||
let missing: Vec<String> = bundled()
|
||||
.into_iter()
|
||||
.filter(|name| name.starts_with("pdfs-") && !manifest.contains(&format!("/{name}.svg<")))
|
||||
.collect();
|
||||
assert!(missing.is_empty(), "not in pdfs.gresource.xml: {missing:?}");
|
||||
}
|
||||
Reference in new issue
Block a user