improv(app): use about widget

This commit is contained in:
Vukašin Vojinović 2025-09-03 23:48:19 +02:00 committed by Jeremy Soller
parent f95869a631
commit 4d642ee3fa
4 changed files with 62 additions and 101 deletions

View file

@ -6,9 +6,6 @@ edition = "2024"
license = "GPL-3.0-only" license = "GPL-3.0-only"
rust-version = "1.85" rust-version = "1.85"
[build-dependencies]
vergen = { version = "8", features = ["git", "gitcl"] }
[dependencies] [dependencies]
anyhow = "1" anyhow = "1"
chrono = { version = "0.4", features = ["unstable-locales"] } chrono = { version = "0.4", features = ["unstable-locales"] }
@ -102,7 +99,12 @@ default = [
"xz2", "xz2",
] ]
dbus-config = ["libcosmic/dbus-config"] dbus-config = ["libcosmic/dbus-config"]
desktop = ["libcosmic/desktop", "dep:cosmic-mime-apps", "dep:xdg"] desktop = [
"libcosmic/desktop",
"libcosmic/about",
"dep:cosmic-mime-apps",
"dep:xdg",
]
desktop-applet = [] desktop-applet = []
gvfs = ["dep:gio", "dep:glib"] gvfs = ["dep:gio", "dep:glib"]
io-uring = ["compio/io-uring", "dep:io-uring"] io-uring = ["compio/io-uring", "dep:io-uring"]

View file

@ -1,17 +0,0 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Rebuild if i18n files change
println!("cargo:rerun-if-changed=i18n");
// Emit version information (if not cached by just vendor)
let mut vergen = vergen::EmitBuilder::builder();
println!("cargo:rerun-if-env-changed=VERGEN_GIT_COMMIT_DATE");
if std::env::var_os("VERGEN_GIT_COMMIT_DATE").is_none() {
vergen.git_commit_date();
}
println!("cargo:rerun-if-env-changed=VERGEN_GIT_SHA");
if std::env::var_os("VERGEN_GIT_SHA").is_none() {
vergen.git_sha(false);
}
vergen.fail_on_error().emit()?;
Ok(())
}

View file

@ -30,11 +30,11 @@ size = Size
# Progress footer # Progress footer
details = Details details = Details
dismiss = Dismiss message dismiss = Dismiss message
operations-running = {$running} {$running -> operations-running = {$running} {$running ->
[one] operation [one] operation
*[other] operations *[other] operations
} running ({$percent}%)... } running ({$percent}%)...
operations-running-finished = {$running} {$running -> operations-running-finished = {$running} {$running ->
[one] operation [one] operation
*[other] operations *[other] operations
} running ({$percent}%), {$finished} finished... } running ({$percent}%), {$finished} finished...
@ -142,7 +142,7 @@ favorite-path-error = Error opening directory
favorite-path-error-description = favorite-path-error-description =
Unable to open "{$path}". Unable to open "{$path}".
It may not exist or you don't have permission to open it. It may not exist or you don't have permission to open it.
Would you like to remove it from the sidebar? Would you like to remove it from the sidebar?
remove = Remove remove = Remove
keep = Keep keep = Keep
@ -150,7 +150,8 @@ keep = Keep
# Context Pages # Context Pages
## About ## About
git-description = Git commit {$hash} on {$date} repository = Repository
support = Support
## Add Network Drive ## Add Network Drive
add-network-drive = Add network drive add-network-drive = Add network drive

View file

