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
14 changes: 14 additions & 0 deletions src-tauri/src/point_scan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,18 @@ pub enum Mode {
Line,
Grid,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum ControlMode {
#[default]
Point,
Mouse,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", default)]
pub struct Config {
pub control_mode: ControlMode,
#[serde(deserialize_with = "crate::scan_preferences::deserialize_preferences")]
pub scan_preferences: crate::scan_preferences::Preferences,
pub word_prediction: bool,
Expand All @@ -35,6 +43,7 @@ pub struct Config {
impl Default for Config {
fn default() -> Self {
Self {
control_mode: ControlMode::Point,
scan_preferences: Default::default(),
word_prediction: true,
keyboard_wait_after_typing: false,
Expand Down Expand Up @@ -91,6 +100,7 @@ impl Config {
}
pub fn point(&self) -> PointSettings {
PointSettings {
control_mode: self.control_mode,
scan: self.resolved(crate::scan_preferences::Area::Point),
menu_scan: self.resolved(crate::scan_preferences::Area::Menu),
keyboard_scan: self.resolved(crate::scan_preferences::Area::Keyboard),
Expand All @@ -115,6 +125,7 @@ impl Config {
}
#[derive(Clone)]
pub struct PointSettings {
pub control_mode: ControlMode,
pub scan: crate::scan_preferences::Resolved,
pub menu_scan: crate::scan_preferences::Resolved,
pub keyboard_scan: crate::scan_preferences::Resolved,
Expand Down Expand Up @@ -284,6 +295,7 @@ impl Engine {
| Action::Stop
| Action::Cancel
| Action::OpenKeyboard
| Action::OpenPoint
| Action::OpenMouse => {}
}
None
Expand Down Expand Up @@ -1006,6 +1018,7 @@ mod tests {
json["autoSelectEnabled"] = serde_json::json!(false);
json["autoSelectDelayMs"] = serde_json::json!(1000);
json["scannerColor"] = serde_json::json!("blue");
json["controlMode"] = serde_json::json!("point");
json["wordPrediction"] = serde_json::json!(true);
json["keyboardWaitAfterTyping"] = serde_json::json!(false);
json["scanPreferences"] =
Expand All @@ -1031,6 +1044,7 @@ mod tests {
fn legacy_empty_config_and_validation() {
let c: Config = serde_json::from_str("{}").unwrap();
assert_eq!(c, Config::default());
assert_eq!(c.control_mode, ControlMode::Point);
assert!(Config {
select_key: "Escape".into(),
..c.clone()
Expand Down
51 changes: 50 additions & 1 deletion src-tauri/src/point_scan_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ impl Adapter for PointScan {
type Environment = Environment;
const EVENT: &'static str = "point-scan-changed";
const FILE: &'static str = "point-scan.json";
fn sync_mode(config: &mut Config, technique: &Workflow) -> bool {
let next = technique.control_mode();
if config.control_mode == next {
return false;
}
config.control_mode = next;
true
}
fn keep_runtime_config(next: &mut Config, current: &Config) {
next.control_mode = current.control_mode;
}
fn cursor_feedback(technique: &Workflow) -> Option<crate::input::PointerFeedback> {
technique.mouse_feedback()
}
Expand Down Expand Up @@ -91,7 +102,7 @@ impl Adapter for PointScan {
crate::prediction::record(stroke, result.is_ok(), scope);
return result.map(|()| None);
}
Request::OpenKeyboard | Request::OpenMouse => {
Request::OpenKeyboard | Request::OpenMouse | Request::OpenPoint => {
crate::prediction::stop();
crate::scan_executor::activate(request)
}
Expand Down Expand Up @@ -282,6 +293,44 @@ fn validate_display(app: &AppHandle, display: Option<&Environment>) -> Result<()
#[cfg(test)]
mod tests {
use super::*;
use crate::scanning::Technique;

#[test]
fn saving_mode_change_keeps_other_settings_and_reloads() {
let mut config = Config {
speed: 4,
..Config::default()
};
let screen = Rect {
x: 0.0,
y: 0.0,
width: 1280.0,
height: 720.0,
};
let mut workflow = Workflow::new(config.point(), screen, 1.0).unwrap();
workflow.handle(crate::scanning::Action::OpenMouse);
assert!(PointScan::sync_mode(&mut config, &workflow));
assert!(!PointScan::sync_mode(&mut config, &workflow));
let restored: Config =
serde_json::from_slice(&serde_json::to_vec(&config).unwrap()).unwrap();
assert_eq!(restored.control_mode, crate::point_scan::ControlMode::Mouse);
assert_eq!(restored.speed, 4);
}

#[test]
fn settings_saves_keep_the_runtime_mode() {
let current = Config {
control_mode: crate::point_scan::ControlMode::Mouse,
..Config::default()
};
let mut next = Config {
speed: 5,
..Config::default()
};
PointScan::keep_runtime_config(&mut next, &current);
assert_eq!(next.control_mode, crate::point_scan::ControlMode::Mouse);
assert_eq!(next.speed, 5);
}

#[test]
fn typing_preserves_visuals_but_pointer_execution_hides_them() {
Expand Down
83 changes: 76 additions & 7 deletions src-tauri/src/point_workflow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub enum Request {
},
OpenKeyboard,
OpenMouse,
CloseMouse,
OpenPoint,
Keyboard(crate::scan_keyboard::Stroke),
MouseMove {
dx: i32,
Expand Down Expand Up @@ -207,6 +207,9 @@ impl Workflow {
pub fn mouse_open(&self) -> bool {
matches!(self.stage, Stage::Mouse | Stage::MouseMoving)
}
pub fn control_mode(&self) -> crate::point_scan::ControlMode {
self.point.config.control_mode
}
pub fn set_mouse_area(&mut self, area: Rect, screen: Rect, scale: f64, displays: usize) {
self.mouse_area = area;
self.point.screen = screen;
Expand Down Expand Up @@ -272,6 +275,7 @@ impl Workflow {
}
}
fn open_mouse(&mut self) -> Option<Request> {
self.point.config.control_mode = crate::point_scan::ControlMode::Mouse;
self.stage = Stage::Mouse;
self.move_repeat = None;
self.mouse = crate::scan_mouse::MousePanel::new(
Expand All @@ -286,6 +290,13 @@ impl Workflow {
self.return_to_mouse = false;
Some(Request::OpenMouse)
}
fn open_point(&mut self) -> Option<Request> {
self.point.config.control_mode = crate::point_scan::ControlMode::Point;
self.reset();
self.stage = Stage::Point;
self.point.start();
Some(Request::OpenPoint)
}
fn mouse_key(&mut self, key: crate::scan_mouse::Key) -> Option<Request> {
use crate::scan_mouse::Key;
self.mouse.choose(key);
Expand Down Expand Up @@ -345,10 +356,7 @@ impl Workflow {
.with_wait_after_typing(self.point.config.keyboard_wait_after_typing);
Some(Request::OpenKeyboard)
}
Key::Close => {
self.reset();
Some(Request::CloseMouse)
}
Key::Close => self.open_point(),
Key::More | Key::Movement | Key::Dock => None,
}
}
Expand Down Expand Up @@ -495,9 +503,14 @@ impl Technique for Workflow {
}
}
fn start(&mut self) {
let mode = self.point.config.control_mode;
self.reset();
self.stage = Stage::Point;
self.point.start();
if mode == crate::point_scan::ControlMode::Mouse {
let _ = self.open_mouse();
} else {
self.stage = Stage::Point;
self.point.start();
}
}
fn reset(&mut self) {
self.keyboard = crate::scan_keyboard::Keyboard::configured(
Expand Down Expand Up @@ -547,6 +560,9 @@ impl Technique for Workflow {
self.stage != Stage::Executing
}
fn handle(&mut self, action: Action) -> Option<Request> {
if action == Action::OpenPoint {
return self.open_point();
}
if action == Action::OpenMouse {
if self.mouse_open() {
return None;
Expand Down Expand Up @@ -828,6 +844,48 @@ mod tests {
use super::*;
use crate::{point_scan::Config, scanning::Session};

#[test]
fn select_resumes_saved_mode_and_switching_updates_it() {
let screen = Rect {
x: 0.0,
y: 0.0,
width: 1280.0,
height: 720.0,
};
let config = Config {
control_mode: crate::point_scan::ControlMode::Mouse,
..Config::default()
};
let mut session = Session::new(Workflow::new(config.point(), screen, 1.0).unwrap(), true);
assert_eq!(session.action(Action::Select), None);
assert_eq!(
session.technique.phase(),
Phase::Workflow(WorkflowPhase::Mouse)
);
assert_eq!(
session.technique.control_mode(),
crate::point_scan::ControlMode::Mouse
);
assert_eq!(session.action(Action::OpenPoint), Some(Request::OpenPoint));
assert_eq!(
session.technique.control_mode(),
crate::point_scan::ControlMode::Point
);
assert!(matches!(session.technique.phase(), Phase::Point(_)));
assert_eq!(session.action(Action::OpenMouse), Some(Request::OpenMouse));
assert_eq!(
session.technique.mouse_key(crate::scan_mouse::Key::Close),
Some(Request::OpenPoint)
);
assert_eq!(
session.technique.control_mode(),
crate::point_scan::ControlMode::Point
);
assert_eq!(session.action(Action::Stop), None);
assert_eq!(session.action(Action::Select), None);
assert!(matches!(session.technique.phase(), Phase::Point(_)));
}

#[test]
fn mouse_motion_stops_on_press_and_returns_to_first_row() {
let screen = Rect {
Expand Down Expand Up @@ -939,10 +997,21 @@ mod tests {
assert!(workflow.return_to_mouse);
workflow.keyboard_closed();
assert_eq!(workflow.phase(), Phase::Workflow(WorkflowPhase::Mouse));
assert_eq!(
workflow.control_mode(),
crate::point_scan::ControlMode::Mouse
);
assert_eq!(
workflow.mouse_feedback(),
Some(crate::input::PointerFeedback::Move)
);
workflow.reset();
assert_eq!(
workflow.control_mode(),
crate::point_scan::ControlMode::Mouse
);
workflow.start();
assert_eq!(workflow.phase(), Phase::Workflow(WorkflowPhase::Mouse));
}

#[test]
Expand Down
4 changes: 2 additions & 2 deletions src-tauri/src/scan_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub fn execute<I: InputInjector>(
}
match request {
Request::Prediction { .. } => Err("Prediction requires the scan controller.".into()),
Request::OpenKeyboard | Request::OpenMouse | Request::CloseMouse => input.release_all(),
Request::OpenKeyboard | Request::OpenMouse | Request::OpenPoint => input.release_all(),
Request::MouseMove { dx, dy } => input.move_pointer_pixels(dx, dy).map(|_| ()),
Request::MouseMoveAbsolute { x, y } => input.move_pointer_pixels_absolute(x, y).map(|_| ()),
Request::MouseClick { right, count } => {
Expand Down Expand Up @@ -436,7 +436,7 @@ mod tests {
.unwrap();
execute(&mut input, Request::MouseScroll { dy: 5 }, true).unwrap();
execute(&mut input, Request::DragStart((10, 20)), true).unwrap();
execute(&mut input, Request::CloseMouse, true).unwrap();
execute(&mut input, Request::OpenPoint, true).unwrap();
assert!(!input.has_active_drag());
assert!(input.injector.events.contains(&"relative 4 -2".into()));
assert!(input.injector.events.contains(&"move 40 50".into()));
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/src/scan_mouse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ impl MousePanel {
Movement => "Movement".into(),
Keyboard => "Keyboard".into(),
Dock => if self.top { "Dock bottom" } else { "Dock top" }.into(),
Close => "Close mouse".into(),
Close => "Switch to Point".into(),
}
}
pub fn frame(&self, screen: Rect, units: f64, color: ScannerColor) -> Frame {
Expand Down
7 changes: 6 additions & 1 deletion src-tauri/src/scanning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ pub enum Action {
Stop,
Cancel,
OpenKeyboard,
OpenPoint,
OpenMouse,
}
impl Action {
pub fn label(self) -> &'static str {
match self {
Self::OpenKeyboard => "Open keyboard",
Self::OpenPoint => "Open point",
Self::OpenMouse => "Open mouse",
Self::Select => "Select",
Self::Next => "Next",
Expand Down Expand Up @@ -386,7 +388,10 @@ impl<T: Technique> Session<T> {
self.reset();
return None;
}
if matches!(action, Action::OpenKeyboard | Action::OpenMouse) {
if matches!(
action,
Action::OpenKeyboard | Action::OpenPoint | Action::OpenMouse
) {
let selection = self.technique.handle(action);
if selection.is_some() {
self.active = true;
Expand Down
Loading
Loading