Merge branch 'master' into type-to-select

This commit is contained in:
Levi Portenier 2025-12-30 12:44:20 -07:00 committed by GitHub
commit 03ec21bdc9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 874 additions and 467 deletions

944
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[package]
name = "cosmic-files"
version = "0.1.0"
version = "1.0.0"
authors = ["Jeremy Soller <jeremy@system76.com>"]
edition = "2024"
license = "GPL-3.0-only"
@ -45,7 +45,7 @@ xdg-mime = { git = "https://github.com/ebassi/xdg-mime-rs" }
bzip2 = { version = "0.6", optional = true } #TODO: replace with pure Rust crate
flate2 = "1.1"
tar = "0.4.44"
lzma-rust2 = { version = "0.15", optional = true }
lzma-rust2 = { version = "0.15.4", optional = true }
ordermap = { version = "1.0.0", features = ["serde"] }
# Internationalization
i18n-embed = { version = "0.16", features = [
@ -56,7 +56,7 @@ i18n-embed-fl = "0.10"
rust-embed = "8"
slotmap = "1.0.7"
recently-used-xbel = { git = "https://github.com/pop-os/recently-used-xbel.git" }
zip = "6.0"
zip = "7"
uzers = "0.12.1"
md-5 = "0.10.6"
png = "0.18"
@ -65,7 +65,7 @@ num_cpus = "1.17.0"
# Completion-based IO runtime to enable io_uring / IOCP file IO support.
[dependencies.compio]
version = "0.16.0"
version = "0.17.0"
default-features = false
features = ["io", "macros", "polling", "runtime"]
@ -123,7 +123,7 @@ inherits = "release"
debug = true
[target.'cfg(unix)'.dependencies]
fork = "0.4"
fork = "0.6"
[target.'cfg(target_os = "linux")'.dependencies]
procfs = "0.18"

6
debian/changelog vendored
View file

@ -1,3 +1,9 @@
cosmic-files (1.0.0) jammy; urgency=medium
* Stable release.
-- Jeremy Soller <jeremy@system76.com> Mon, 29 Dec 2025 15:12:39 -0700
cosmic-files (0.1.0) jammy; urgency=medium
* Initial release.

View file

@ -1 +1,302 @@
progress = { $percent }%
cosmic-files = Cosmic Files
empty-folder = Tuščias aplankas
empty-folder-hidden = Tuščias aplankas (turi paslėptų failų)
no-results = Rezultatų nėra
filesystem = Failų sistema
home = Namai
networks = Tinklai
notification-in-progress = Vyksta failų operacijos
trash = Šiukšlinė
recents = Neseniai naudoti
undo = Anuliuoti
today = Šiandien
desktop-view-options = Darbalaukio peržiūros parinktys...
show-on-desktop = Rodyti darbalaukyje
desktop-folder-content = Darbalaukio aplanko turinys
mounted-drives = Prijungti kaupikliai
trash-folder-icon = Šiukšlinės aplanko piktograma
icon-size-and-spacing = Piktogramos dydis ir tarpai
icon-size = Piktogramos dydis
grid-spacing = Tinklelio tarpai
name = Pavadinimas
modified = Modifikuota
trashed-on = Ištrinta
size = Dydis
details = Išsami informacija
dismiss = Atmesti pranešimą
operations-running =
{ $running } { $running ->
[one] vykdoma operaciją
*[other] vykdomos operacijos
} ({ $percent }%)...
operations-running-finished =
{ $running } { $running ->
[one] vykdoma operacija
*[other] vykdomos operacijos
} ({ $percent }%), { $finished } baigtos...
pause = Pauzė
resume = Tęsti
create-archive = Sukurti archyvą
extract-password-required = Reikalingas slaptažodis
extract-to = Išskleistį į...
extract-to-title = Išskleisti į aplanką
empty-trash-title = Ištuštinti šiukšlinę?
empty-trash-warning = Šiukšlinjė esantys elementai bus ištrinti negrįžtamai
mount-error = Nepavyko pasiekti kaupyklės
create-new-file = Sukurti naują failą
create-new-folder = Sukurti naują aplanką
file-name = Failo pavadinimas
folder-name = Aplanko pavadinimas
file-already-exists = Failas su tokiu vardu jau egzistuoja
empty-trash = Ištuštinti šiukšlinę
folder-already-exists = Aplankas su tokiu vardu jau egzistuoja
name-hidden = Pavadinimai prasidedantys „.“ simboliu bus paslėpti
name-invalid = „{ $filename }“ negalima naudoti kaip pavadinimo
name-no-slashes = Pavadinime negali būti pasvyrųjų brūkšnių
cancel = Atšaukti
create = Sukurti
open = Atidaryti
open-file = Atidaryti failą
open-folder = Atidaryti aplanką
open-in-new-tab = Atidaryti naujame skirtuke
open-in-new-window = Atidaryti naujame lange
open-item-location = Atidaryti elemento vietą
open-multiple-files = Atidaryti keletą failų
open-multiple-folders = Atidaryti keletą aplankų
save = Išsaugoti
save-file = Išsaugoti failą
open-with-title = Kaip norite atidaryti „{ $name }“?
browse-store = Naršyti { $store }
other-apps = Kitos aplikacijos
related-apps = Susijusios aplikacijos
selected-items = Pažymėti { $items } elementai
permanently-delete-question = Ištrinti visam laikui?
delete = Ištrinti
permanently-delete-warning = { $target } bus ištrintas visam laikui. Šis veiksmas yra negrįžtamas.
rename-file = Pervadinti failą
rename-folder = Pervadinti aplanką
replace = Pakeisti
replace-title = „{ $filename }“ jau egzistuoja šioje vietoje
replace-warning = Ar norite pakeisti tai su tuo, kas yra įrašoma? Keičiant bus pakeistas turinys.
replace-warning-operation = Ar norite pakeisti tai? Pakeičiant bus keičiamas turinys.
original-file = Originalus failas
replace-with = Pakeisti su
apply-to-all = Pritaikyti visiems
keep-both = Palikti abu
skip = Praleisti
set-executable-and-launch = Nustatyti kaip paleidžiamą ir paleisti
set-executable-and-launch-description = Ar norite nustatyti „{ $name }“ kaip paleidžiamą ir paleisti iš karto?
set-and-launch = Nustatyti ir paleisti
open-with = Atidaryti su
owner = Savininkas
group = Grupė
other = Kita
none = Joks
execute-only = Tik paleidžiamas
write-only = Tik įrašomas
write-execute = Įrašyti ir paleisti
read-only = Tik skaitomas
read-execute = Skaitomas ir paleidžiamas
read-write = Skaitomas ir įrašomas
read-write-execute = Skaitomas, įrašomas ir paleidžiamas
favorite-path-error = Klaida atidarant aplanką
favorite-path-error-description =
Nepavyko atidaryt „{ $path }“
„{ $path }“ gali neegzistuoti arba neturi teisių atidaryti
Ar norėtumėte pašalinti tai iš šonjuostės?
remove = Pašalinti
keep = Išlaikyti
repository = Saugykla
support = Palaikymas
add-network-drive = Pridėti tinklo talpyklą
connect = Prijungti
connect-anonymously = Prijungti anonimiškai
connecting = Jungiamasi...
domain = Domenas
enter-server-address = Įveskite serverio adresą
network-drive-description =
Serverio adresą sudaro protokolas ir adresas.
Pavyzdžiui: ssh://192.168.0.1, ftp://[2001:db8::1]
network-drive-schemes =
Galimi protokolai, Prefiksas
AppleTalk, afp://
Duomenų Perdavimo Protokolas (File Transfer Protocol), ftp:// or ftps://
Tinklo Failų Sistema (Network File System), nfs://
Serverio Žinučių Blokas (Server Message Block), smb://
SSH Duomenų Perdavimo Protokolas, sftp:// or ssh://
WebDAV, dav:// or davs://
network-drive-error = Nepavyko pasiekti tinklo kaupyklos
password = Slaptažodis
remember-password = Prisiminti slaptažodį
try-again = Pabandyti dar kartą
username = Naudotojo vardas
cancelled = Atšaukta
edit-history = Redaguoti istoriją
history = Istorija
no-history = Istorija tuščia.
pending = Laukiama patvirtinimo
progress-cancelled = { $percent }%, atšaukta
progress-failed = { $percent }%, nepavyko
progress-paused = { $percent }%, pristabdyta
failed = Nepavyko
complete = Baigta
compressing =
Suspaudžiami { $items } { $items ->
[one] elementas
*[other] elementai
} iš „{ $from }“ į „{ $to }“ ({ $progress })...
compressed =
{ $items } { $items ->
[one] Suspaustas elementas
*[other] Suspausti elementai
} iš „{ $from }“ to „{ $to }“
copy_noun = Kopijuoti
creating = Kuriamas „{ $name }“ „{ $parent }“ lokacijoje
created = Sukurtas „{ $name }“ „{ $parent }“ lokacijoje
copying =
{ $items } { $items ->
[one] Kopijuojamas elementas
*[other] Kopijuojami elementai
} iš „{ $from }“ į „{ $to }“ ({ $progress })...
copied =
{ $items } { $items ->
[one] Nukopijuotas elementas
*[other] Nukopijuoti elementai
} iš „{ $from }“ į „{ $to }“
deleting =
{ $items } { $items ->
[one] Trinamas elementas
*[other] Trinami elementai
} iš { trash } { $progress }...
emptying-trash = Tuštinama { trash } ({ $progress })...
deleted =
{ $items } { $items ->
[one] Ištrintas elementas
*[other] Ištrinti elementai
} iš { trash }
emptied-trash = Ištuštinta { trash }
extracting =
{ $items } { $items ->
[one] Išskleidžiamas elementas
*[other] Išskleidžiami elementai
} iš „{ $from }“ į „{ $to }“ { $progress }...
extracted =
{ $items } { $items ->
[one] Iškleistas elementas
*[other] Iškleisti elementai
} iš „{ $from }“ į „{ $to }“
setting-executable-and-launching = Nustatomas „{ $name }“, kaip vykdomas, ir paleidžiamas
set-executable-and-launched = Nustatyti „{ $name }“, kaip vykdomą, ir paleisti
setting-permissions = Nustatomi leidimai { $mode } "{ $name }"
set-permissions = Nustatyti { $mode } leidimus „{ $name }“
moving =
{ $items } { $items ->
[one] Perkeliamas elementas
*[other] Perkeliami elementai
} iš „{ $from }“ į „{ $to }“ { $progress }...
moved =
{ $items } { $items ->
[one] Perkeltas elementas
*[other] Perkelti elementai
} iš „{ $from }“ į „{ $to }“
permanently-deleting =
{ $items } { $items ->
[one] Visam laikui ištrinamas elementas
*[other] Visam laikui ištrinami elementai
}
permanently-deleted =
{ $items } { $items ->
[one] Visam laikui ištrintas elementas
*[other] Visam laikui ištrinti elementai
}
removing-from-recents =
{ $items } { $items ->
[one] Pašalinamas elementas
*[other] Pašalinami elementi
} iš { recents }
removed-from-recents =
{ $items } { $items ->
[one] Pašalintas elementas
*[other] Pašalinti elementai
} iš { recents }
renaming = Pervadinamas „{ $from }“ į „{ $to }“
renamed = „{ $from }“ pervadintas į „{ $to }“
restoring =
{ $items } { $items ->
[one] Atkuriamas elementas
*[other] Atkuriami elementai
} iš { trash } ({ $progress })...
restored =
{ $items } { $items ->
[one] Atkurtas elementas
*[other] Atkurti elementai
} iš { trash }
unknown-folder = nežinomas aplankas
menu-open-with = Atidaryti su...
default-app = { $name } (numatytas)
show-details = Rodyti išsamią informaciją
type = Tipas: { $mime }
items = Elementai: { $items }
item-size = Dydis: { $size }
item-created = Sukurtas: { $created }
item-modified = Modifikuota: { $modified }
item-accessed = Paskutinė prieiga: { $accessed }
calculating = Skaičiuojama...
settings = Nustatymai
single-click = Vieno paspaudimo atidarymas
appearance = Išvaizda
match-desktop = Pagal darbalaukio temą
type-to-search = Norint ieškoti, pradėkite rašyti
type-to-search-recursive = Paieška dabartiniame aplanke ir jo poaplankiuose
type-to-search-enter-path = Įvedamas aplanko ar failo kelias
add-to-sidebar = Pridėti į šonjuostę
compress = Suspausti
delete-permanently = Ištrinti visam laikui
eject = Išstumti
extract-here = Išskleisti
new-file = Naujas failas...
new-folder = Naujas aplankas...
open-in-terminal = Atidaryti terminale
move-to-trash = Perkelti į šiukšlinę
restore-from-trash = Atkurti iš šiukšlinės
remove-from-sidebar = Pašalinti iš šonjuostės
sort-by-name = Rikiuoti pagal pavadinimą
sort-by-modified = Rikiuoti pagal modifikavimo laiką
sort-by-size = Rikiuoti pagal dydį
sort-by-trashed = Rikiuoti pagal ištrinimo laiką
remove-from-recents = Pašalinti iš neseniai naudotų
change-wallpaper = Pakeisti darbalaukio foną...
desktop-appearance = Darbalaukio išvaizda...
display-settings = Vaizdo nustatymai...
file = Failas
new-tab = Naujas skirtukas
new-window = Naujas langas
reload-folder = Perkrauti aplanką
rename = Pervadinti...
close-tab = Uždaryti skirtuką
quit = Išeiti
edit = Redaguoti
cut = Iškirpti
copy = Kopijuoti
paste = Įklijuoti
select-all = Pažymėti viską
zoom-in = Priartinti
default-size = Numatytas dydis
zoom-out = Nutolinti
view = Rodymas
grid-view = Tinklelio išdėstymas
list-view = Sąrašo išdėstymas
show-hidden-files = Rodyti paslėptus failus
list-directories-first = Pirmiau pateikti aplankus
gallery-preview = Galerijos peržiūra
menu-settings = Nustatymai...
menu-about = Apie COSMIC Files...
sort = Rikiuoti
sort-a-z = A-Ž
sort-z-a = Ž-A
sort-newest-first = Pirma naujausi
sort-oldest-first = Pirma seniausi
sort-smallest-to-largest = Nuo mažiausio iki didžiausio
sort-largest-to-smallest = Nuo didžiausio iki mažiausio

View file

@ -4179,10 +4179,19 @@ impl Application for App {
tab.sort_name = sort.0;
tab.sort_direction = sort.1;
let mut tasks = Vec::with_capacity(2);
if let Some(selection_paths) = selection_paths {
tab.select_paths(selection_paths);
// Ensure selected path is scrolled to after redraw
tasks.push(Task::done(cosmic::action::app(Message::TabMessage(
Some(entity),
tab::Message::ScrollToFocused,
))));
}
return clipboard::read_data::<ClipboardPaste>().map(|p| {
tasks.push(clipboard::read_data::<ClipboardPaste>().map(|p| {
cosmic::action::app(Message::CutPaths(match p {
Some(s) => match s.kind {
ClipboardKind::Copy => Vec::new(),
@ -4190,7 +4199,9 @@ impl Application for App {
},
None => Vec::new(),
}))
});
}));
return Task::batch(tasks);
}
}
}
@ -5615,7 +5626,7 @@ impl Application for App {
}
let finished = count - running;
total_progress /= count as f32;
if running > 1 || finished > 0 {
if running >= 1 && (running > 1 || finished > 0) {
if finished > 0 {
title = fl!(
"operations-running-finished",

View file

@ -50,6 +50,8 @@ fn gio_icon_to_path(icon: &gio::Icon, size: u16) -> Option<PathBuf> {
fn items(monitor: &gio::VolumeMonitor, sizes: IconSizes) -> MounterItems {
let mut items: MounterItems = (monitor.mounts().into_iter())
.enumerate()
// Hide shadowed mounts
.filter(|(_, mount)| !mount.is_shadowed())
.map(|(i, mount)| {
let root = MountExt::root(&mount);
let is_remote = root

View file

@ -527,14 +527,24 @@ fn update<Message: Clone>(
let offset = layout.virtual_offset();
let layout_bounds = layout.bounds();
let viewport_changed = state.viewport.map_or(true, |v| v != *viewport);
if let Some(message) = widget.on_resize.as_ref() {
if state.viewport != Some(*viewport) {
state.viewport = Some(*viewport);
if viewport_changed {
shell.publish(message(*viewport));
}
}
if let Event::Mouse(mouse::Event::CursorMoved { position }) = event {
state.viewport = Some(*viewport);
let should_check_hover = viewport_changed
|| matches!(
event,
Event::Mouse(mouse::Event::CursorMoved { .. })
| Event::Mouse(mouse::Event::WheelScrolled { .. })
);
if should_check_hover {
let position_in = cursor.position_in(layout_bounds);
match (position_in, state.last_position) {
(None, Some(_)) => {
@ -550,7 +560,9 @@ fn update<Message: Clone>(
_ => {}
}
state.last_position = position_in;
}
if let Event::Mouse(mouse::Event::CursorMoved { position }) = event {
let virtual_position = Point::new(
viewport.x - layout_bounds.x + position.x,
viewport.y - layout_bounds.y + position.y,

View file

@ -631,6 +631,17 @@ fn get_desktop_file_icon(path: &Path) -> Option<String> {
.map(str::to_string)
}
/// Creates an icon handle from a desktop file's Icon field value.
/// Supports both icon names (looked up in theme) and absolute paths (used directly).
fn desktop_icon_handle(icon: &str, size: u16) -> widget::icon::Handle {
let icon_path = Path::new(icon);
if icon_path.is_absolute() && icon_path.exists() {
widget::icon::from_path(icon_path.to_path_buf())
} else {
widget::icon::from_name(icon).size(size).handle()
}
}
pub fn parse_desktop_file(path: &Path) -> (Option<String>, Option<String>) {
let entry = match freedesktop_entry_parser::parse_entry(path) {
Ok(ok) => ok,
@ -694,15 +705,9 @@ pub fn item_from_gvfs_info(path: PathBuf, file_info: gio::FileInfo, sizes: IconS
if let Some(icon_name) = icon_name_opt {
(
mime,
widget::icon::from_name(&*icon_name)
.size(sizes.grid())
.handle(),
widget::icon::from_name(&*icon_name)
.size(sizes.list())
.handle(),
widget::icon::from_name(&*icon_name)
.size(sizes.list_condensed())
.handle(),
desktop_icon_handle(&icon_name, sizes.grid()),
desktop_icon_handle(&icon_name, sizes.list()),
desktop_icon_handle(&icon_name, sizes.list_condensed()),
)
} else {
(
@ -829,15 +834,9 @@ pub fn item_from_entry(
if let Some(icon_name) = icon_name_opt {
(
mime,
widget::icon::from_name(&*icon_name)
.size(sizes.grid())
.handle(),
widget::icon::from_name(&*icon_name)
.size(sizes.list())
.handle(),
widget::icon::from_name(icon_name)
.size(sizes.list_condensed())
.handle(),
desktop_icon_handle(&icon_name, sizes.grid()),
desktop_icon_handle(&icon_name, sizes.list()),
desktop_icon_handle(&icon_name, sizes.list_condensed()),
)
} else {
(
@ -1666,6 +1665,7 @@ pub enum Message {
Resize(Rectangle),
Scroll(Viewport),
ScrollTab(f32),
ScrollToFocused,
SearchContext(Location, SearchContextWrapper),
SearchReady(bool),
SelectAll,
@ -3906,6 +3906,13 @@ impl Tab {
.into(),
));
}
Message::ScrollToFocused => {
if let Some(offset) = self.select_focus_scroll() {
commands.push(Command::Iced(
scrollable::scroll_to(self.scrollable_id.clone(), offset).into(),
));
}
}
Message::SearchContext(location, context) => {
if location == self.location {
self.search_context = context.0;