@ -34,8 +34,9 @@ use cosmic::{
style, surface, theme, style, surface, theme,
widget::{ widget::{
self, self,
about::About,
dnd_destination::DragId, dnd_destination::DragId,
horizontal_space, horizontal_space, icon,
menu::{action::MenuAction, key_bind::KeyBind}, menu::{action::MenuAction, key_bind::KeyBind},
segmented_button::{self, Entity}, segmented_button::{self, Entity},
vertical_space, vertical_space,
@ -634,6 +635,7 @@ impl PartialEq for WatcherWrapper {
// The [`App`] stores application-specific state. // The [`App`] stores application-specific state.
pub struct App { pub struct App {
core: Core, core: Core,
about: About,
nav_bar_context_id: segmented_button::Entity, nav_bar_context_id: segmented_button::Entity,
nav_model: segmented_button::SingleSelectModel, nav_model: segmented_button::SingleSelectModel,
tab_model: segmented_button::Model<segmented_button::SingleSelect>, tab_model: segmented_button::Model<segmented_button::SingleSelect>,
@ -1432,7 +1434,7 @@ impl App {
nav_model = nav_model.insert(|b| { nav_model = nav_model.insert(|b| {
b.text(fl!("recents")) b.text(fl!("recents"))
.icon(widget::icon::from_name("document-open-recent-symbolic")) .icon(icon::from_name("document-open-recent-symbolic"))
.data(Location::Recents) .data(Location::Recents)
}); });
@ -1448,12 +1450,10 @@ impl App {
nav_model = nav_model.insert(move |b| { nav_model = nav_model.insert(move |b| {
b.text(name.clone()) b.text(name.clone())
.icon( .icon(
widget::icon::icon(if path.is_dir() { icon::icon(if path.is_dir() {
tab::folder_icon_symbolic(&path, 16) tab::folder_icon_symbolic(&path, 16)
} else { } else {
widget::icon::from_name("text-x-generic-symbolic") icon::from_name("text-x-generic-symbolic").size(16).handle()
.size(16)
.handle()
}) })
.size(16), .size(16),
) )
@ -1470,7 +1470,7 @@ impl App {
nav_model = nav_model.insert(|b| { nav_model = nav_model.insert(|b| {
b.text(fl!("trash")) b.text(fl!("trash"))
.icon(widget::icon::icon(tab::trash_icon_symbolic(16))) .icon(icon::icon(tab::trash_icon_symbolic(16)))
.data(Location::Trash) .data(Location::Trash)
.divider_above() .divider_above()
}); });
@ -1478,8 +1478,8 @@ impl App {
if !MOUNTERS.is_empty() { if !MOUNTERS.is_empty() {
nav_model = nav_model.insert(|b| { nav_model = nav_model.insert(|b| {
b.text(fl!("networks")) b.text(fl!("networks"))
.icon(widget::icon::icon( .icon(icon::icon(
widget::icon::from_name("network-workgroup-symbolic") icon::from_name("network-workgroup-symbolic")
.size(16) .size(16)
.handle(), .handle(),
)) ))
@ -1512,7 +1512,7 @@ impl App {
b = b.data(Location::Network(uri, item.name(), None)); b = b.data(Location::Network(uri, item.name(), None));
} }
if let Some(icon) = item.icon(true) { if let Some(icon) = item.icon(true) {
b = b.icon(widget::icon::icon(icon).size(16)); b = b.icon(icon::icon(icon).size(16));
} }
if item.is_mounted() { if item.is_mounted() {
b = b.closable(); b = b.closable();
@ -1618,38 +1618,6 @@ impl App {
Task::none() Task::none()
} }
fn about(&self) -> Element<Message> {
let cosmic_theme::Spacing { space_xxs, .. } = theme::active().cosmic().spacing;
let repository = "https://github.com/pop-os/cosmic-files";
let hash = env!("VERGEN_GIT_SHA");
let short_hash: String = hash.chars().take(7).collect();
let date = env!("VERGEN_GIT_COMMIT_DATE");
widget::column::with_children(vec![
widget::svg(widget::svg::Handle::from_memory(
&include_bytes!(
"../res/icons/hicolor/128x128/apps/com.system76.CosmicFiles.svg"
)[..],
))
.into(),
widget::text::title3(fl!("cosmic-files")).into(),
widget::button::link(repository)
.on_press(Message::LaunchUrl(repository.to_string()))
.padding(0)
.into(),
widget::button::link(fl!(
"git-description",
hash = short_hash.as_str(),
date = date
))
.on_press(Message::LaunchUrl(format!("{}/commits/{}", repository, hash)))
.padding(0)
.into(),
])
.align_x(Alignment::Center)
.spacing(space_xxs)
.into()
}
fn network_drive(&self) -> Element<Message> { fn network_drive(&self) -> Element<Message> {
let cosmic_theme::Spacing { let cosmic_theme::Spacing {
space_xxs, space_m, .. space_xxs, space_m, ..
@ -1782,7 +1750,7 @@ impl App {
.into(), .into(),
if controller.is_paused() { if controller.is_paused() {
widget::tooltip( widget::tooltip(
widget::button::icon(widget::icon::from_name( widget::button::icon(icon::from_name(
"media-playback-start-symbolic", "media-playback-start-symbolic",
)) ))
.on_press(Message::PendingPause(*id, false)) .on_press(Message::PendingPause(*id, false))
@ -1793,7 +1761,7 @@ impl App {
.into() .into()
} else { } else {
widget::tooltip( widget::tooltip(
widget::button::icon(widget::icon::from_name( widget::button::icon(icon::from_name(
"media-playback-pause-symbolic", "media-playback-pause-symbolic",
)) ))
.on_press(Message::PendingPause(*id, true)) .on_press(Message::PendingPause(*id, true))
@ -1804,7 +1772,7 @@ impl App {
.into() .into()
}, },
widget::tooltip( widget::tooltip(
widget::button::icon(widget::icon::from_name("window-close-symbolic")) widget::button::icon(icon::from_name("window-close-symbolic"))
.on_press(Message::PendingCancel(*id)) .on_press(Message::PendingCancel(*id))
.padding(8), .padding(8),
widget::text::body(fl!("cancel")), widget::text::body(fl!("cancel")),
@ -2124,8 +2092,24 @@ impl Application for App {
}) })
}); });
let about = About::default()
.name(fl!("cosmic-files"))
.icon(icon::from_name(Self::APP_ID))
.version(env!("CARGO_PKG_VERSION"))
.author("System76")
.license("GPL-3.0-only")
.developers([("Jeremy Soller", "jeremy@system76.com")])
.links([
(fl!("repository"), "https://github.com/pop-os/cosmic-files"),
(
fl!("support"),
"https://github.com/pop-os/cosmic-files/issues",
),
]);
let mut app = App { let mut app = App {
core, core,
about,
nav_bar_context_id: segmented_button::Entity::null(), nav_bar_context_id: segmented_button::Entity::null(),
nav_model: segmented_button::ModelBuilder::default().build(), nav_model: segmented_button::ModelBuilder::default().build(),
tab_model: segmented_button::ModelBuilder::default().build(), tab_model: segmented_button::ModelBuilder::default().build(),
@ -2235,11 +2219,7 @@ impl Application for App {
cosmic::Action::App(Message::NavMenuAction(NavMenuAction::OpenInNewTab(entity))) cosmic::Action::App(Message::NavMenuAction(NavMenuAction::OpenInNewTab(entity)))
}) })
.context_menu(self.nav_context_menu(self.nav_bar_context_id)) .context_menu(self.nav_context_menu(self.nav_bar_context_id))
.close_icon( .close_icon(icon::from_name("media-eject-symbolic").size(16).icon())
widget::icon::from_name("media-eject-symbolic")
.size(16)
.icon(),
)
.into_container(); .into_container();
if !self.core.is_condensed() { if !self.core.is_condensed() {
@ -3606,7 +3586,7 @@ impl Application for App {
}); });
if let Some(entity) = maybe_entity { if let Some(entity) = maybe_entity {
self.nav_model self.nav_model
.icon_set(entity, widget::icon::icon(tab::trash_icon_symbolic(16))); .icon_set(entity, icon::icon(tab::trash_icon_symbolic(16)));
} }
return Task::batch([self.rescan_trash(), self.update_desktop()]); return Task::batch([self.rescan_trash(), self.update_desktop()]);
@ -4721,8 +4701,9 @@ impl Application for App {
} }
Some(match &self.context_page { Some(match &self.context_page {
ContextPage::About => context_drawer::context_drawer( ContextPage::About => context_drawer::about(
self.about(), &self.about,
Message::LaunchUrl,
Message::ToggleContextPage(ContextPage::About), Message::ToggleContextPage(ContextPage::About),
), ),
ContextPage::EditHistory => context_drawer::context_drawer( ContextPage::EditHistory => context_drawer::context_drawer(
@ -4932,7 +4913,7 @@ impl Application for App {
widget::dialog() widget::dialog()
.title("Failed operation") .title("Failed operation")
.body(format!("{:#?}\n{}", operation, err)) .body(format!("{:#?}\n{}", operation, err))
.icon(widget::icon::from_name("dialog-error").size(64)) .icon(icon::from_name("dialog-error").size(64))
//TODO: retry action //TODO: retry action
.primary_action( .primary_action(
widget::button::standard(fl!("cancel")).on_press(Message::DialogCancel), widget::button::standard(fl!("cancel")).on_press(Message::DialogCancel),
@ -4941,7 +4922,7 @@ impl Application for App {
DialogPage::ExtractPassword { id, password } => { DialogPage::ExtractPassword { id, password } => {
widget::dialog() widget::dialog()
.title(fl!("extract-password-required")) .title(fl!("extract-password-required"))
.icon(widget::icon::from_name("dialog-error").size(64)) .icon(icon::from_name("dialog-error").size(64))
.control(widget::text_input("", password).password().on_input( .control(widget::text_input("", password).password().on_input(
move |password| { move |password| {
Message::DialogUpdate(DialogPage::ExtractPassword { id: *id, password }) Message::DialogUpdate(DialogPage::ExtractPassword { id: *id, password })
@ -4962,7 +4943,7 @@ impl Application for App {
} => widget::dialog() } => widget::dialog()
.title(fl!("mount-error")) .title(fl!("mount-error"))
.body(error) .body(error)
.icon(widget::icon::from_name("dialog-error").size(64)) .icon(icon::from_name("dialog-error").size(64))
.primary_action( .primary_action(
widget::button::standard(fl!("try-again")).on_press(Message::DialogComplete), widget::button::standard(fl!("try-again")).on_press(Message::DialogComplete),
) )
@ -5105,7 +5086,7 @@ impl Application for App {
} => widget::dialog() } => widget::dialog()
.title(fl!("network-drive-error")) .title(fl!("network-drive-error"))
.body(error) .body(error)
.icon(widget::icon::from_name("dialog-error").size(64)) .icon(icon::from_name("dialog-error").size(64))
.primary_action( .primary_action(
widget::button::standard(fl!("try-again")).on_press(Message::DialogComplete), widget::button::standard(fl!("try-again")).on_press(Message::DialogComplete),
) )
@ -5216,7 +5197,7 @@ impl Application for App {
widget::mouse_area( widget::mouse_area(
widget::button::custom( widget::button::custom(
widget::row::with_children(vec![ widget::row::with_children(vec![
widget::icon(app.icon.clone()).size(32).into(), icon(app.icon.clone()).size(32).into(),
if app.is_default && !displayed_default { if app.is_default && !displayed_default {
displayed_default = true; displayed_default = true;
widget::text::body(fl!( widget::text::body(fl!(
@ -5229,9 +5210,7 @@ impl Application for App {
}, },
widget::horizontal_space().into(), widget::horizontal_space().into(),
if *selected == i { if *selected == i {
widget::icon::from_name("checkbox-checked-symbolic") icon::from_name("checkbox-checked-symbolic").size(16).into()
.size(16)
.into()
} else { } else {
widget::Space::with_width(Length::Fixed(16.0)).into() widget::Space::with_width(Length::Fixed(16.0)).into()
}, },
@ -5473,7 +5452,7 @@ impl Application for App {
"favorite-path-error-description", "favorite-path-error-description",
path = path.as_os_str().to_str() path = path.as_os_str().to_str()
)) ))
.icon(widget::icon::from_name("dialog-error").size(64)) .icon(icon::from_name("dialog-error").size(64))
.primary_action( .primary_action(
widget::button::destructive(fl!("remove")).on_press(Message::DialogComplete), widget::button::destructive(fl!("remove")).on_press(Message::DialogComplete),
) )
@ -5547,29 +5526,25 @@ impl Application for App {
progress_bar.into(), progress_bar.into(),
if all_paused { if all_paused {
widget::tooltip( widget::tooltip(
widget::button::icon(widget::icon::from_name( widget::button::icon(icon::from_name("media-playback-start-symbolic"))
"media-playback-start-symbolic", .on_press(Message::PendingPauseAll(false))
)) .padding(8),
.on_press(Message::PendingPauseAll(false))
.padding(8),
widget::text::body(fl!("resume")), widget::text::body(fl!("resume")),
widget::tooltip::Position::Top, widget::tooltip::Position::Top,
) )
.into() .into()
} else { } else {
widget::tooltip( widget::tooltip(
widget::button::icon(widget::icon::from_name( widget::button::icon(icon::from_name("media-playback-pause-symbolic"))
"media-playback-pause-symbolic", .on_press(Message::PendingPauseAll(true))
)) .padding(8),
.on_press(Message::PendingPauseAll(true))
.padding(8),
widget::text::body(fl!("pause")), widget::text::body(fl!("pause")),
widget::tooltip::Position::Top, widget::tooltip::Position::Top,
) )
.into() .into()
}, },
widget::tooltip( widget::tooltip(
widget::button::icon(widget::icon::from_name("window-close-symbolic")) widget::button::icon(icon::from_name("window-close-symbolic"))
.on_press(Message::PendingCancelAll) .on_press(Message::PendingCancelAll)
.padding(8), .padding(8),
widget::text::body(fl!("cancel")), widget::text::body(fl!("cancel")),
@ -5618,7 +5593,7 @@ impl Application for App {
if self.core.is_condensed() { if self.core.is_condensed() {
elements.push( elements.push(
//TODO: selected state is not appearing different //TODO: selected state is not appearing different
widget::button::icon(widget::icon::from_name("system-search-symbolic")) widget::button::icon(icon::from_name("system-search-symbolic"))
.on_press(Message::SearchClear) .on_press(Message::SearchClear)
.padding(8) .padding(8)
.selected(true) .selected(true)
@ -5636,7 +5611,7 @@ impl Application for App {
} }
} else { } else {
elements.push( elements.push(
widget::button::icon(widget::icon::from_name("system-search-symbolic")) widget::button::icon(icon::from_name("system-search-symbolic"))
.on_press(Message::SearchActivate) .on_press(Message::SearchActivate)
.padding(8) .padding(8)
.into(), .into(),