Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 53 additions & 15 deletions src/conf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,18 @@ const HYPR_OVERRIDES_PATH: &str = ".config/hypr/conf-overrides.conf";

const MONITOR_CONFIG_PREFIX: &str = "monitor=";
const KEYBOARD_LAYOUT_PREFIX: &str = "input:kb_layout=";
const MOUSE_SENSITIVITY_PREFIX: &str = "input:sensitivity=";
const MOUSE_FORCE_NO_ACCEL_PREFIX: &str = "input:force_no_accel=";

/// Trait for configuration lines that can be overridden in the config file
trait ConfigLine {
/// Get the prefix that identifies this type of config line
fn prefix(&self) -> &str;

/// Extract the identifier/key from a config line (e.g., monitor name, setting key)
/// Returns None if the line doesn't match this config type
fn extract_key(&self, line: &str) -> Option<String>;

/// Check if this line should replace an existing line with the same key
fn should_replace(&self) -> bool {
true
Expand All @@ -27,12 +29,15 @@ trait ConfigLine {

struct MonitorConfig;
struct KeyboardLayoutConfig;
// I dont like this but i am too stupid and tired to think
struct MouseSensitivityConfig;
struct MouseForceNoAccelConfig;

impl ConfigLine for MonitorConfig {
fn prefix(&self) -> &str {
MONITOR_CONFIG_PREFIX
}

fn extract_key(&self, line: &str) -> Option<String> {
let trimmed = line.trim();
if let Some(config) = trimmed.strip_prefix(self.prefix())
Expand All @@ -48,7 +53,7 @@ impl ConfigLine for KeyboardLayoutConfig {
fn prefix(&self) -> &str {
KEYBOARD_LAYOUT_PREFIX
}

fn extract_key(&self, line: &str) -> Option<String> {
if line.trim().starts_with(self.prefix()) {
// For keyboard layout, we use a constant key since there's only one
Expand All @@ -59,11 +64,43 @@ impl ConfigLine for KeyboardLayoutConfig {
}
}

impl ConfigLine for MouseSensitivityConfig {
fn prefix(&self) -> &str {
MOUSE_SENSITIVITY_PREFIX
}

fn extract_key(&self, line: &str) -> Option<String> {
if line.trim().starts_with(self.prefix()) {
// For mouse sensitivity, we use a constant key since there's only one
Some("sensitivity".to_string())
} else {
None
}
}
}

impl ConfigLine for MouseForceNoAccelConfig {
fn prefix(&self) -> &str {
MOUSE_FORCE_NO_ACCEL_PREFIX
}

fn extract_key(&self, line: &str) -> Option<String> {
if line.trim().starts_with(self.prefix()) {
// For mouse force_no_accel, we use a constant key since there's only one
Some("force_no_accel".to_string())
} else {
None
}
}
}

/// Registry of all known config line types
fn get_config_handlers() -> Vec<Box<dyn ConfigLine>> {
vec![
Box::new(MonitorConfig),
Box::new(KeyboardLayoutConfig),
Box::new(MouseSensitivityConfig),
Box::new(MouseForceNoAccelConfig),
]
}

Expand Down Expand Up @@ -156,7 +193,6 @@ pub fn write_override_line(line: &str) -> anyhow::Result<()> {
Ok(())
}


/// Generate a monitor override string for hyprland configuration.
/// Currently just generates the basic one as i am a europoor and only have 1 monitor to test with.
pub fn monitor_override(monitor_name: String, settings: MonitorMode) -> String {
Expand All @@ -180,16 +216,18 @@ pub fn locale_override(locale: HashSet<String>) -> String {
input_locale_setting
}

// Capitalise the locales so they are easier to read for now
// TODO: Find some form of default list of 2-letter locale codes with proper names
// e.g., "us" -> "US", "fi" -> "FI". Maybe hyprland config has a list
#[allow(dead_code)]
pub fn humanize_locale(locale: &str) -> String {
locale
.chars()
.enumerate()
.map(|(i, c)| if i == 0 { c.to_ascii_uppercase() } else { c })
.collect()
/// Generate a mouse sensitivity override string for hyprland configuration.
pub fn mouse_sensitivity_override(sensitivity: f32) -> String {
format!("{}{}", MOUSE_SENSITIVITY_PREFIX, sensitivity)
}

/// Generate a mouse force_no_accel override string for hyprland configuration.
pub fn mouse_force_no_accel_override(force_no_accel: bool) -> String {
format!(
"{}{}",
MOUSE_FORCE_NO_ACCEL_PREFIX,
if force_no_accel { 1 } else { 0 }
)
}

#[cfg(test)]
Expand Down
15 changes: 8 additions & 7 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
use gpui::App;
use gpui::*;
use gpui_component::scroll::ScrollbarAxis;
use gpui_component::*;

mod conf;
mod ui;
mod util;

use crate::ui::{keyboard_settings::KeyboardSettings, monitor_settings::MonitorSettings};
use crate::ui::{input_settings::InputSettings, monitor_settings::MonitorSettings};
use crate::util::monitor;

pub struct Hyprconfig {
monitor_settings: Vec<Entity<MonitorSettings>>,
keyboard_settings: Entity<KeyboardSettings>,
input_settings: Entity<InputSettings>,
}

impl Render for Hyprconfig {
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
div()
.v_flex()
v_flex()
.gap_4()
.size_full()
.scrollable(ScrollbarAxis::Vertical)
.bg(transparent_white())
.p_4()
.child(
Expand All @@ -33,7 +34,7 @@ impl Render for Hyprconfig {
.children(self.monitor_settings.iter().cloned())
.child(div().w_full().h_1().rounded_sm().bg(rgb(0xf6f6f6)))
.child(div().font_normal().child("Input"))
.child(self.keyboard_settings.clone())
.child(self.input_settings.clone())
}
}

Expand Down Expand Up @@ -80,11 +81,11 @@ fn main() {
.map(|monitor| cx.new(|cx| MonitorSettings::new(monitor, window, cx)))
.collect();

let keyboard_settings = cx.new(|cx| KeyboardSettings::new(window, cx));
let input_settings = cx.new(|cx| InputSettings::new(window, cx));

Hyprconfig {
monitor_settings,
keyboard_settings,
input_settings,
}
});
// Root component
Expand Down
32 changes: 32 additions & 0 deletions src/ui/input_settings.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use gpui::*;
use gpui_component::StyledExt;

use crate::ui::keyboard_settings::KeyboardSettings;
use crate::ui::mouse_settings::MouseSettings;

// Grouped input settings (keyboard now, mouse later)
pub struct InputSettings {
keyboard_settings: Entity<KeyboardSettings>,
mouse_settings: Entity<MouseSettings>,
}

impl InputSettings {
pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
let keyboard_settings = cx.new(|cx| KeyboardSettings::new(window, cx));
let mouse_settings = cx.new(|cx| MouseSettings::new(window, cx));
Self {
keyboard_settings,
mouse_settings,
}
}
}

impl Render for InputSettings {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
div()
.v_flex()
.gap_2()
.child(self.keyboard_settings.clone())
.child(self.mouse_settings.clone())
}
}
2 changes: 2 additions & 0 deletions src/ui/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
pub mod input_settings;
pub mod keyboard_settings;
pub mod monitor_settings;
pub mod mouse_settings;
162 changes: 162 additions & 0 deletions src/ui/mouse_settings.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
use gpui::*;
use gpui_component::StyledExt;
use gpui_component::button::Button;
use gpui_component::slider::{Slider, SliderEvent, SliderState};
use gpui_component::switch::Switch;

use crate::conf::{mouse_force_no_accel_override, mouse_sensitivity_override, write_override_line};
use crate::util::mouse::{get_accel_setting, get_current_sensitivity};

pub struct MouseSettings {
force_no_accel_checked: bool,
mouse_sensitivity_slider: Entity<SliderState>,
current_sensitivity: f32,
}

// helper to convert sensitivity to slider value and back
// i think gpui_component has a issue with the negative floats?
fn sensitivity_to_slider(sens: f32) -> f32 {
// Convert -1.0..1.0 to 0.0..100.0
(sens + 1.0) * 50.0
}

fn slider_to_sensitivity(slider: f32) -> f32 {
// Convert 0.0..100.0 to -1.0..1.0
(slider / 50.0) - 1.0
}

impl MouseSettings {
pub fn new(window: &mut Window, cx: &mut gpui::Context<Self>) -> Self {
let current_sens = get_current_sensitivity().unwrap_or(0.0);
let accel_setting = get_accel_setting().unwrap_or(false);

println!(
"DEBUG: Initializing slider with sensitivity: {}",
current_sens
);

let mouse_sensitivity_slider = cx.new(|_cx| {
SliderState::new().min(0.0).max(100.0).step(2.5) // 0.05 * 50
});

// Set value after creation
mouse_sensitivity_slider.update(cx, |state, cx| {
state.set_value(sensitivity_to_slider(current_sens), window, cx);
});

cx.subscribe(
&mouse_sensitivity_slider,
|this, _, event: &SliderEvent, cx| match event {
SliderEvent::Change(value) => {
let sens = slider_to_sensitivity(value.start());
println!(
"DEBUG: Slider changed to: {} (sensitivity: {})",
value.start(),
sens
);
this.current_sensitivity = sens;
cx.notify();
}
},
)
.detach();

Self {
mouse_sensitivity_slider,
current_sensitivity: current_sens,
force_no_accel_checked: accel_setting,
}
}
}

impl Render for MouseSettings {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let slider_raw = self.mouse_sensitivity_slider.read(cx).value().start();
let current_sens = slider_to_sensitivity(slider_raw);
let accel_setting = self.force_no_accel_checked;

div()
.v_flex()
.gap_2()
.p_4()
.border_1()
.border_color(rgb(0x404040))
.rounded_lg()
.child(
div()
.font_weight(FontWeight::BOLD)
.child("Mouse settings".to_string()),
)
.child(
div()
.v_flex()
.gap_1()
.child(
div()
.h_flex()
.gap_4()
.items_center()
.child(div().min_w(px(120.0)).child("Sensitivity:"))
.child(
div()
.text_size(px(14.0))
.text_color(rgb(0x808080))
.child(format!("{:.2}", current_sens)),
),
)
.child(
div()
.h_flex()
.gap_2()
.items_center()
.child(
div()
.min_w(px(120.0))
.text_size(px(12.0))
.text_color(rgb(0x808080))
.child("Slow"),
)
.child(Slider::new(&self.mouse_sensitivity_slider).w_full())
.child(
div()
.text_size(px(12.0))
.text_color(rgb(0x808080))
.child("Fast"),
),
),
)
.child(
div()
.h_flex()
.gap_4()
.items_center()
.child("Acceleration".to_string())
.child(
Switch::new("force-no-accel-switch")
.checked(self.force_no_accel_checked)
.on_click(cx.listener(|view, checked, _, cx| {
view.force_no_accel_checked = *checked;
cx.notify();
})),
),
)
.child(
div()
.h_flex()
.gap_4()
.items_center()
.child(div().min_w(px(120.0)))
.child(
Button::new("apply-mouse-settings")
.label("Apply mouse config")
.on_click(move |_, _, _cx| {
let sens = mouse_sensitivity_override(current_sens);
let force_no_accel = mouse_force_no_accel_override(accel_setting);

write_override_line(&sens).unwrap();
write_override_line(&force_no_accel).unwrap();
}),
),
)
}
}
1 change: 1 addition & 0 deletions src/util/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod keyboard;
pub mod monitor;
pub mod mouse;
Loading