2023-08-01 16:41:24 -07:00
|
|
|
use crate::app;
|
|
|
|
|
use cosmic::{
|
2023-08-09 15:31:30 -07:00
|
|
|
cosmic_config::{self, ConfigGet, ConfigSet},
|
2023-08-01 16:41:24 -07:00
|
|
|
iced::{self, wayland::actions::window::SctkWindowSettings, window},
|
|
|
|
|
iced_sctk::commands,
|
|
|
|
|
iced_widget::core::layout,
|
|
|
|
|
};
|
2023-09-01 15:17:47 -07:00
|
|
|
use cosmic_comp_config::{
|
|
|
|
|
input::{AccelProfile, InputConfig},
|
|
|
|
|
XkbConfig,
|
|
|
|
|
};
|
2023-04-28 15:45:29 -07:00
|
|
|
use cosmic_settings_page as page;
|
2023-08-09 15:31:30 -07:00
|
|
|
use itertools::Itertools;
|
|
|
|
|
use tracing::error;
|
2023-04-28 15:45:29 -07:00
|
|
|
|
|
|
|
|
pub mod keyboard;
|
|
|
|
|
mod mouse;
|
2023-09-06 14:44:50 -07:00
|
|
|
mod touchpad;
|
2023-04-28 15:45:29 -07:00
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
pub enum Message {
|
2023-09-06 14:44:50 -07:00
|
|
|
SetAcceleration(bool, bool),
|
|
|
|
|
SetNaturalScroll(bool, bool),
|
|
|
|
|
SetScrollFactor(f64, bool),
|
|
|
|
|
SetDoubleClickSpeed(u32, bool),
|
|
|
|
|
SetMouseSpeed(f64, bool),
|
|
|
|
|
PrimaryButtonSelected(cosmic::widget::segmented_button::Entity, bool),
|
2023-04-28 15:45:29 -07:00
|
|
|
// seperate close message, to make sure another isn't closed?
|
|
|
|
|
ExpandInputSourcePopover(Option<String>),
|
2023-08-01 16:41:24 -07:00
|
|
|
OpenSpecialCharacterDialog(keyboard::SpecialKey),
|
|
|
|
|
CloseSpecialCharacterDialog,
|
|
|
|
|
SpecialCharacterSelect(Option<&'static str>),
|
2023-04-28 15:45:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct Page {
|
2023-08-09 15:31:30 -07:00
|
|
|
config: cosmic_config::Config,
|
2023-09-01 15:17:47 -07:00
|
|
|
input_default: InputConfig,
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
|
input_touchpad: InputConfig,
|
2023-08-01 16:41:24 -07:00
|
|
|
|
2023-04-28 15:45:29 -07:00
|
|
|
// Mouse
|
|
|
|
|
primary_button: cosmic::widget::segmented_button::SingleSelectModel,
|
|
|
|
|
|
2023-09-06 14:44:50 -07:00
|
|
|
// Touchpad
|
|
|
|
|
touchpad_primary_button: cosmic::widget::segmented_button::SingleSelectModel,
|
|
|
|
|
|
2023-04-28 15:45:29 -07:00
|
|
|
// Keyboard
|
|
|
|
|
expanded_source_popover: Option<String>,
|
|
|
|
|
sources: Vec<keyboard::InputSource>,
|
2023-08-01 16:41:24 -07:00
|
|
|
special_character_dialog: Option<keyboard::SpecialKey>,
|
2023-08-09 15:31:30 -07:00
|
|
|
xkb: XkbConfig,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_config<T: Default + serde::de::DeserializeOwned>(
|
|
|
|
|
config: &cosmic_config::Config,
|
|
|
|
|
key: &str,
|
|
|
|
|
) -> T {
|
|
|
|
|
config.get(key).unwrap_or_else(|err| {
|
|
|
|
|
error!(?err, "Failed to read config '{}'", key);
|
|
|
|
|
T::default()
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for Page {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
let config = cosmic_config::Config::new("com.system76.CosmicComp", 1).unwrap();
|
2023-12-20 17:00:34 -08:00
|
|
|
let input_default: InputConfig = get_config(&config, "input_default");
|
|
|
|
|
let input_touchpad: InputConfig = get_config(&config, "input_touchpad");
|
|
|
|
|
let xkb = get_config(&config, "xkb_config");
|
2023-08-09 15:31:30 -07:00
|
|
|
|
2023-09-01 15:17:47 -07:00
|
|
|
let mut primary_button = mouse::default_primary_button();
|
|
|
|
|
let idx = if input_default.left_handed.unwrap_or(false) {
|
|
|
|
|
1
|
2023-09-06 14:44:50 -07:00
|
|
|
} else {
|
|
|
|
|
0
|
2023-09-01 15:17:47 -07:00
|
|
|
};
|
|
|
|
|
primary_button.activate_position(idx);
|
|
|
|
|
|
2023-09-06 14:44:50 -07:00
|
|
|
let mut touchpad_primary_button = mouse::default_primary_button();
|
|
|
|
|
let idx = if input_touchpad.left_handed.unwrap_or(false) {
|
|
|
|
|
1
|
|
|
|
|
} else {
|
|
|
|
|
0
|
|
|
|
|
};
|
|
|
|
|
touchpad_primary_button.activate_position(idx);
|
|
|
|
|
|
2023-08-09 15:31:30 -07:00
|
|
|
Self {
|
|
|
|
|
config,
|
2023-09-01 15:17:47 -07:00
|
|
|
input_default,
|
|
|
|
|
input_touchpad,
|
2023-08-09 15:31:30 -07:00
|
|
|
|
|
|
|
|
// Mouse
|
2023-09-01 15:17:47 -07:00
|
|
|
primary_button,
|
2023-08-09 15:31:30 -07:00
|
|
|
|
2023-09-06 14:44:50 -07:00
|
|
|
// Touchpad
|
|
|
|
|
touchpad_primary_button,
|
|
|
|
|
|
2023-08-09 15:31:30 -07:00
|
|
|
// Keyboard
|
|
|
|
|
expanded_source_popover: None,
|
|
|
|
|
sources: keyboard::default_input_sources(),
|
|
|
|
|
special_character_dialog: None,
|
|
|
|
|
xkb,
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-04-28 15:45:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Page {
|
2023-09-06 14:44:50 -07:00
|
|
|
fn update_input<F: Fn(&mut InputConfig)>(&mut self, touchpad: bool, f: F) {
|
|
|
|
|
let (name, input_config) = if touchpad {
|
2023-12-20 17:00:34 -08:00
|
|
|
("input_touchpad", &mut self.input_touchpad)
|
2023-09-06 14:44:50 -07:00
|
|
|
} else {
|
2023-12-20 17:00:34 -08:00
|
|
|
("input_default", &mut self.input_default)
|
2023-09-06 14:44:50 -07:00
|
|
|
};
|
|
|
|
|
f(input_config);
|
|
|
|
|
if let Err(err) = self.config.set(name, input_config) {
|
|
|
|
|
error!(?err, "Failed to set config '{}'", name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-01 16:41:24 -07:00
|
|
|
pub fn update(&mut self, message: Message) -> iced::Command<app::Message> {
|
2023-04-28 15:45:29 -07:00
|
|
|
match message {
|
2023-09-06 14:44:50 -07:00
|
|
|
Message::SetAcceleration(value, touchpad) => {
|
2023-09-01 15:17:47 -07:00
|
|
|
let profile = if value {
|
|
|
|
|
AccelProfile::Adaptive
|
|
|
|
|
} else {
|
|
|
|
|
AccelProfile::Flat
|
|
|
|
|
};
|
2023-09-06 14:44:50 -07:00
|
|
|
self.update_input(touchpad, |x| {
|
|
|
|
|
x.acceleration.get_or_insert(Default::default()).profile = Some(profile);
|
|
|
|
|
});
|
2023-04-28 15:45:29 -07:00
|
|
|
}
|
2023-09-06 14:44:50 -07:00
|
|
|
Message::SetNaturalScroll(value, touchpad) => self.update_input(touchpad, |x| {
|
|
|
|
|
x.scroll_config
|
2023-09-01 15:17:47 -07:00
|
|
|
.get_or_insert(Default::default())
|
|
|
|
|
.natural_scroll = Some(value);
|
2023-09-06 14:44:50 -07:00
|
|
|
}),
|
|
|
|
|
Message::SetScrollFactor(value, touchpad) => self.update_input(touchpad, |x| {
|
|
|
|
|
x.scroll_config
|
2023-09-01 15:17:47 -07:00
|
|
|
.get_or_insert(Default::default())
|
2023-09-06 14:44:50 -07:00
|
|
|
.scroll_factor = Some(value)
|
|
|
|
|
}),
|
|
|
|
|
Message::SetDoubleClickSpeed(_value, _touchpad) => {
|
2023-09-01 15:17:47 -07:00
|
|
|
// TODO
|
2023-04-28 15:45:29 -07:00
|
|
|
}
|
2023-09-06 14:44:50 -07:00
|
|
|
Message::SetMouseSpeed(value, touchpad) => self.update_input(touchpad, |x| {
|
|
|
|
|
x.acceleration.get_or_insert(Default::default()).speed = value
|
|
|
|
|
}),
|
|
|
|
|
Message::PrimaryButtonSelected(entity, touchpad) => {
|
|
|
|
|
let select_model = if touchpad {
|
|
|
|
|
&mut self.touchpad_primary_button
|
|
|
|
|
} else {
|
|
|
|
|
&mut self.primary_button
|
|
|
|
|
};
|
|
|
|
|
select_model.activate(entity);
|
|
|
|
|
let left_entity = select_model.entity_at(1).unwrap();
|
|
|
|
|
let left_handed = select_model.active() == left_entity;
|
|
|
|
|
self.update_input(touchpad, |x| x.left_handed = Some(left_handed));
|
2023-04-28 15:45:29 -07:00
|
|
|
}
|
|
|
|
|
Message::ExpandInputSourcePopover(value) => {
|
|
|
|
|
self.expanded_source_popover = value;
|
|
|
|
|
}
|
2023-08-01 16:41:24 -07:00
|
|
|
Message::OpenSpecialCharacterDialog(special_key) => {
|
|
|
|
|
self.special_character_dialog = Some(special_key);
|
|
|
|
|
let window_settings = SctkWindowSettings {
|
2023-12-11 14:46:22 -05:00
|
|
|
window_id: *keyboard::SPECIAL_CHARACTER_DIALOGUE_ID,
|
2023-08-01 16:41:24 -07:00
|
|
|
app_id: Some("com.system76.CosmicSettings".to_string()),
|
|
|
|
|
title: Some(special_key.title()),
|
2023-12-11 14:46:22 -05:00
|
|
|
parent: Some(window::Id::MAIN),
|
2023-08-01 16:41:24 -07:00
|
|
|
autosize: false,
|
|
|
|
|
size_limits: layout::Limits::NONE
|
|
|
|
|
.min_width(300.0)
|
|
|
|
|
.max_width(800.0)
|
|
|
|
|
.min_height(200.0)
|
|
|
|
|
.max_height(1080.0),
|
|
|
|
|
size: (512, 420),
|
|
|
|
|
resizable: None,
|
|
|
|
|
client_decorations: true,
|
|
|
|
|
transparent: true,
|
2023-11-16 15:39:10 -05:00
|
|
|
..Default::default()
|
2023-08-01 16:41:24 -07:00
|
|
|
};
|
|
|
|
|
return commands::window::get_window(window_settings);
|
|
|
|
|
}
|
|
|
|
|
Message::CloseSpecialCharacterDialog => {
|
|
|
|
|
self.special_character_dialog = None;
|
2023-12-11 14:46:22 -05:00
|
|
|
return commands::window::close_window(*keyboard::SPECIAL_CHARACTER_DIALOGUE_ID);
|
2023-08-01 16:41:24 -07:00
|
|
|
}
|
|
|
|
|
Message::SpecialCharacterSelect(id) => {
|
|
|
|
|
if let Some(special_key) = self.special_character_dialog {
|
2023-08-09 15:31:30 -07:00
|
|
|
let options = self.xkb.options.as_deref().unwrap_or("");
|
2023-08-01 16:41:24 -07:00
|
|
|
let prefix = special_key.prefix();
|
2023-08-09 15:31:30 -07:00
|
|
|
let new_options = options
|
|
|
|
|
.split(',')
|
|
|
|
|
.filter(|x| !x.starts_with(prefix))
|
|
|
|
|
.chain(id.into_iter())
|
|
|
|
|
.join(",");
|
|
|
|
|
self.xkb.options = Some(new_options).filter(|x| !x.is_empty());
|
2023-12-20 17:00:34 -08:00
|
|
|
if let Err(err) = self.config.set("xkb_config", &self.xkb) {
|
|
|
|
|
error!(?err, "Failed to set config 'xkb_config'");
|
2023-08-01 16:41:24 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-04-28 15:45:29 -07:00
|
|
|
}
|
2023-08-01 16:41:24 -07:00
|
|
|
iced::Command::none()
|
2023-04-28 15:45:29 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl page::Page<crate::pages::Message> for Page {
|
|
|
|
|
fn info(&self) -> page::Info {
|
|
|
|
|
// XXX icon?
|
|
|
|
|
page::Info::new("input", "input-keyboard-symbolic")
|
|
|
|
|
.title(fl!("input"))
|
|
|
|
|
.description(fl!("input", "desc"))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl page::AutoBind<crate::pages::Message> for Page {
|
|
|
|
|
fn sub_pages(page: page::Insert<crate::pages::Message>) -> page::Insert<crate::pages::Message> {
|
2023-09-06 14:44:50 -07:00
|
|
|
page.sub_page::<keyboard::Page>()
|
|
|
|
|
.sub_page::<mouse::Page>()
|
|
|
|
|
.sub_page::<touchpad::Page>()
|
2023-04-28 15:45:29 -07:00
|
|
|
}
|
|
|
|
|
}
|