Add touchpad settings

This commit is contained in:
Ian Douglas Scott 2023-09-06 14:44:50 -07:00
parent 69b5c3148f
commit cafe56d86c
4 changed files with 225 additions and 61 deletions

View file

@ -15,15 +15,16 @@ use tracing::error;
pub mod keyboard; pub mod keyboard;
mod mouse; mod mouse;
mod touchpad;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Message { pub enum Message {
SetAcceleration(bool), SetAcceleration(bool, bool),
SetNaturalScroll(bool), SetNaturalScroll(bool, bool),
SetScrollFactor(f64), SetScrollFactor(f64, bool),
SetDoubleClickSpeed(u32), SetDoubleClickSpeed(u32, bool),
SetMouseSpeed(f64), SetMouseSpeed(f64, bool),
PrimaryButtonSelected(cosmic::widget::segmented_button::Entity), PrimaryButtonSelected(cosmic::widget::segmented_button::Entity, bool),
// seperate close message, to make sure another isn't closed? // seperate close message, to make sure another isn't closed?
ExpandInputSourcePopover(Option<String>), ExpandInputSourcePopover(Option<String>),
OpenSpecialCharacterDialog(keyboard::SpecialKey), OpenSpecialCharacterDialog(keyboard::SpecialKey),
@ -40,6 +41,9 @@ pub struct Page {
// Mouse // Mouse
primary_button: cosmic::widget::segmented_button::SingleSelectModel, primary_button: cosmic::widget::segmented_button::SingleSelectModel,
// Touchpad
touchpad_primary_button: cosmic::widget::segmented_button::SingleSelectModel,
// Keyboard // Keyboard
expanded_source_popover: Option<String>, expanded_source_popover: Option<String>,
sources: Vec<keyboard::InputSource>, sources: Vec<keyboard::InputSource>,
@ -61,17 +65,25 @@ impl Default for Page {
fn default() -> Self { fn default() -> Self {
let config = cosmic_config::Config::new("com.system76.CosmicComp", 1).unwrap(); let config = cosmic_config::Config::new("com.system76.CosmicComp", 1).unwrap();
let input_default: InputConfig = get_config(&config, "input-default"); let input_default: InputConfig = get_config(&config, "input-default");
let input_touchpad = get_config(&config, "input-touchpad"); let input_touchpad: InputConfig = get_config(&config, "input-touchpad");
let xkb = get_config(&config, "xkb-config"); let xkb = get_config(&config, "xkb-config");
let mut primary_button = mouse::default_primary_button(); let mut primary_button = mouse::default_primary_button();
let idx = if input_default.left_handed.unwrap_or(false) { let idx = if input_default.left_handed.unwrap_or(false) {
0
} else {
1 1
} else {
0
}; };
primary_button.activate_position(idx); primary_button.activate_position(idx);
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);
Self { Self {
config, config,
input_default, input_default,
@ -80,6 +92,9 @@ impl Default for Page {
// Mouse // Mouse
primary_button, primary_button,
// Touchpad
touchpad_primary_button,
// Keyboard // Keyboard
expanded_source_popover: None, expanded_source_popover: None,
sources: keyboard::default_input_sources(), sources: keyboard::default_input_sources(),
@ -90,61 +105,56 @@ impl Default for Page {
} }
impl Page { impl Page {
// TODO fn update_input<F: Fn(&mut InputConfig)>(&mut self, touchpad: bool, f: F) {
let (name, input_config) = if touchpad {
("input-touchpad", &mut self.input_touchpad)
} else {
("input-default", &mut self.input_default)
};
f(input_config);
if let Err(err) = self.config.set(name, input_config) {
error!(?err, "Failed to set config '{}'", name);
}
}
pub fn update(&mut self, message: Message) -> iced::Command<app::Message> { pub fn update(&mut self, message: Message) -> iced::Command<app::Message> {
match message { match message {
Message::SetAcceleration(value) => { Message::SetAcceleration(value, touchpad) => {
let profile = if value { let profile = if value {
AccelProfile::Adaptive AccelProfile::Adaptive
} else { } else {
AccelProfile::Flat AccelProfile::Flat
}; };
self.input_default self.update_input(touchpad, |x| {
.acceleration x.acceleration.get_or_insert(Default::default()).profile = Some(profile);
.get_or_insert(Default::default()) });
.profile = Some(profile);
if let Err(err) = self.config.set("input-default", &self.input_default) {
error!(?err, "Failed to set config 'input-default'");
}
} }
Message::SetNaturalScroll(value) => { Message::SetNaturalScroll(value, touchpad) => self.update_input(touchpad, |x| {
self.input_default x.scroll_config
.scroll_config
.get_or_insert(Default::default()) .get_or_insert(Default::default())
.natural_scroll = Some(value); .natural_scroll = Some(value);
if let Err(err) = self.config.set("input-default", &self.input_default) { }),
error!(?err, "Failed to set config 'input-default'"); Message::SetScrollFactor(value, touchpad) => self.update_input(touchpad, |x| {
} x.scroll_config
}
Message::SetScrollFactor(value) => {
self.input_default
.scroll_config
.get_or_insert(Default::default()) .get_or_insert(Default::default())
.scroll_factor = Some(value); .scroll_factor = Some(value)
if let Err(err) = self.config.set("input-default", &self.input_default) { }),
error!(?err, "Failed to set config 'input-default'"); Message::SetDoubleClickSpeed(_value, _touchpad) => {
}
}
Message::SetDoubleClickSpeed(_value) => {
// TODO // TODO
} }
Message::SetMouseSpeed(value) => { Message::SetMouseSpeed(value, touchpad) => self.update_input(touchpad, |x| {
self.input_default x.acceleration.get_or_insert(Default::default()).speed = value
.acceleration }),
.get_or_insert(Default::default()) Message::PrimaryButtonSelected(entity, touchpad) => {
.speed = value; let select_model = if touchpad {
if let Err(err) = self.config.set("input-default", &self.input_default) { &mut self.touchpad_primary_button
error!(?err, "Failed to set config 'input-default'"); } else {
} &mut self.primary_button
} };
Message::PrimaryButtonSelected(entity) => { select_model.activate(entity);
self.primary_button.activate(entity); let left_entity = select_model.entity_at(1).unwrap();
let left_entity = self.primary_button.entity_at(1).unwrap(); let left_handed = select_model.active() == left_entity;
let left_handed = self.primary_button.active() == left_entity; self.update_input(touchpad, |x| x.left_handed = Some(left_handed));
self.input_default.left_handed = Some(left_handed);
if let Err(err) = self.config.set("input-default", &self.input_default) {
error!(?err, "Failed to set config 'input-default'");
}
} }
Message::ExpandInputSourcePopover(value) => { Message::ExpandInputSourcePopover(value) => {
self.expanded_source_popover = value; self.expanded_source_popover = value;
@ -204,6 +214,8 @@ impl page::Page<crate::pages::Message> for Page {
impl page::AutoBind<crate::pages::Message> for Page { impl page::AutoBind<crate::pages::Message> for Page {
fn sub_pages(page: page::Insert<crate::pages::Message>) -> page::Insert<crate::pages::Message> { fn sub_pages(page: page::Insert<crate::pages::Message>) -> page::Insert<crate::pages::Message> {
page.sub_page::<keyboard::Page>().sub_page::<mouse::Page>() page.sub_page::<keyboard::Page>()
.sub_page::<mouse::Page>()
.sub_page::<touchpad::Page>()
} }
} }

View file

@ -9,7 +9,6 @@ use slotmap::SlotMap;
use super::Message; use super::Message;
// XXX
pub fn default_primary_button() -> cosmic::widget::segmented_button::SingleSelectModel { pub fn default_primary_button() -> cosmic::widget::segmented_button::SingleSelectModel {
let mut model = cosmic::widget::segmented_button::SingleSelectModel::builder() let mut model = cosmic::widget::segmented_button::SingleSelectModel::builder()
.insert(|b| b.text(fl!("mouse", "primary-button-left"))) .insert(|b| b.text(fl!("mouse", "primary-button-left")))
@ -54,13 +53,11 @@ fn mouse() -> Section<crate::pages::Message> {
let input = binder.page::<super::Page>().expect("input page not found"); let input = binder.page::<super::Page>().expect("input page not found");
// TODO need something more custom
settings::view_section(&section.title) settings::view_section(&section.title)
// TODO
.add(settings::item( .add(settings::item(
&descriptions[0], &descriptions[0],
cosmic::widget::segmented_selection::horizontal(&input.primary_button) cosmic::widget::segmented_selection::horizontal(&input.primary_button)
.on_activate(Message::PrimaryButtonSelected), .on_activate(|x| Message::PrimaryButtonSelected(x, false)),
)) ))
.add( .add(
settings::item::builder(&descriptions[1]).control(widget::slider( settings::item::builder(&descriptions[1]).control(widget::slider(
@ -72,7 +69,7 @@ fn mouse() -> Section<crate::pages::Message> {
.map_or(0.0, |x| x.speed) .map_or(0.0, |x| x.speed)
+ 1.0) + 1.0)
* 50.0, * 50.0,
|value| Message::SetMouseSpeed((value / 50.0) - 1.0), |value| Message::SetMouseSpeed((value / 50.0) - 1.0, false),
)), )),
) )
.add( .add(
@ -84,13 +81,15 @@ fn mouse() -> Section<crate::pages::Message> {
.acceleration .acceleration
.as_ref() .as_ref()
.map_or(true, |x| x.profile == Some(AccelProfile::Adaptive)), .map_or(true, |x| x.profile == Some(AccelProfile::Adaptive)),
Message::SetAcceleration, |x| Message::SetAcceleration(x, false),
), ),
) )
.add( .add(
settings::item::builder(&descriptions[4]) settings::item::builder(&descriptions[4])
.description(&descriptions[5]) .description(&descriptions[5])
.control(widget::slider(0..=100, 0, Message::SetDoubleClickSpeed)), .control(widget::slider(0..=100, 0, |x| {
Message::SetDoubleClickSpeed(x, false)
})),
) )
.apply(Element::from) .apply(Element::from)
.map(crate::pages::Message::Input) .map(crate::pages::Message::Input)
@ -126,7 +125,7 @@ fn scrolling() -> Section<crate::pages::Message> {
.log(2.) .log(2.)
* 10.0 * 10.0
+ 50.0, + 50.0,
|value| Message::SetScrollFactor(2f64.powf((value - 50.0) / 10.0)), |value| Message::SetScrollFactor(2f64.powf((value - 50.0) / 10.0), false),
), ),
)) ))
.add( .add(
@ -139,7 +138,7 @@ fn scrolling() -> Section<crate::pages::Message> {
.as_ref() .as_ref()
.and_then(|x| x.natural_scroll) .and_then(|x| x.natural_scroll)
.unwrap_or(false), .unwrap_or(false),
Message::SetNaturalScroll, |x| Message::SetNaturalScroll(x, false),
), ),
) )
.apply(Element::from) .apply(Element::from)

View file

@ -0,0 +1,142 @@
use apply::Apply;
use cosmic::iced::widget;
use cosmic::widget::settings;
use cosmic::Element;
use cosmic_comp_config::input::AccelProfile;
use cosmic_settings_page::Section;
use cosmic_settings_page::{self as page, section};
use slotmap::SlotMap;
use super::Message;
#[derive(Default)]
pub struct Page;
impl page::Page<crate::pages::Message> for Page {
fn content(
&self,
sections: &mut SlotMap<section::Entity, Section<crate::pages::Message>>,
) -> Option<page::Content> {
Some(vec![
sections.insert(touchpad()),
sections.insert(scrolling()),
])
}
fn info(&self) -> page::Info {
page::Info::new("touchpad", "input-touchpad-symbolic")
.title(fl!("touchpad"))
.description(fl!("touchpad", "desc"))
}
}
impl page::AutoBind<crate::pages::Message> for Page {}
fn touchpad() -> Section<crate::pages::Message> {
Section::default()
.descriptions(vec![
fl!("touchpad", "primary-button"),
fl!("touchpad", "speed"),
fl!("touchpad", "acceleration"),
fl!("touchpad", "acceleration-desc"),
fl!("touchpad", "double-click-speed"),
fl!("touchpad", "double-click-speed-desc"),
])
.view::<Page>(|binder, _page, section| {
let descriptions = &section.descriptions;
let input = binder.page::<super::Page>().expect("input page not found");
settings::view_section(&section.title)
.add(settings::item(
&descriptions[0],
cosmic::widget::segmented_selection::horizontal(&input.touchpad_primary_button)
.on_activate(|x| Message::PrimaryButtonSelected(x, true)),
))
.add(
settings::item::builder(&descriptions[1]).control(widget::slider(
0.0..=100.0,
(input
.input_touchpad
.acceleration
.as_ref()
.map_or(0.0, |x| x.speed)
+ 1.0)
* 50.0,
|value| Message::SetMouseSpeed((value / 50.0) - 1.0, true),
)),
)
.add(
settings::item::builder(&descriptions[2])
.description(&descriptions[3])
.toggler(
input
.input_touchpad
.acceleration
.as_ref()
.map_or(true, |x| x.profile == Some(AccelProfile::Adaptive)),
|x| Message::SetAcceleration(x, true),
),
)
// TODO disable while typing
.add(
settings::item::builder(&descriptions[4])
.description(&descriptions[5])
.control(widget::slider(0..=100, 0, |x| {
Message::SetDoubleClickSpeed(x, true)
})),
)
.apply(Element::from)
.map(crate::pages::Message::Input)
})
}
fn scrolling() -> Section<crate::pages::Message> {
Section::default()
.title(fl!("mouse-scrolling"))
.descriptions(vec![
fl!("mouse-scrolling", "speed"),
fl!("mouse-scrolling", "natural"),
fl!("mouse-scrolling", "natural-desc"),
])
.view::<Page>(|binder, _page, section| {
let descriptions = &section.descriptions;
let input = binder.page::<super::Page>().expect("input page not found");
settings::view_section(&section.title)
.add(settings::item(
&descriptions[0],
// TODO show numeric value
// TODO desired range?
widget::slider(
1.0..=100.0,
input
.input_touchpad
.scroll_config
.as_ref()
.and_then(|x| x.scroll_factor)
.unwrap_or(1.)
.log(2.)
* 10.0
+ 50.0,
|value| Message::SetScrollFactor(2f64.powf((value - 50.0) / 10.0), true),
),
))
.add(
settings::item::builder(&descriptions[1])
.description(&descriptions[2])
.toggler(
input
.input_touchpad
.scroll_config
.as_ref()
.and_then(|x| x.natural_scroll)
.unwrap_or(false),
|x| Message::SetNaturalScroll(x, true),
),
)
.apply(Element::from)
.map(crate::pages::Message::Input)
})
}

View file

@ -267,3 +267,14 @@ mouse-scrolling = Scrolling
.natural-desc = Scroll the content, instead of the view .natural-desc = Scroll the content, instead of the view
## Input: Touchpad ## Input: Touchpad
touchpad = Touchpad
.desc = Touchpad speed, click options, gestures.
.primary-button = Primary button
.primary-button-left = Left
.primary-button-right = Right
.speed = Touchpad speed
.acceleration = Enable touchpad acceleration
.acceleration-desc = Automatically adjusts tracking sensitivty based on speed.
.double-click-speed = Double-click speed
.double-click-speed-desc = Changes how fast double-clicks have to be to register.