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

@ -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(),