Add touchpad settings
This commit is contained in:
parent
69b5c3148f
commit
cafe56d86c
4 changed files with 225 additions and 61 deletions
|
|
@ -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>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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(§ion.title)
|
settings::view_section(§ion.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)
|
||||||
|
|
|
||||||
142
app/src/pages/input/touchpad.rs
Normal file
142
app/src/pages/input/touchpad.rs
Normal 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 = §ion.descriptions;
|
||||||
|
|
||||||
|
let input = binder.page::<super::Page>().expect("input page not found");
|
||||||
|
|
||||||
|
settings::view_section(§ion.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 = §ion.descriptions;
|
||||||
|
|
||||||
|
let input = binder.page::<super::Page>().expect("input page not found");
|
||||||
|
|
||||||
|
settings::view_section(§ion.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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue