Merge tag 'epoch-1.7.0' into local/yoda-main-1.3.0
Epoch 1.7.0 # Conflicts: # Cargo.lock
This commit is contained in:
commit
29ed971e64
38 changed files with 989 additions and 651 deletions
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "cosmic-settings"
|
||||
version = "1.5.0"
|
||||
version = "1.7.0"
|
||||
edition = "2024"
|
||||
license = "GPL-3.0-only"
|
||||
publish = false
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use cosmic::iced::stream;
|
|||
use cosmic::theme::CosmicTheme;
|
||||
use cosmic::widget::{dropdown, settings, text};
|
||||
use cosmic::{Apply, Element, Task, surface};
|
||||
use cosmic_comp_config::CosmicCompConfig;
|
||||
pub use cosmic_comp_config::ZoomMovement;
|
||||
use cosmic_config::CosmicConfigEntry;
|
||||
use cosmic_settings_a11y_manager_subscription as cosmic_a11y_manager;
|
||||
|
|
@ -36,11 +37,21 @@ pub struct Page {
|
|||
daemon_helper: cosmic_config::Config,
|
||||
dbus_sender: Option<UnboundedSender<a11y_bus::Request>>,
|
||||
reader_enabled: bool,
|
||||
comp_config: CosmicCompConfig,
|
||||
comp_helper: cosmic_config::Config,
|
||||
}
|
||||
|
||||
impl Default for Page {
|
||||
fn default() -> Self {
|
||||
let daemon_helper = CosmicSettingsDaemonConfig::config().unwrap();
|
||||
let comp_helper = cosmic_config::Config::new("com.system76.CosmicComp", 1).unwrap();
|
||||
let comp_config =
|
||||
CosmicCompConfig::get_entry(&comp_helper).unwrap_or_else(|(errs, config)| {
|
||||
for err in errs {
|
||||
tracing::warn!(?err, "error while reading cosmic-comp config");
|
||||
}
|
||||
config
|
||||
});
|
||||
Page {
|
||||
dbus_sender: None,
|
||||
entity: page::Entity::default(),
|
||||
|
|
@ -65,6 +76,8 @@ impl Default for Page {
|
|||
.unwrap_or_default(),
|
||||
daemon_helper,
|
||||
reader_enabled: false,
|
||||
comp_config,
|
||||
comp_helper,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -77,6 +90,7 @@ pub enum Message {
|
|||
ProtocolUnavailable,
|
||||
Return,
|
||||
ScreenReaderEnabled(bool),
|
||||
SetCursorShakeToFind(bool),
|
||||
SetScreenFilterActive(bool),
|
||||
SetScreenFilterSelection(ColorFilter),
|
||||
SetScreenInverted(bool),
|
||||
|
|
@ -187,6 +201,8 @@ pub fn vision() -> section::Section<crate::pages::Message> {
|
|||
invert_colors = fl!("accessibility", "invert-colors");
|
||||
color_filters = fl!("accessibility", "color-filters");
|
||||
color_filter_type = fl!("color-filter");
|
||||
shake_cursor = fl!("accessibility", "shake-cursor");
|
||||
shake_cursor_desc = fl!("accessibility", "shake-cursor-desc");
|
||||
});
|
||||
|
||||
Section::default()
|
||||
|
|
@ -276,6 +292,13 @@ pub fn vision() -> section::Section<crate::pages::Message> {
|
|||
|
||||
settings::item::builder(&descriptions[color_filter_type]).control(dropdown)
|
||||
})
|
||||
.add(
|
||||
settings::item::builder(&descriptions[shake_cursor])
|
||||
.description(&descriptions[shake_cursor_desc])
|
||||
.toggler(page.comp_config.cursor_shake_to_find, |enabled| {
|
||||
Message::SetCursorShakeToFind(enabled).into()
|
||||
}),
|
||||
)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
|
|
@ -416,6 +439,14 @@ impl Page {
|
|||
Message::Surface(a) => {
|
||||
return cosmic::task::message(crate::app::Message::Surface(a));
|
||||
}
|
||||
Message::SetCursorShakeToFind(enabled) => {
|
||||
if let Err(err) = self
|
||||
.comp_config
|
||||
.set_cursor_shake_to_find(&self.comp_helper, enabled)
|
||||
{
|
||||
tracing::error!(?err, "failed to set cursor_shake_to_find");
|
||||
}
|
||||
}
|
||||
Message::SetSoundMono(active) => {
|
||||
if let Err(err) = self
|
||||
.daemon_config
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use futures::channel::oneshot;
|
|||
use futures::{SinkExt, StreamExt};
|
||||
use slotmap::SlotMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, LazyLock};
|
||||
use std::time::Duration;
|
||||
use zbus::zvariant::OwnedObjectPath;
|
||||
|
||||
|
|
@ -20,6 +20,8 @@ use zbus::zvariant::OwnedObjectPath;
|
|||
use crate::service_manager::MockServiceManager;
|
||||
use crate::service_manager::ServiceManagerHandle;
|
||||
|
||||
static BLUETOOTH_PAGE_LABEL: LazyLock<String> = LazyLock::new(|| fl!("bluetooth"));
|
||||
|
||||
enum Dialog {
|
||||
RequestConfirmation {
|
||||
device: String,
|
||||
|
|
@ -173,6 +175,19 @@ impl page::Page<crate::pages::Message> for Page {
|
|||
.description(fl!("xdg-entry-bluetooth-comment"))
|
||||
}
|
||||
|
||||
fn header(&self) -> Option<Element<'_, crate::pages::Message>> {
|
||||
if self.model.adapters.len() > 1 {
|
||||
let (_, adapter) = self.model.get_selected_adapter()?;
|
||||
return Some(crate::widget::sub_page_header(
|
||||
&adapter.alias,
|
||||
BLUETOOTH_PAGE_LABEL.as_str(),
|
||||
Message::SelectAdapter(None).into(),
|
||||
));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn content(
|
||||
&self,
|
||||
sections: &mut SlotMap<section::Entity, Section<crate::pages::Message>>,
|
||||
|
|
@ -392,6 +407,30 @@ impl From<Event> for Message {
|
|||
}
|
||||
|
||||
impl Page {
|
||||
fn update_heading(&mut self) {
|
||||
self.heading = if let Some((_, adapter)) = self.model.get_selected_adapter() {
|
||||
fl!(
|
||||
"bluetooth",
|
||||
"status",
|
||||
aliases = format!("“{}”", adapter.alias)
|
||||
)
|
||||
} else {
|
||||
fl!(
|
||||
"bluetooth",
|
||||
"status",
|
||||
aliases = self
|
||||
.model
|
||||
.adapters
|
||||
.values()
|
||||
.map(|adapter| format!("“{}”", adapter.alias))
|
||||
.collect::<HashSet<String>>()
|
||||
.into_iter()
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
pub fn update(&mut self, message: Message) -> cosmic::Task<crate::Message> {
|
||||
let span = tracing::span!(tracing::Level::INFO, "bluetooth::update");
|
||||
let _span = span.enter();
|
||||
|
|
@ -437,28 +476,7 @@ impl Page {
|
|||
|
||||
Event::SetAdapters(adapters) => {
|
||||
let select_adapter = self.model.set_adapters(adapters);
|
||||
|
||||
if let Some((_, adapter)) = self.model.get_selected_adapter() {
|
||||
self.heading = fl!(
|
||||
"bluetooth",
|
||||
"status",
|
||||
aliases = format!("“{}”", adapter.alias)
|
||||
);
|
||||
} else {
|
||||
self.heading = fl!(
|
||||
"bluetooth",
|
||||
"status",
|
||||
aliases = self
|
||||
.model
|
||||
.adapters
|
||||
.values()
|
||||
.map(|adapter| format!("“{}”", adapter.alias))
|
||||
.collect::<HashSet<String>>()
|
||||
.into_iter()
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
);
|
||||
}
|
||||
self.update_heading();
|
||||
|
||||
if let Some(adapter) = select_adapter {
|
||||
return cosmic::task::message(Message::SelectAdapter(Some(adapter)));
|
||||
|
|
@ -741,6 +759,7 @@ impl Page {
|
|||
Message::SelectAdapter(adapter_maybe) => {
|
||||
tracing::debug!("Adapter selected: {adapter_maybe:?}");
|
||||
self.model.selected_adapter = adapter_maybe;
|
||||
self.update_heading();
|
||||
self.model.update_status();
|
||||
let Some(connection) = self.connection.as_ref() else {
|
||||
tracing::error!("No DBus connection ready");
|
||||
|
|
@ -1158,6 +1177,51 @@ impl Page {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use cosmic_settings_page::Page as _;
|
||||
|
||||
fn adapter(path: &str, alias: &str) -> (OwnedObjectPath, Adapter) {
|
||||
(
|
||||
OwnedObjectPath::try_from(path).unwrap(),
|
||||
Adapter {
|
||||
alias: alias.to_owned(),
|
||||
..Adapter::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn page_with_two_adapters() -> Page {
|
||||
let mut page = Page::default();
|
||||
let adapters = [
|
||||
adapter("/org/bluez/hci0", "Adapter 0"),
|
||||
adapter("/org/bluez/hci1", "Adapter 1"),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
let _task = page.update(Message::BluetoothEvent(Event::SetAdapters(adapters)));
|
||||
page
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bluetooth_header_is_absent_at_adapter_chooser() {
|
||||
let page = page_with_two_adapters();
|
||||
|
||||
assert!(page.header().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bluetooth_header_tracks_adapter_selection_and_clearing() {
|
||||
let mut page = page_with_two_adapters();
|
||||
let selected = OwnedObjectPath::try_from("/org/bluez/hci1").unwrap();
|
||||
|
||||
let _task = page.update(Message::SelectAdapter(Some(selected)));
|
||||
assert!(page.header().is_some());
|
||||
assert!(page.heading.contains("Adapter 1"));
|
||||
|
||||
let _task = page.update(Message::SelectAdapter(None));
|
||||
assert!(page.header().is_none());
|
||||
assert!(page.heading.contains("Adapter 0"));
|
||||
assert!(page.heading.contains("Adapter 1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dbus_service_unknown_with_installed_service_queries_manager() {
|
||||
|
|
|
|||
|
|
@ -1103,6 +1103,12 @@ impl Page {
|
|||
.arg(itoa::Buffer::new().format(x))
|
||||
.arg("--pos-y")
|
||||
.arg(itoa::Buffer::new().format(y))
|
||||
.arg("--refresh")
|
||||
.arg(format!(
|
||||
"{}.{:03}",
|
||||
current.refresh_rate / 1000,
|
||||
current.refresh_rate % 1000
|
||||
))
|
||||
.arg(name)
|
||||
.arg(itoa::Buffer::new().format(current.size.0))
|
||||
.arg(itoa::Buffer::new().format(current.size.1));
|
||||
|
|
|
|||
|
|
@ -118,8 +118,8 @@ pub enum SourceContext {
|
|||
MoveDown(DefaultKey),
|
||||
MoveUp(DefaultKey),
|
||||
Remove(DefaultKey),
|
||||
Settings(DefaultKey),
|
||||
ViewLayout(DefaultKey),
|
||||
// Settings(DefaultKey),
|
||||
// ViewLayout(DefaultKey),
|
||||
}
|
||||
|
||||
pub type Locale = String;
|
||||
|
|
@ -211,6 +211,7 @@ fn popover_menu_row(
|
|||
id: DefaultKey,
|
||||
label: String,
|
||||
message: impl Fn(DefaultKey) -> SourceContext + 'static,
|
||||
disabled: bool,
|
||||
) -> cosmic::Element<'static, Message> {
|
||||
let spacing = theme::spacing();
|
||||
widget::text::body(label)
|
||||
|
|
@ -219,37 +220,54 @@ fn popover_menu_row(
|
|||
.padding([spacing.space_xxxs, spacing.space_xs])
|
||||
.width(Length::Fill)
|
||||
.class(theme::Button::MenuItem)
|
||||
.on_press(Message::SourceContext(message(id)))
|
||||
.on_press_maybe(if disabled {
|
||||
None
|
||||
} else {
|
||||
Some(Message::SourceContext(message(id)))
|
||||
})
|
||||
.apply(Element::from)
|
||||
}
|
||||
|
||||
fn popover_menu(id: DefaultKey) -> cosmic::Element<'static, Message> {
|
||||
fn popover_menu(
|
||||
id: DefaultKey,
|
||||
is_first: bool,
|
||||
is_last: bool,
|
||||
) -> cosmic::Element<'static, Message> {
|
||||
widget::column::with_children([
|
||||
popover_menu_row(
|
||||
id,
|
||||
fl!("keyboard-sources", "move-up"),
|
||||
SourceContext::MoveUp,
|
||||
is_first,
|
||||
),
|
||||
popover_menu_row(
|
||||
id,
|
||||
fl!("keyboard-sources", "move-down"),
|
||||
SourceContext::MoveDown,
|
||||
is_last,
|
||||
),
|
||||
widget::divider::horizontal::default()
|
||||
.apply(widget::container)
|
||||
.padding(8)
|
||||
.into(),
|
||||
// popover_menu_row(
|
||||
// id,
|
||||
// fl!("keyboard-sources", "settings"),
|
||||
// SourceContext::Settings,
|
||||
// false,
|
||||
// ),
|
||||
// popover_menu_row(
|
||||
// id,
|
||||
// fl!("keyboard-sources", "view-layout"),
|
||||
// SourceContext::ViewLayout,
|
||||
// false,
|
||||
// ),
|
||||
popover_menu_row(
|
||||
id,
|
||||
fl!("keyboard-sources", "settings"),
|
||||
SourceContext::Settings,
|
||||
fl!("keyboard-sources", "remove"),
|
||||
SourceContext::Remove,
|
||||
false,
|
||||
),
|
||||
popover_menu_row(
|
||||
id,
|
||||
fl!("keyboard-sources", "view-layout"),
|
||||
SourceContext::ViewLayout,
|
||||
),
|
||||
popover_menu_row(id, fl!("keyboard-sources", "remove"), SourceContext::Remove),
|
||||
])
|
||||
.width(Length::Fixed(200.0))
|
||||
.apply(widget::container)
|
||||
|
|
@ -258,7 +276,12 @@ fn popover_menu(id: DefaultKey) -> cosmic::Element<'static, Message> {
|
|||
.into()
|
||||
}
|
||||
|
||||
fn popover_button(id: DefaultKey, expanded: bool) -> cosmic::Element<'static, Message> {
|
||||
fn popover_button(
|
||||
id: DefaultKey,
|
||||
expanded: bool,
|
||||
is_first: bool,
|
||||
is_last: bool,
|
||||
) -> cosmic::Element<'static, Message> {
|
||||
let on_press = Message::ExpandInputSourcePopover(if expanded { None } else { Some(id) });
|
||||
|
||||
let button = button::icon(icon::from_name("view-more-symbolic"))
|
||||
|
|
@ -268,7 +291,7 @@ fn popover_button(id: DefaultKey, expanded: bool) -> cosmic::Element<'static, Me
|
|||
if expanded {
|
||||
widget::popover(button)
|
||||
.position(widget::popover::Position::Bottom)
|
||||
.popup(popover_menu(id))
|
||||
.popup(popover_menu(id, is_first, is_last))
|
||||
.on_close(Message::ExpandInputSourcePopover(None))
|
||||
.into()
|
||||
} else {
|
||||
|
|
@ -280,10 +303,12 @@ fn input_source(
|
|||
id: DefaultKey,
|
||||
description: &str,
|
||||
expanded_source_popover: Option<DefaultKey>,
|
||||
is_first: bool,
|
||||
is_last: bool,
|
||||
) -> cosmic::Element<'_, Message> {
|
||||
let expanded = expanded_source_popover.is_some_and(|expanded_id| expanded_id == id);
|
||||
|
||||
settings::item(description, popover_button(id, expanded)).into()
|
||||
settings::item(description, popover_button(id, expanded, is_first, is_last)).into()
|
||||
}
|
||||
|
||||
fn special_char_radio_row<'a>(
|
||||
|
|
@ -521,15 +546,13 @@ impl Page {
|
|||
let _removed = self.active_layouts.remove(pos);
|
||||
self.update_xkb_config();
|
||||
}
|
||||
}
|
||||
} // SourceContext::Settings(_id) => {
|
||||
// eprintln!("settings not implemented");
|
||||
// }
|
||||
|
||||
SourceContext::Settings(_id) => {
|
||||
eprintln!("settings not implemented");
|
||||
}
|
||||
|
||||
SourceContext::ViewLayout(_id) => {
|
||||
eprintln!("view layout not implemented");
|
||||
}
|
||||
// SourceContext::ViewLayout(_id) => {
|
||||
// eprintln!("view layout not implemented");
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -747,12 +770,17 @@ fn input_sources() -> Section<crate::pages::Message> {
|
|||
// TODO Need something more custom, with drag and drop
|
||||
let mut section = settings::section().title(§ion.title);
|
||||
|
||||
for id in &page.active_layouts {
|
||||
for (position, id) in page.active_layouts.iter().enumerate() {
|
||||
if let Some((_locale, _variant, description, _source)) =
|
||||
page.keyboard_layouts.get(*id)
|
||||
{
|
||||
section =
|
||||
section.add(input_source(*id, description, page.expanded_source_popover));
|
||||
section = section.add(input_source(
|
||||
*id,
|
||||
description,
|
||||
page.expanded_source_popover,
|
||||
position == 0,
|
||||
position + 1 == page.active_layouts.len(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue