improv: update libcosmic with wallpaper page UI improvements
This commit is contained in:
parent
039aeb1e74
commit
55dc777bb3
17 changed files with 533 additions and 423 deletions
517
Cargo.lock
generated
517
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,7 @@
|
|||
[workspace]
|
||||
members = ["app", "page", "pages/*"]
|
||||
default-members = ["app"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.dependencies.libcosmic]
|
||||
git = "https://github.com/pop-os/libcosmic"
|
||||
|
|
@ -26,5 +27,3 @@ rev = "2e9bf9f"
|
|||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
# debug = true
|
||||
# lto = "thin"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
name = "cosmic-settings"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "GPL-3.0-only"
|
||||
license = "GPL-3.0"
|
||||
rust-version = "1.65.0"
|
||||
|
||||
[dependencies]
|
||||
|
|
@ -22,7 +22,7 @@ i18n-embed-fl = "0.7.0"
|
|||
itertools = "0.12.0"
|
||||
libcosmic = {workspace = true}
|
||||
once_cell = "1.19.0"
|
||||
regex = "1.10.2"
|
||||
regex = "1.10.3"
|
||||
rust-embed = "8.2.0"
|
||||
slotmap = "1.0.7"
|
||||
tokio = "1.35.1"
|
||||
|
|
@ -38,7 +38,7 @@ freedesktop-desktop-entry = "0.5.0"
|
|||
notify = "6.1.1"
|
||||
anyhow = "1.0"
|
||||
image = "0.24.8"
|
||||
serde = { version = "1.0.195", features = ["derive"] }
|
||||
serde = { version = "1.0.196", features = ["derive"] }
|
||||
ashpd = { version = "0.6.8", default-features = false }
|
||||
ron = "0.8"
|
||||
static_init = "1.0.3"
|
||||
|
|
|
|||
130
app/src/app.rs
130
app/src/app.rs
|
|
@ -18,9 +18,8 @@ use crate::subscription::desktop_files;
|
|||
use crate::widget::{page_title, search_header};
|
||||
use crate::PageCommands;
|
||||
use cosmic::app::DbusActivationMessage;
|
||||
use cosmic::dialog::file_chooser;
|
||||
use cosmic::iced::Subscription;
|
||||
use cosmic::widget::row;
|
||||
use cosmic::widget::{button, row, text_input};
|
||||
use cosmic::{
|
||||
app::{Command, Core},
|
||||
cosmic_config::config_subscription,
|
||||
|
|
@ -30,7 +29,7 @@ use cosmic::{
|
|||
window, Length,
|
||||
},
|
||||
prelude::*,
|
||||
widget::{column, container, icon, nav_bar, scrollable, search, segmented_button, settings},
|
||||
widget::{column, container, icon, nav_bar, scrollable, segmented_button, settings},
|
||||
Element,
|
||||
};
|
||||
use cosmic_panel_config::CosmicPanelConfig;
|
||||
|
|
@ -44,7 +43,6 @@ pub struct SettingsApp {
|
|||
active_page: page::Entity,
|
||||
config: Config,
|
||||
core: Core,
|
||||
file_chooser: Option<(file_chooser::Sender, page::Entity)>,
|
||||
nav_model: nav_bar::Model,
|
||||
pages: page::Binder<crate::pages::Message>,
|
||||
search_active: bool,
|
||||
|
|
@ -70,7 +68,6 @@ impl SettingsApp {
|
|||
#[derive(Clone, Debug)]
|
||||
pub enum Message {
|
||||
DesktopInfo,
|
||||
FileChooser(FileChooser),
|
||||
Error(String),
|
||||
Page(page::Entity),
|
||||
PageMessage(crate::pages::Message),
|
||||
|
|
@ -79,28 +76,12 @@ pub enum Message {
|
|||
SearchChanged(String),
|
||||
SearchClear,
|
||||
SearchSubmit,
|
||||
Search(search::Message),
|
||||
SetWindowTitle,
|
||||
OpenContextDrawer(Cow<'static, str>),
|
||||
CloseContextDrawer,
|
||||
SetTheme(cosmic::theme::Theme),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum FileChooser {
|
||||
Closed,
|
||||
Init(file_chooser::Sender),
|
||||
Open {
|
||||
title: String,
|
||||
accept_label: String,
|
||||
include_directories: bool,
|
||||
modal: bool,
|
||||
multiple_files: bool,
|
||||
},
|
||||
Opened,
|
||||
Selected(Vec<url::Url>),
|
||||
}
|
||||
|
||||
impl cosmic::Application for SettingsApp {
|
||||
type Executor = cosmic::executor::single::Executor;
|
||||
type Flags = crate::Args;
|
||||
|
|
@ -121,7 +102,6 @@ impl cosmic::Application for SettingsApp {
|
|||
active_page: page::Entity::default(),
|
||||
config: Config::new(),
|
||||
core,
|
||||
file_chooser: None,
|
||||
nav_model: nav_bar::Model::default(),
|
||||
pages: page::Binder::default(),
|
||||
search_active: false,
|
||||
|
|
@ -158,7 +138,7 @@ impl cosmic::Application for SettingsApp {
|
|||
let mut widgets = Vec::new();
|
||||
|
||||
widgets.push(if self.search_active {
|
||||
cosmic::widget::text_input::search_input("", &self.search_input)
|
||||
text_input::search_input("", &self.search_input)
|
||||
.width(Length::Fixed(240.0))
|
||||
.id(self.search_id.clone())
|
||||
.on_clear(Message::SearchClear)
|
||||
|
|
@ -166,7 +146,11 @@ impl cosmic::Application for SettingsApp {
|
|||
.on_submit(Message::SearchSubmit)
|
||||
.into()
|
||||
} else {
|
||||
cosmic::widget::search::button(Message::SearchActivate)
|
||||
icon::from_name("system-search-symbolic")
|
||||
.apply(button::icon)
|
||||
.padding([0, 16])
|
||||
.on_press(Message::SearchActivate)
|
||||
.into()
|
||||
});
|
||||
|
||||
widgets
|
||||
|
|
@ -234,53 +218,19 @@ impl cosmic::Application for SettingsApp {
|
|||
Subscription::batch(vec![
|
||||
window_break,
|
||||
desktop_files(0).map(|_| Message::DesktopInfo),
|
||||
config_subscription(0, "com.system76.CosmicPanel.Panel".into(), 1).map(
|
||||
|(_, e)| match e {
|
||||
Ok(config) => Message::PanelConfig(config),
|
||||
Err((errors, config)) => {
|
||||
for why in errors {
|
||||
tracing::error!(?why, "panel config load error");
|
||||
}
|
||||
Message::PanelConfig(config)
|
||||
}
|
||||
},
|
||||
),
|
||||
config_subscription(0, "com.system76.CosmicPanel.Dock".into(), 1).map(
|
||||
|(_, e)| match e {
|
||||
Ok(config) => Message::PanelConfig(config),
|
||||
Err((errors, config)) => {
|
||||
for why in errors {
|
||||
tracing::error!(?why, "dock config load error");
|
||||
}
|
||||
Message::PanelConfig(config)
|
||||
}
|
||||
},
|
||||
),
|
||||
file_chooser::subscription(|response| match response {
|
||||
file_chooser::Message::Closed => Message::FileChooser(FileChooser::Closed),
|
||||
|
||||
file_chooser::Message::Opened => Message::FileChooser(FileChooser::Opened),
|
||||
|
||||
file_chooser::Message::Selected(files) => {
|
||||
Message::FileChooser(FileChooser::Selected(files.uris().to_owned()))
|
||||
config_subscription(0, "com.system76.CosmicPanel.Panel".into(), 1).map(|update| {
|
||||
for why in update.errors {
|
||||
tracing::error!(?why, "panel config load error");
|
||||
}
|
||||
|
||||
file_chooser::Message::Err(why) => {
|
||||
let mut source: &dyn std::error::Error = &why;
|
||||
let mut string =
|
||||
format!("open dialog subscription errored\n cause: {source}");
|
||||
|
||||
while let Some(new_source) = source.source() {
|
||||
string.push_str(&format!("\n cause: {new_source}"));
|
||||
source = new_source;
|
||||
}
|
||||
|
||||
Message::Error(string)
|
||||
Message::PanelConfig(update.config)
|
||||
}),
|
||||
config_subscription(0, "com.system76.CosmicPanel.Dock".into(), 1).map(|update| {
|
||||
for why in update.errors {
|
||||
tracing::error!(?why, "dock config load error");
|
||||
}
|
||||
|
||||
file_chooser::Message::Init(sender) => {
|
||||
Message::FileChooser(FileChooser::Init(sender))
|
||||
}
|
||||
Message::PanelConfig(update.config)
|
||||
}),
|
||||
])
|
||||
}
|
||||
|
|
@ -381,50 +331,6 @@ impl cosmic::Application for SettingsApp {
|
|||
}
|
||||
},
|
||||
|
||||
Message::FileChooser(message) => match message {
|
||||
FileChooser::Selected(files) => {
|
||||
return self.pages.page[self.active_page]
|
||||
.file_chooser(files)
|
||||
.map(crate::app::Message::PageMessage)
|
||||
.map(cosmic::app::Message::App)
|
||||
}
|
||||
|
||||
FileChooser::Closed => {}
|
||||
|
||||
FileChooser::Opened => {
|
||||
if let Some((sender, _)) = self.file_chooser.as_mut() {
|
||||
return sender.response().map(|_| cosmic::app::Message::None);
|
||||
}
|
||||
}
|
||||
|
||||
FileChooser::Open {
|
||||
title,
|
||||
accept_label,
|
||||
include_directories,
|
||||
modal,
|
||||
multiple_files,
|
||||
} => {
|
||||
if let Some((sender, entity)) = self.file_chooser.as_mut() {
|
||||
if let Some(dialog) = file_chooser::open_file() {
|
||||
*entity = self.active_page;
|
||||
|
||||
return dialog
|
||||
.title(title)
|
||||
.accept_label(accept_label)
|
||||
.include_directories(include_directories)
|
||||
.modal(modal)
|
||||
.multiple_files(multiple_files)
|
||||
.create(sender)
|
||||
.map(|_| cosmic::app::message::none());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileChooser::Init(sender) => {
|
||||
self.file_chooser = Some((sender, page::Entity::default()));
|
||||
}
|
||||
},
|
||||
|
||||
Message::PanelConfig(config) if config.name.to_lowercase().contains("panel") => {
|
||||
page::update!(
|
||||
self.pages,
|
||||
|
|
@ -477,7 +383,7 @@ impl cosmic::Application for SettingsApp {
|
|||
}
|
||||
}
|
||||
|
||||
Message::PanelConfig(_) | Message::Search(_) => {}
|
||||
Message::PanelConfig(_) => {}
|
||||
|
||||
Message::SetTheme(t) => return cosmic::app::command::set_theme(t),
|
||||
|
||||
|
|
|
|||
|
|
@ -719,7 +719,9 @@ impl Page {
|
|||
let Ok(path) = f.to_file_path() else {
|
||||
return Command::none();
|
||||
};
|
||||
let Ok(builder) = ron::ser::to_string_pretty(&self.theme_builder, PrettyConfig::default()) else {
|
||||
let Ok(builder) =
|
||||
ron::ser::to_string_pretty(&self.theme_builder, PrettyConfig::default())
|
||||
else {
|
||||
return Command::none();
|
||||
};
|
||||
Command::perform(
|
||||
|
|
|
|||
|
|
@ -386,7 +386,7 @@ impl Page {
|
|||
/// View for the display configuration section.
|
||||
pub fn display_view(&self) -> Element<pages::Message> {
|
||||
let Some(&active_id) = self.display_tabs.active_data::<OutputKey>() else {
|
||||
return column().into()
|
||||
return column().into();
|
||||
};
|
||||
|
||||
let active_output = &self.list.outputs[active_id];
|
||||
|
|
@ -467,7 +467,7 @@ impl Page {
|
|||
|
||||
for (name, id) in sorted_outputs {
|
||||
let Some(output) = self.list.outputs.get(id) else {
|
||||
continue
|
||||
continue;
|
||||
};
|
||||
|
||||
let inches =
|
||||
|
|
|
|||
|
|
@ -351,7 +351,13 @@ impl Page {
|
|||
return Command::none();
|
||||
};
|
||||
let Some((list, _)) = config.plugins_wings.as_mut() else {
|
||||
config.plugins_wings = Some((start_list.into_iter().map(|a: Applet| a.id.into()).collect(), Vec::new()));
|
||||
config.plugins_wings = Some((
|
||||
start_list
|
||||
.into_iter()
|
||||
.map(|a: Applet| a.id.into())
|
||||
.collect(),
|
||||
Vec::new(),
|
||||
));
|
||||
return Command::none();
|
||||
};
|
||||
*list = start_list.into_iter().map(|a| a.id.into()).collect();
|
||||
|
|
@ -361,7 +367,12 @@ impl Page {
|
|||
return Command::none();
|
||||
};
|
||||
let Some(list) = config.plugins_center.as_mut() else {
|
||||
config.plugins_center = Some(center_list.into_iter().map(|a: Applet| a.id.into()).collect());
|
||||
config.plugins_center = Some(
|
||||
center_list
|
||||
.into_iter()
|
||||
.map(|a: Applet| a.id.into())
|
||||
.collect(),
|
||||
);
|
||||
return Command::none();
|
||||
};
|
||||
*list = center_list.into_iter().map(|a| a.id.into()).collect();
|
||||
|
|
@ -371,7 +382,10 @@ impl Page {
|
|||
return Command::none();
|
||||
};
|
||||
let Some((_, list)) = config.plugins_wings.as_mut() else {
|
||||
config.plugins_wings = Some((Vec::new(), end_list.into_iter().map(|a: Applet| a.id.into()).collect()));
|
||||
config.plugins_wings = Some((
|
||||
Vec::new(),
|
||||
end_list.into_iter().map(|a: Applet| a.id.into()).collect(),
|
||||
));
|
||||
return Command::none();
|
||||
};
|
||||
*list = end_list.into_iter().map(|a| a.id.into()).collect();
|
||||
|
|
@ -504,9 +518,7 @@ pub fn lists<
|
|||
Section::default().view::<P>(move |_binder, page, _section| {
|
||||
let page = page.inner();
|
||||
let Some(config) = page.current_config.as_ref() else {
|
||||
return Element::from(
|
||||
text(fl!("unknown"))
|
||||
);
|
||||
return Element::from(text(fl!("unknown")));
|
||||
};
|
||||
|
||||
let button = button::standard(fl!("add-applet"));
|
||||
|
|
|
|||
|
|
@ -350,7 +350,7 @@ impl PageInner {
|
|||
pub fn update(&mut self, message: Message) {
|
||||
let helper = self.config_helper.as_ref().unwrap();
|
||||
let Some(mut panel_config) = self.panel_config.as_mut() else {
|
||||
return
|
||||
return;
|
||||
};
|
||||
|
||||
match message {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ mod config;
|
|||
pub mod widgets;
|
||||
|
||||
pub use config::Config;
|
||||
use url::Url;
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
|
|
@ -13,12 +14,15 @@ use std::{
|
|||
};
|
||||
|
||||
use apply::Apply;
|
||||
use cosmic::widget::{
|
||||
button, dropdown, list_column, row,
|
||||
segmented_button::{self, SingleSelectModel},
|
||||
segmented_selection, settings, text, toggler,
|
||||
};
|
||||
use cosmic::{command, Command};
|
||||
use cosmic::{
|
||||
dialog::file_chooser,
|
||||
widget::{
|
||||
button, dropdown, list_column, row,
|
||||
segmented_button::{self, SingleSelectModel},
|
||||
settings, text, toggler, view_switcher,
|
||||
},
|
||||
};
|
||||
use cosmic::{
|
||||
iced::{wayland::actions::window::SctkWindowSettings, window, Color, Length},
|
||||
prelude::CollectionWidget,
|
||||
|
|
@ -45,8 +49,7 @@ use slotmap::{DefaultKey, SecondaryMap, SlotMap};
|
|||
use static_init::dynamic;
|
||||
|
||||
const FIT: usize = 0;
|
||||
const STRETCH: usize = 1;
|
||||
const ZOOM: usize = 2;
|
||||
const ZOOM: usize = 1;
|
||||
|
||||
const SIMULATED_WIDTH: u16 = 300;
|
||||
const SIMULATED_HEIGHT: u16 = 169;
|
||||
|
|
@ -63,6 +66,10 @@ pub type Image = ImageBuffer<Rgba<u8>, Vec<u8>>;
|
|||
/// Messages for the wallpaper view.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Message {
|
||||
/// Adds a new wallpaper folder.
|
||||
AddFolder(Arc<Result<Url, file_chooser::Error>>),
|
||||
/// Adds a new image file the system wallpaper folder.
|
||||
AddFile(Arc<Result<Url, file_chooser::Error>>),
|
||||
/// Selects an option in the category dropdown menu.
|
||||
ChangeCategory(Category),
|
||||
/// Changes the displayed images in the wallpaper view.
|
||||
|
|
@ -112,17 +119,6 @@ pub enum Category {
|
|||
Wallpapers,
|
||||
}
|
||||
|
||||
/// The status of active dialog requests.
|
||||
#[derive(Copy, Clone)]
|
||||
enum ActiveDialog {
|
||||
/// The active dialog is a folder dialog.
|
||||
AddFolder,
|
||||
/// The active dialog is an image dialog.
|
||||
AddImage,
|
||||
/// No request has been made for a dialog.
|
||||
None,
|
||||
}
|
||||
|
||||
/// The page struct for the wallpaper view.
|
||||
pub struct Page {
|
||||
/// The display that is currently being configured.
|
||||
|
|
@ -130,9 +126,6 @@ pub struct Page {
|
|||
/// If set to `None`, all displays will have the same wallpaper.
|
||||
active_output: Option<String>,
|
||||
|
||||
/// The state of an active dialog request.
|
||||
active_dialog: ActiveDialog,
|
||||
|
||||
/// Configuration parameters used by the cosmic-bg service.
|
||||
wallpaper_service_config: wallpaper::Config,
|
||||
|
||||
|
|
@ -187,67 +180,6 @@ impl page::Page<crate::pages::Message> for Page {
|
|||
.description(fl!("wallpaper", "desc"))
|
||||
}
|
||||
|
||||
fn file_chooser(&mut self, selections: Vec<url::Url>) -> Command<crate::pages::Message> {
|
||||
let active_dialog = self.active_dialog;
|
||||
self.active_dialog = ActiveDialog::None;
|
||||
|
||||
if let Some(selection) = selections.first() {
|
||||
let Ok(path) = selection.to_file_path() else {
|
||||
tracing::error!(path = selection.path(), "not a valid file path");
|
||||
return Command::none();
|
||||
};
|
||||
|
||||
match active_dialog {
|
||||
ActiveDialog::AddFolder => {
|
||||
if path.is_dir() {
|
||||
tracing::info!(?path, "opening new folder");
|
||||
|
||||
let _res = self.config.set_current_folder(Some(path.clone()));
|
||||
|
||||
// Add the selected folder to the recent folders list.
|
||||
self.add_recent_folder(path.clone());
|
||||
|
||||
// Select that folder in the recent folders list.
|
||||
for (id, recent) in self.config.recent_folders().iter().enumerate() {
|
||||
if &path == recent {
|
||||
self.categories.selected = Some(Category::RecentFolder(id));
|
||||
}
|
||||
}
|
||||
|
||||
// Load the wallpapers from the selected folder into the view.
|
||||
return cosmic::command::future(async move {
|
||||
crate::pages::Message::DesktopWallpaper(Message::ChangeFolder(
|
||||
change_folder(path).await,
|
||||
))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ActiveDialog::AddImage => {
|
||||
if path.is_file() {
|
||||
tracing::info!(?path, "opening custom image");
|
||||
|
||||
// Loads a single custom image and its thumbnail for display in the backgrounds view.
|
||||
return cosmic::command::future(async move {
|
||||
let result =
|
||||
wallpaper::load_image_with_thumbnail(&mut Vec::new(), path).await;
|
||||
|
||||
crate::pages::Message::DesktopWallpaper(Message::ImageAdd(
|
||||
result.map(Arc::new),
|
||||
))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ActiveDialog::None => {
|
||||
tracing::error!("not actively handling a dialog");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Command::none()
|
||||
}
|
||||
|
||||
fn reload(&mut self, _page: page::Entity) -> Command<crate::pages::Message> {
|
||||
let current_folder = self.config.current_folder().to_owned();
|
||||
|
||||
|
|
@ -270,7 +202,6 @@ impl page::AutoBind<crate::pages::Message> for Page {}
|
|||
impl Default for Page {
|
||||
fn default() -> Self {
|
||||
let mut page = Page {
|
||||
active_dialog: ActiveDialog::None,
|
||||
active_output: None,
|
||||
cached_display_handle: None,
|
||||
categories: {
|
||||
|
|
@ -304,7 +235,7 @@ impl Default for Page {
|
|||
color_dialog: window::Id::unique(),
|
||||
color_model: ColorPickerModel::new(fl!("hex"), fl!("rgb"), None, Some(Color::WHITE)),
|
||||
config: Config::new(),
|
||||
fit_options: vec![fl!("fit-to-screen"), fl!("stretch"), fl!("zoom")],
|
||||
fit_options: vec![fl!("fill"), fl!("fit-to-screen")],
|
||||
outputs: SingleSelectModel::default(),
|
||||
rotation_frequency: 300,
|
||||
rotation_options: vec![
|
||||
|
|
@ -380,18 +311,6 @@ impl Page {
|
|||
let temp_image;
|
||||
|
||||
let image = match self.selected_fit {
|
||||
FIT => image,
|
||||
|
||||
STRETCH => {
|
||||
temp_image = image::imageops::resize(
|
||||
image,
|
||||
SIMULATED_WIDTH as u32,
|
||||
SIMULATED_HEIGHT as u32,
|
||||
Lanczos3,
|
||||
);
|
||||
&temp_image
|
||||
}
|
||||
|
||||
ZOOM => {
|
||||
let (w, h) = (image.width(), image.height());
|
||||
|
||||
|
|
@ -417,6 +336,8 @@ impl Page {
|
|||
&temp_image
|
||||
}
|
||||
|
||||
FIT => image,
|
||||
|
||||
_ => return,
|
||||
};
|
||||
|
||||
|
|
@ -585,16 +506,19 @@ impl Page {
|
|||
}
|
||||
|
||||
Category::AddFolder => {
|
||||
self.active_dialog = ActiveDialog::AddFolder;
|
||||
return cosmic::command::message(crate::Message::FileChooser(
|
||||
crate::app::FileChooser::Open {
|
||||
title: fl!("wallpaper", "folder-dialog"),
|
||||
accept_label: fl!("dialog-add"),
|
||||
include_directories: true,
|
||||
modal: false,
|
||||
multiple_files: false,
|
||||
},
|
||||
));
|
||||
return cosmic::command::future(async {
|
||||
let dialog_result = file_chooser::open::Dialog::new()
|
||||
.title(fl!("wallpaper", "folder-dialog"))
|
||||
.accept_label(fl!("dialog-add"))
|
||||
.modal(false)
|
||||
.open_folder()
|
||||
.await
|
||||
.map(|response| response.url().to_owned());
|
||||
|
||||
let message = Message::AddFolder(Arc::new(dialog_result));
|
||||
let page_message = crate::pages::Message::DesktopWallpaper(message);
|
||||
crate::Message::PageMessage(page_message)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -628,9 +552,8 @@ impl Page {
|
|||
/// Updates configuration for wallpaper image.
|
||||
fn config_wallpaper_entry(&self, output: String, path: PathBuf) -> Option<Entry> {
|
||||
let scaling_mode = match self.selected_fit {
|
||||
FIT => ScalingMode::Fit([0.0, 0.0, 0.0]),
|
||||
STRETCH => ScalingMode::Stretch,
|
||||
ZOOM => ScalingMode::Zoom,
|
||||
FIT => ScalingMode::Fit([0.0, 0.0, 0.0]),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
|
|
@ -749,16 +672,19 @@ impl Page {
|
|||
}
|
||||
|
||||
Message::ImageAddDialog => {
|
||||
self.active_dialog = ActiveDialog::AddImage;
|
||||
return cosmic::command::message(crate::Message::FileChooser(
|
||||
crate::app::FileChooser::Open {
|
||||
title: fl!("wallpaper", "image-dialog"),
|
||||
accept_label: fl!("dialog-add"),
|
||||
include_directories: false,
|
||||
modal: false,
|
||||
multiple_files: false,
|
||||
},
|
||||
));
|
||||
return cosmic::command::future(async {
|
||||
let dialog_result = file_chooser::open::Dialog::new()
|
||||
.title(fl!("wallpaper", "image-dialog"))
|
||||
.accept_label(fl!("dialog-add"))
|
||||
.modal(false)
|
||||
.open_file()
|
||||
.await
|
||||
.map(|response| response.url().to_owned());
|
||||
|
||||
let message = Message::AddFile(Arc::new(dialog_result));
|
||||
let page_message = crate::pages::Message::DesktopWallpaper(message);
|
||||
crate::Message::PageMessage(page_message)
|
||||
});
|
||||
}
|
||||
|
||||
Message::ImageRemove(image) => {
|
||||
|
|
@ -806,6 +732,75 @@ impl Page {
|
|||
}
|
||||
}
|
||||
|
||||
Message::AddFolder(result) => {
|
||||
let selection = match Arc::into_inner(result) {
|
||||
Some(Ok(response)) => response,
|
||||
Some(Err(why)) => {
|
||||
// TODO:
|
||||
return Command::none();
|
||||
}
|
||||
None => return Command::none(),
|
||||
};
|
||||
|
||||
let Ok(path) = selection.to_file_path() else {
|
||||
tracing::error!(path = selection.path(), "not a valid file path");
|
||||
return Command::none();
|
||||
};
|
||||
|
||||
if path.is_dir() {
|
||||
tracing::info!(?path, "opening new folder");
|
||||
|
||||
let _res = self.config.set_current_folder(Some(path.clone()));
|
||||
|
||||
// Add the selected folder to the recent folders list.
|
||||
self.add_recent_folder(path.clone());
|
||||
|
||||
// Select that folder in the recent folders list.
|
||||
for (id, recent) in self.config.recent_folders().iter().enumerate() {
|
||||
if &path == recent {
|
||||
self.categories.selected = Some(Category::RecentFolder(id));
|
||||
}
|
||||
}
|
||||
|
||||
// Load the wallpapers from the selected folder into the view.
|
||||
return cosmic::command::future(async move {
|
||||
let message = Message::ChangeFolder(change_folder(path).await);
|
||||
let page_message = crate::pages::Message::DesktopWallpaper(message);
|
||||
crate::Message::PageMessage(page_message)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Message::AddFile(result) => {
|
||||
let selection = match Arc::into_inner(result) {
|
||||
Some(Ok(response)) => response,
|
||||
Some(Err(why)) => {
|
||||
// TODO:
|
||||
return Command::none();
|
||||
}
|
||||
None => return Command::none(),
|
||||
};
|
||||
|
||||
let Ok(path) = selection.to_file_path() else {
|
||||
tracing::error!(path = selection.path(), "not a valid file path");
|
||||
return Command::none();
|
||||
};
|
||||
|
||||
if path.is_file() {
|
||||
tracing::info!(?path, "opening custom image");
|
||||
|
||||
// Loads a single custom image and its thumbnail for display in the backgrounds view.
|
||||
return cosmic::command::future(async move {
|
||||
let result =
|
||||
wallpaper::load_image_with_thumbnail(&mut Vec::new(), path).await;
|
||||
|
||||
let message = Message::ImageAdd(result.map(Arc::new));
|
||||
let page_message = crate::pages::Message::DesktopWallpaper(message);
|
||||
crate::Message::PageMessage(page_message)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Message::Init(update) => {
|
||||
self.wallpaper_service_config_update(update.0, update.1, update.2);
|
||||
self.config_apply();
|
||||
|
|
@ -905,9 +900,8 @@ impl Page {
|
|||
};
|
||||
|
||||
match entry.scaling_mode {
|
||||
ScalingMode::Zoom | ScalingMode::Stretch => self.selected_fit = ZOOM,
|
||||
ScalingMode::Fit(_) => self.selected_fit = FIT,
|
||||
ScalingMode::Stretch => self.selected_fit = STRETCH,
|
||||
ScalingMode::Zoom => self.selected_fit = ZOOM,
|
||||
}
|
||||
|
||||
match entry.rotation_frequency {
|
||||
|
|
@ -1079,7 +1073,7 @@ pub fn settings() -> Section<crate::pages::Message> {
|
|||
.height(Length::Fixed(32.0))
|
||||
.into()
|
||||
} else {
|
||||
segmented_selection::horizontal(&page.outputs)
|
||||
view_switcher::horizontal(&page.outputs)
|
||||
.on_activate(Message::Output)
|
||||
.into()
|
||||
});
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ appearance = Vzhled
|
|||
notifications = Oznámení
|
||||
.desc = Nerušit, oznámení na zamykací obrazovce, a nastavení pro specifické aplikace.
|
||||
|
||||
|
||||
## Desktop: Options
|
||||
|
||||
desktop-panel-options = Plocha a Panel
|
||||
|
|
@ -111,8 +110,6 @@ wallpaper = Tapeta
|
|||
all-displays = Všechny monitory
|
||||
colors = Barvy
|
||||
fit-to-screen = Vyplnit obrazovku
|
||||
stretch = Roztáhnout
|
||||
zoom = Přiblížení
|
||||
|
||||
x-minutes = { $number } minut
|
||||
x-hours = { $number ->
|
||||
|
|
|
|||
|
|
@ -228,11 +228,10 @@ add-image = Add image
|
|||
all-displays = All Displays
|
||||
colors = Colors
|
||||
dialog-add = _Add
|
||||
fill = Fill
|
||||
fit-to-screen = Fit to Screen
|
||||
open-new-folder = Open new folder
|
||||
recent-folders = Recent Folders
|
||||
stretch = Stretch
|
||||
zoom = Zoom
|
||||
|
||||
x-minutes = { $number } minutes
|
||||
x-hours = { $number ->
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
derive_setters = "0.1.6"
|
||||
regex = "1.10.2"
|
||||
regex = "1.10.3"
|
||||
slotmap = "1.0.7"
|
||||
libcosmic = { workspace = true }
|
||||
generator = "0.7.5"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ edition = "2021"
|
|||
cosmic-bg-config = { workspace = true }
|
||||
cosmic-config = { workspace = true }
|
||||
dirs = "5.0.1"
|
||||
freedesktop-icons = "0.2.4"
|
||||
freedesktop-icons = "0.2.5"
|
||||
futures-lite = "2.2.0"
|
||||
image = "0.24.8"
|
||||
infer = "0.15.0"
|
||||
|
|
@ -17,4 +17,4 @@ rayon = "1.8.1"
|
|||
sctk = { workspace = true }
|
||||
tokio = { version = "1.35.1", features = ["sync"] }
|
||||
tracing = "0.1.40"
|
||||
wayland-client = "0.31.1"
|
||||
wayland-client = "0.31.2"
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ pub fn load_each_from_path(path: PathBuf) -> Receiver<(PathBuf, RgbaImage, RgbaI
|
|||
if let Ok(dir) = path.read_dir() {
|
||||
for entry in dir.filter_map(Result::ok) {
|
||||
let Ok(file_type) = entry.file_type() else {
|
||||
continue
|
||||
continue;
|
||||
};
|
||||
|
||||
let path = entry.path();
|
||||
|
|
@ -114,7 +114,7 @@ pub fn load_each_from_path(path: PathBuf) -> Receiver<(PathBuf, RgbaImage, RgbaI
|
|||
paths.push(path);
|
||||
} else if file_type.is_file() {
|
||||
let Ok(Some(kind)) = infer::get_from_path(&path) else {
|
||||
continue
|
||||
continue;
|
||||
};
|
||||
|
||||
if infer::MatcherType::Image == kind.matcher_type() {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ license = "GPL-3.0-only"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
byte-unit = "5.1.3"
|
||||
byte-unit = "5.1.4"
|
||||
const_format = "0.2.32"
|
||||
concat-in-place = "1.1.0"
|
||||
sysinfo = "0.30.5"
|
||||
|
|
|
|||
|
|
@ -17,12 +17,14 @@ impl Info {
|
|||
pub async fn load(proxy: &TimeDateProxy<'_>) -> Option<Info> {
|
||||
let can_ntp = proxy.can_ntp().await.unwrap_or_default();
|
||||
|
||||
let Ok(timezone) = proxy.timezone()
|
||||
let Ok(timezone) = proxy
|
||||
.timezone()
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.parse::<CustomTimeZone>() else {
|
||||
return None;
|
||||
};
|
||||
.parse::<CustomTimeZone>()
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let Ok(duration) = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) else {
|
||||
return None;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
[toolchain]
|
||||
channel = "1.70.0"
|
||||
channel = "1.75.0"
|
||||
components = ["clippy", "rustfmt"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue