mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-24 09:57:08 -06:00
crates/ui-egui/Cargo.toml: kept main's photocraft-psd dev-dependency (testgen feature) and added the PR's tempfile dev-dependency docs/scorecard.md: ours
This commit is contained in:
Generated
+1
@@ -5076,6 +5076,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sys-locale",
|
||||
"tempfile",
|
||||
"toml",
|
||||
"unicode-bidi",
|
||||
"winsafe",
|
||||
|
||||
@@ -383,8 +383,11 @@ fn preferences_may_grant_ambient_paths(id: &str, params: &Value) -> bool {
|
||||
fn preference_uses_ambient_filesystem(path: &str) -> bool {
|
||||
// The engine skips empty segments (`.scriptEvents.enabled` sets `scriptEvents.enabled`), so
|
||||
// judge the first non-empty one; no segment at all is the whole-preferences update.
|
||||
let Some(section) = path.split('.').find(|segment| !segment.is_empty()) else { return true };
|
||||
let mut segments = path.split('.').filter(|segment| !segment.is_empty());
|
||||
let Some(section) = segments.next() else { return true };
|
||||
// File Handling's LUT folder (#6558) is listed from disk; the rest of that section is not.
|
||||
matches!(section, "colorSettings" | "scriptEvents" | "historyLog" | "plugIns" | "scratchDisks")
|
||||
|| (section == "fileHandling" && segments.next().is_none_or(|key| key == "lutFolder"))
|
||||
}
|
||||
|
||||
fn command_uses_ambient_path(id: &str, params: &Value) -> bool {
|
||||
@@ -862,6 +865,8 @@ mod tests {
|
||||
"historyLog.filePath",
|
||||
".historyLog.filePath",
|
||||
"plugIns.additionalPluginsFolder",
|
||||
"fileHandling.lutFolder",
|
||||
"fileHandling",
|
||||
"scratchDisks.disks",
|
||||
".",
|
||||
"..historyLog.filePath",
|
||||
@@ -872,6 +877,7 @@ mod tests {
|
||||
authorize_engine_command("prefs.set", &serde_json::json!({"values": {"interface.language": "fr", "historyLog.filePath": "/outside/log"}})).is_err()
|
||||
);
|
||||
assert!(authorize_engine_command("prefs.set", &serde_json::json!({"path": "interface.language", "value": "fr"})).is_ok());
|
||||
assert!(authorize_engine_command("prefs.set", &serde_json::json!({"path": "fileHandling.autosave", "value": false})).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -168,6 +168,44 @@ fn base_name(path: &str) -> String {
|
||||
path.rsplit(['/', '\\']).next().unwrap_or(path).to_string()
|
||||
}
|
||||
|
||||
/// Most LUT files [`folder_luts`] lists.
|
||||
const MAX_FOLDER_LUTS: usize = 500;
|
||||
/// Largest LUT file [`folder_luts`] lists: a 129³ `.cube` (the parser's largest) is about 60 MB.
|
||||
const MAX_FOLDER_LUT_BYTES: u64 = 64 << 20;
|
||||
|
||||
/// The `.cube`, `.3dl` and `.look` files directly in `dir`, as (file name, path) sorted by name,
|
||||
/// for Color Lookup's dropdown (Preferences › File Handling › LUT Folder, #6558). An empty,
|
||||
/// missing or unreadable folder lists nothing; hidden files, subfolders and files too large to be
|
||||
/// a LUT are skipped. A symlink to a LUT file is listed, as a studio's shared library often is.
|
||||
/// Contents are only parsed when one is picked, which reports a malformed file.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn folder_luts(dir: &str) -> Vec<(String, String)> {
|
||||
let dir = dir.trim();
|
||||
let Some(entries) = (!dir.is_empty()).then(|| std::fs::read_dir(dir).ok()).flatten() else { return Vec::new() };
|
||||
let mut out: Vec<(String, String)> = entries
|
||||
.flatten()
|
||||
.filter_map(|e| {
|
||||
let name = e.file_name().into_string().ok()?;
|
||||
let ext = name.rsplit_once('.')?.1.to_ascii_lowercase();
|
||||
if name.starts_with('.') || !matches!(ext.as_str(), "cube" | "3dl" | "look") {
|
||||
return None;
|
||||
}
|
||||
let path = e.path();
|
||||
let meta = std::fs::metadata(&path).ok()?;
|
||||
(meta.is_file() && meta.len() <= MAX_FOLDER_LUT_BYTES).then_some((name, path.to_str()?.to_string()))
|
||||
})
|
||||
.collect();
|
||||
out.sort_by_key(|(name, _)| name.to_lowercase());
|
||||
out.truncate(MAX_FOLDER_LUTS);
|
||||
out
|
||||
}
|
||||
|
||||
/// No file system on the web: a LUT folder lists nothing there.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn folder_luts(_: &str) -> Vec<(String, String)> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn read_file(path: &str) -> Option<Vec<u8>> {
|
||||
std::fs::read(path).ok()
|
||||
|
||||
@@ -731,3 +731,23 @@ fn invert_turns_the_black_of_quick_mask_and_alpha_channels_white() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// #6558: a LUT folder lists its LUT files by name, case-insensitively, and nothing else.
|
||||
#[test]
|
||||
fn folder_luts_lists_lut_files_by_name() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
for f in ["b.cube", "A.3DL", "c.look", "notes.txt", ".hidden.cube", "noext"] {
|
||||
std::fs::write(dir.path().join(f), "LUT_3D_SIZE 2\n").unwrap();
|
||||
}
|
||||
std::fs::create_dir(dir.path().join("sub.cube")).unwrap();
|
||||
// Too large to be a LUT (sparse, so cheap to make).
|
||||
std::fs::File::create(dir.path().join("huge.cube")).unwrap().set_len(MAX_FOLDER_LUT_BYTES + 1).unwrap();
|
||||
// A symlink to a LUT is listed: shared LUT libraries are often linked in.
|
||||
#[cfg(unix)]
|
||||
std::os::unix::fs::symlink(dir.path().join("b.cube"), dir.path().join("linked.cube")).unwrap();
|
||||
let want: Vec<_> = ["A.3DL", "b.cube", "c.look"].into_iter().chain(cfg!(unix).then_some("linked.cube")).collect();
|
||||
let names: Vec<_> = folder_luts(dir.path().to_str().unwrap()).into_iter().map(|(name, _)| name).collect();
|
||||
assert_eq!(names, want);
|
||||
assert!(folder_luts("").is_empty());
|
||||
assert!(folder_luts(dir.path().join("missing").to_str().unwrap()).is_empty());
|
||||
}
|
||||
|
||||
@@ -465,6 +465,9 @@ pub struct FileHandling {
|
||||
pub recent_file_count: u32,
|
||||
/// Most recently opened files, newest first (File › Open Recent).
|
||||
pub recent_files: Vec<String>,
|
||||
/// A folder whose `.cube`, `.3dl` and `.look` files Color Lookup lists under its built-in
|
||||
/// looks (native only, #6558). Empty lists none.
|
||||
pub lut_folder: String,
|
||||
}
|
||||
|
||||
impl Default for FileHandling {
|
||||
@@ -483,6 +486,7 @@ impl Default for FileHandling {
|
||||
rasterize_svg_groups_deeper_than: photocraft_doc::MAX_GROUP_DEPTH as u32,
|
||||
recent_file_count: 20,
|
||||
recent_files: Vec::new(),
|
||||
lut_folder: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,8 @@ photocraft-testkit = { workspace = true }
|
||||
# examples/perf_scenarios.rs (a 500-brush .abr for the #210 import row) and large_file_bench.rs
|
||||
# (the generated 2 GB PSD and 4.4 GB PSB of P20, `fixtures`).
|
||||
photocraft-psd = { workspace = true, features = ["testgen"] }
|
||||
# Isolated, self-cleaning folders for filesystem tests (the LUT folder, #6558).
|
||||
tempfile = "3"
|
||||
|
||||
[build-dependencies]
|
||||
# Deflates the translation catalogs and egui's default fonts (build.rs); already linked at run time to inflate them.
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
//! values). Selective Color previews live while a slider drags, via `app.live_adjust` carrying
|
||||
//! the full parameter set; Color Lookup commits on every change (its table isn't in the params).
|
||||
|
||||
use std::sync::{Arc, Mutex, PoisonError};
|
||||
|
||||
use photocraft_doc::{Adjustment, LayerId};
|
||||
use photocraft_engine::adjust_cmds::RANGES;
|
||||
use serde_json::{Value, json};
|
||||
@@ -90,14 +92,94 @@ fn browse_color_lookup(app: &mut PhotocraftApp, layer: LayerId) -> Result<Value,
|
||||
})
|
||||
}
|
||||
|
||||
/// Dropdown key prefix of a LUT listed from the LUT folder; the rest of the key is its path.
|
||||
const FOLDER_LUT: &str = "file:";
|
||||
|
||||
type FolderLuts = Vec<(String, String)>;
|
||||
|
||||
/// The LUT folder scan kept between frames: the latest finished list of `folder`, and the scan
|
||||
/// in flight, if any.
|
||||
#[derive(Clone, Default)]
|
||||
struct FolderScan {
|
||||
folder: String,
|
||||
started: Option<f64>,
|
||||
luts: FolderLuts,
|
||||
pending: Option<Arc<Mutex<Option<FolderLuts>>>>,
|
||||
}
|
||||
|
||||
/// The LUT folder's files (Preferences › File Handling › LUT Folder, #6558). The folder is
|
||||
/// scanned on a worker thread at most every couple of seconds, since a network share can be
|
||||
/// slow; until a scan finishes the dropdown shows the previous one.
|
||||
fn folder_luts(app: &PhotocraftApp, ctx: &egui::Context) -> FolderLuts {
|
||||
let folder = app.session.prefs().file_handling.lut_folder.clone();
|
||||
let now = ctx.input(|input| input.time);
|
||||
let key = egui::Id::new("color-lookup-folder");
|
||||
let mut scan = ctx.data(|data| data.get_temp::<FolderScan>(key)).unwrap_or_default();
|
||||
if let Some(done) = scan.pending.as_ref().and_then(|p| p.lock().unwrap_or_else(PoisonError::into_inner).take()) {
|
||||
scan.luts = done;
|
||||
scan.pending = None;
|
||||
}
|
||||
if scan.folder != folder {
|
||||
// Another folder: never show the old one's files, and drop its scan's result.
|
||||
scan = FolderScan { folder: folder.clone(), ..FolderScan::default() };
|
||||
}
|
||||
if scan.pending.is_none() && scan.started.is_none_or(|at| now - at >= 2.0) {
|
||||
scan.started = Some(now);
|
||||
scan.pending = Some(start_folder_scan(folder, ctx.clone()));
|
||||
}
|
||||
let luts = scan.luts.clone();
|
||||
ctx.data_mut(|data| data.insert_temp(key, scan));
|
||||
luts
|
||||
}
|
||||
|
||||
/// Scan `folder` on a worker thread (on the web, where there are none, right here: it lists
|
||||
/// nothing there anyway).
|
||||
fn start_folder_scan(folder: String, ctx: egui::Context) -> Arc<Mutex<Option<FolderLuts>>> {
|
||||
let result = Arc::new(Mutex::new(None));
|
||||
let shared = Arc::clone(&result);
|
||||
let work = move || {
|
||||
let luts = photocraft_engine::adjust_cmds::folder_luts(&folder);
|
||||
*shared.lock().unwrap_or_else(PoisonError::into_inner) = Some(luts);
|
||||
ctx.request_repaint();
|
||||
};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
if std::thread::Builder::new().name("lut-folder-scan".into()).spawn(work).is_err() {
|
||||
// No thread: list nothing this time; the next scan retries.
|
||||
*result.lock().unwrap_or_else(PoisonError::into_inner) = Some(Vec::new());
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
work();
|
||||
result
|
||||
}
|
||||
|
||||
/// The dropdown key for the current look: a built-in id, a LUT folder file, `none` or `custom`.
|
||||
fn current_lookup(name: &str, has_lut: bool, folder: &[(String, String)]) -> String {
|
||||
if !has_lut {
|
||||
return "none".into();
|
||||
}
|
||||
if let Some((id, _)) = photocraft_engine::adjust_cmds::LOOKS.iter().find(|b| b.1 == name) {
|
||||
return id.to_string();
|
||||
}
|
||||
folder.iter().find(|(file, _)| file == name).map_or_else(|| "custom".into(), |(_, path)| format!("{FOLDER_LUT}{path}"))
|
||||
}
|
||||
|
||||
/// `layer.setAdjustment` params for a dropdown key: a folder file is read and embedded like
|
||||
/// Load 3D LUT… does; anything else is a built-in id or `none`.
|
||||
fn lookup_params(key: &str) -> Value {
|
||||
match key.strip_prefix(FOLDER_LUT) {
|
||||
Some(path) => json!({"file": path}),
|
||||
None => json!({"lut": key}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn color_lookup_editor(app: &mut PhotocraftApp, ui: &mut egui::Ui, id: LayerId, adj: &Adjustment) {
|
||||
let t = Tokens::get(ui.ctx());
|
||||
let Adjustment::ColorLookup { name, lut, size, tetrahedral, dither } = adj else {
|
||||
return;
|
||||
};
|
||||
let builtins = photocraft_engine::adjust_cmds::LOOKS;
|
||||
let current =
|
||||
if lut.is_none() { "none".to_string() } else { builtins.iter().find(|b| b.1 == name).map_or_else(|| "custom".to_string(), |b| b.0.to_string()) };
|
||||
let folder = folder_luts(app, ui.ctx());
|
||||
let current = current_lookup(name, lut.is_some(), &folder);
|
||||
let current = app
|
||||
.live_adjust
|
||||
.as_ref()
|
||||
@@ -108,6 +190,7 @@ pub fn color_lookup_editor(app: &mut PhotocraftApp, ui: &mut egui::Ui, id: Layer
|
||||
let custom_label = format!("{name} ({size}³)");
|
||||
let mut opts: Vec<(String, &str)> = vec![("load".into(), tl!("Load 3D LUT…")), ("none".into(), tl!("None"))];
|
||||
opts.extend(builtins.iter().map(|(id, label)| (id.to_string(), *label)));
|
||||
opts.extend(folder.iter().map(|(file, path)| (format!("{FOLDER_LUT}{path}"), file.as_str())));
|
||||
if current == "custom" {
|
||||
opts.push(("custom".into(), custom_label.as_str()));
|
||||
}
|
||||
@@ -127,14 +210,16 @@ pub fn color_lookup_editor(app: &mut PhotocraftApp, ui: &mut egui::Ui, id: Layer
|
||||
let (chosen, _) = widgets::dropdown_wheel_hovered(ui, &format!("clrl-{}", id.0), &mut sel, &opts, 170.0);
|
||||
if !chosen.is_empty() {
|
||||
if cycle_input {
|
||||
if sel != "load" && sel != "custom" {
|
||||
// ponytail: wheel previews cover the built-ins only; a folder LUT would be re-read
|
||||
// from disk on every step. Pick it with a click.
|
||||
if sel != "load" && sel != "custom" && !sel.starts_with(FOLDER_LUT) {
|
||||
app.live_adjust = Some((id, json!({"lut": sel})));
|
||||
cycling_preview = true;
|
||||
}
|
||||
} else if sel == "load" {
|
||||
browse = true;
|
||||
} else if sel != current && sel != "custom" {
|
||||
params = Some(json!({"lut": sel}));
|
||||
params = Some(lookup_params(&sel));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -219,6 +304,43 @@ mod lookup_picker_tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// #6558: a LUT folder file is offered by name, and picking it embeds it.
|
||||
#[test]
|
||||
fn lut_folder_files_are_offered_and_embedded() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let dir = tmp.path();
|
||||
let path = dir.join("film.cube");
|
||||
std::fs::write(&path, CUBE).unwrap();
|
||||
let mut app = PhotocraftApp::new(photocraft_engine::Session::new(), crate::Services::default());
|
||||
app.run("prefs.set", json!({"path": "fileHandling.lutFolder", "value": dir.to_str().unwrap()})).unwrap();
|
||||
// The scan runs on a worker thread: ask again until it has finished.
|
||||
let ctx = egui::Context::default();
|
||||
let mut folder = folder_luts(&app, &ctx);
|
||||
for _ in 0..500 {
|
||||
if !folder.is_empty() {
|
||||
break;
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
folder = folder_luts(&app, &ctx);
|
||||
}
|
||||
let key = format!("{FOLDER_LUT}{}", path.to_str().unwrap());
|
||||
assert_eq!(folder, [("film.cube".to_string(), path.to_str().unwrap().to_string())]);
|
||||
app.run("file.new", json!({"width": 16, "height": 12})).unwrap();
|
||||
let layer = app.run("layer.newAdjustmentLayer.colorLookup", json!({})).unwrap()["layer"].as_u64().unwrap();
|
||||
let mut p = lookup_params(&key);
|
||||
p["layer"] = json!(layer);
|
||||
app.run("layer.setAdjustment", p).unwrap();
|
||||
let photocraft_doc::LayerContent::Adjustment(Adjustment::ColorLookup { name, lut, .. }) =
|
||||
&app.session.active().unwrap().doc.layer(LayerId(layer)).unwrap().content
|
||||
else {
|
||||
panic!("not a Color Lookup layer")
|
||||
};
|
||||
assert!(lut.is_some());
|
||||
assert_eq!(current_lookup(name, true, &folder), key);
|
||||
assert_eq!(current_lookup(name, true, &[]), "custom");
|
||||
assert_eq!(lookup_params("none"), json!({"lut": "none"}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_lut_does_not_modify_the_existing_adjustment() {
|
||||
let (dialog, _) = file_dialog::fake(vec![Some(FileDialogAnswer::Contents("broken.cube".into(), b"invalid".to_vec()))]);
|
||||
|
||||
@@ -2008,6 +2008,7 @@
|
||||
Quick export format Formát rychlého exportu
|
||||
Quick export location Umístění rychlého exportu
|
||||
Recent file count Počet nedávných souborů
|
||||
LUT folder Složka LUT
|
||||
Recent files Nedávné soubory
|
||||
Recent fonts Nedávná písma
|
||||
Recover on launch Obnovit při spuštění
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1982,6 +1982,7 @@
|
||||
Quick export format Schnellexport-Format
|
||||
Quick export location Schnellexport-Ziel
|
||||
Recent file count Anzahl zuletzt geöffneter Dateien
|
||||
LUT folder LUT-Ordner
|
||||
Recent files Zuletzt geöffnete Dateien
|
||||
Recent fonts Zuletzt verwendete Schriften
|
||||
Recover on launch Beim Start wiederherstellen
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -2898,6 +2898,7 @@
|
||||
Raw Data Ακατέργαστα δεδομένα
|
||||
Raw Defaults Προεπιλογές RAW
|
||||
Recent file count Αριθμός πρόσφατων αρχείων
|
||||
LUT folder Φάκελος LUT
|
||||
Recent files Πρόσφατα αρχεία
|
||||
Recent fonts Πρόσφατες γραμματοσειρές
|
||||
Reconstruct Ανακατασκευή
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 1030 and column 18.
|
@@ -1996,6 +1996,7 @@
|
||||
Quick export format Formato de exportación rápida
|
||||
Quick export location Ubicación de exportación rápida
|
||||
Recent file count Número de archivos recientes
|
||||
LUT folder Carpeta de LUT
|
||||
Recent files Archivos recientes
|
||||
Recent fonts Fuentes recientes
|
||||
Recover on launch Recuperar al iniciar
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 4 and column 47.
|
@@ -1964,6 +1964,7 @@
|
||||
Quick export format Format d'exportation rapide
|
||||
Quick export location Emplacement d'exportation rapide
|
||||
Recent file count Nombre de fichiers récents
|
||||
LUT folder Dossier LUT
|
||||
Recent files Fichiers récents
|
||||
Recent fonts Polices récentes
|
||||
Recover on launch Récupérer au démarrage
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -3479,6 +3479,7 @@ cameraRaw Yellow Sárga
|
||||
Random Véletlenszerű
|
||||
Rasterize svg groups deeper than SVG-csoportok raszterizálása ennél mélyebb szinten
|
||||
Recent file count Legutóbbi fájlok száma
|
||||
LUT folder LUT-mappa
|
||||
Recent files Legutóbbi fájlok
|
||||
Recent Files Legutóbbi fájlok
|
||||
Recent fonts Legutóbbi betűtípusok
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1958,6 +1958,7 @@
|
||||
Quick export format Format ekspor cepat
|
||||
Quick export location Lokasi ekspor cepat
|
||||
Recent file count Jumlah berkas terbaru
|
||||
LUT folder Folder LUT
|
||||
Recent files Berkas terbaru
|
||||
Recent fonts Font terbaru
|
||||
Recover on launch Pulihkan saat dibuka
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 4 and column 47.
|
@@ -1990,6 +1990,7 @@
|
||||
Quick export format Formato esportazione rapida
|
||||
Quick export location Posizione esportazione rapida
|
||||
Recent file count Conteggio file recenti
|
||||
LUT folder Cartella LUT
|
||||
Recent files File recenti
|
||||
Recent fonts Font recenti
|
||||
Recover on launch Recupera all'avvio
|
||||
|
||||
|
@@ -1992,6 +1992,7 @@
|
||||
Quick export format クイック書き出しの形式
|
||||
Quick export location クイック書き出しの保存先
|
||||
Recent file count 最近使ったファイルの数
|
||||
LUT folder LUT フォルダー
|
||||
Recent files 最近使ったファイル
|
||||
Recent fonts 最近使ったフォント
|
||||
Recover on launch 起動時に復元
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1959,6 +1959,7 @@
|
||||
Quick export format 빠른 내보내기 형식
|
||||
Quick export location 빠른 내보내기 위치
|
||||
Recent file count 최근 파일 수
|
||||
LUT folder LUT 폴더
|
||||
Recent files 최근 파일
|
||||
Recent fonts 최근 글꼴
|
||||
Recover on launch 시작 시 복구
|
||||
|
||||
|
@@ -1964,6 +1964,7 @@
|
||||
Quick export format Formaat snel exporteren
|
||||
Quick export location Locatie snel exporteren
|
||||
Recent file count Aantal recente bestanden
|
||||
LUT folder LUT-map
|
||||
Recent files Recente bestanden
|
||||
Recent fonts Recente lettertypen
|
||||
Recover on launch Herstellen bij opstarten
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 8 and column 34.
|
@@ -1985,6 +1985,7 @@
|
||||
Quick export format Format szybkiego eksportu
|
||||
Quick export location Lokalizacja szybkiego eksportu
|
||||
Recent file count Liczba ostatnich plików
|
||||
LUT folder Folder LUT
|
||||
Recent files Ostatnie pliki
|
||||
Recent fonts Ostatnie czcionki
|
||||
Recover on launch Odzyskaj przy uruchomieniu
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1958,6 +1958,7 @@
|
||||
Quick export format Formato da exportação rápida
|
||||
Quick export location Local da exportação rápida
|
||||
Recent file count Número de arquivos recentes
|
||||
LUT folder Pasta de LUTs
|
||||
Recent files Arquivos recentes
|
||||
Recent fonts Fontes recentes
|
||||
Recover on launch Recuperar ao iniciar
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -2420,6 +2420,7 @@
|
||||
Quick export format Формат быстрого экспорта
|
||||
Quick export location Расположение быстрого экспорта
|
||||
Recent file count Число недавних файлов
|
||||
LUT folder Папка LUT
|
||||
Recent files Недавние файлы
|
||||
Recent fonts Недавние шрифты
|
||||
Recover on launch Восстановление при запуске
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1944,6 +1944,7 @@
|
||||
Quick export format Format brzog izvoza
|
||||
Quick export location Lokacija brzog izvoza
|
||||
Recent file count Broj nedavnih datoteka
|
||||
LUT folder Fascikla LUT
|
||||
Recent files Nedavne datoteke
|
||||
Recent fonts Nedavni fontovi
|
||||
Recover on launch Oporavi pri pokretanju
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1940,6 +1940,7 @@
|
||||
Quick export format Hızlı dışa aktarma formatı
|
||||
Quick export location Hızlı dışa aktarma konumu
|
||||
Recent file count Son dosya sayısı
|
||||
LUT folder LUT klasörü
|
||||
Recent files Son dosyalar
|
||||
Recent fonts En yeni yazı tipleri
|
||||
Recover on launch Başlatıldığında kurtar
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 8 and column 34.
|
@@ -2392,6 +2392,7 @@
|
||||
Quick export format Формат швидкого експорту
|
||||
Quick export location Розташування швидкого експорту
|
||||
Recent file count Кількість нещодавніх файлів
|
||||
LUT folder Папка LUT
|
||||
Recent files Нещодавні файли
|
||||
Recent fonts Нещодавні шрифти
|
||||
Recover on launch Відновлювати під час запуску
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 1394 and column 18.
|
@@ -1502,6 +1502,7 @@ cameraRaw Shadows Vùng tối
|
||||
Quick export format Định dạng xuất nhanh
|
||||
Quick export location Vị trí xuất nhanh
|
||||
Recent file count Số tệp gần đây
|
||||
LUT folder Thư mục LUT
|
||||
Recent files Tệp gần đây
|
||||
Recover on launch Khôi phục khi khởi chạy
|
||||
Remember workspace changes Ghi nhớ thay đổi không gian làm việc
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -2008,6 +2008,7 @@
|
||||
Quick export format 快速导出格式
|
||||
Quick export location 快速导出位置
|
||||
Recent file count 最近使用的文件数量
|
||||
LUT folder LUT 文件夹
|
||||
Recent files 最近使用的文件
|
||||
Recent fonts 最近使用的字体
|
||||
Recover on launch 启动时恢复
|
||||
|
||||
|
@@ -2001,6 +2001,7 @@
|
||||
Quick export format 快速匯出格式
|
||||
Quick export location 快速匯出位置
|
||||
Recent file count 最近檔案數量
|
||||
LUT folder LUT 資料夾
|
||||
Recent files 最近檔案
|
||||
Recent fonts 最近使用的字體
|
||||
Recover on launch 啟動時復原
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1246,7 +1246,13 @@ fn humanize(key: &str) -> String {
|
||||
s.push(ch);
|
||||
}
|
||||
}
|
||||
s.replace("Psd", "PSD").replace("Gpu", "GPU").replace("Ui ", "UI ").replace("Mb", "(MB)").replace("Exif", "EXIF").replace("Hud", "HUD")
|
||||
s.replace("Psd", "PSD")
|
||||
.replace("Gpu", "GPU")
|
||||
.replace("Ui ", "UI ")
|
||||
.replace("Mb", "(MB)")
|
||||
.replace("Exif", "EXIF")
|
||||
.replace("Hud", "HUD")
|
||||
.replace("Lut ", "LUT ")
|
||||
}
|
||||
|
||||
fn choice_label(v: &str) -> String {
|
||||
|
||||
Reference in New Issue
Block a user