feat(input): handle key sequence
This commit is contained in:
parent
2670e7d1da
commit
9e1ed1b6d5
|
@ -75,7 +75,7 @@ Logout with
|
|||
|
||||
You can upload your changeset,
|
||||
|
||||
Pressing `c`
|
||||
Pressing the sequence `c c`
|
||||
|
||||
Your default text editor will appear, you can then preview the changes included in the changeset
|
||||
|
||||
|
@ -89,6 +89,10 @@ Your changeset will be then uploaded.
|
|||
|
||||
Depending on the result a sucess of failure message will appear at bottom center of your screen.
|
||||
|
||||
### Discard the changes
|
||||
|
||||
You can revert the changes using `c d`
|
||||
|
||||
## IPC with bobosm CLI
|
||||
|
||||
You can use bobosm CLI to connect to a existing graphical instance of bobosm.
|
||||
|
|
4
TODO.md
4
TODO.md
|
@ -18,7 +18,9 @@
|
|||
- [x] 2024-07-01: upload changeset
|
||||
- [x] 2024-07-01: add success or error messages buffer
|
||||
- [x] 2024-07-01: refacto with clippy
|
||||
- [ ] refacto renderer
|
||||
- [x] refacto renderer
|
||||
- [ ] compute the real zoom
|
||||
- [ ] hint key to select nodes
|
||||
- [ ] vim-like commands sequence
|
||||
- [ ] show every singular nodes
|
||||
- [ ] create node
|
||||
|
|
48
src/main.rs
48
src/main.rs
|
@ -18,14 +18,13 @@ use fully_pub::fully_pub;
|
|||
use layers::{load_osm_data, load_osm_zone, load_static_cities_layer, Layer, LayerKind};
|
||||
use osm_api::OSMApiClient;
|
||||
use raylib::prelude::*;
|
||||
use ui::{edit_tags, inputs::{collect_actions_from_inputs, UiAction}, messages::{UiMessage, UiMessageLevel}, renderer::render_app, selection::get_objects_in_area};
|
||||
use ui::{changeset::try_changeset, edit_tags, inputs::{collect_actions_from_inputs, UiAction}, messages::{UiMessage, UiMessageLevel}, renderer::render_app, selection::get_objects_in_area};
|
||||
use utils::deg2num;
|
||||
use xdg::BaseDirectories;
|
||||
use std::io::Read;
|
||||
use serde::Deserialize;
|
||||
use ureq::Agent;
|
||||
use rand::Rng;
|
||||
use crate::utils::Invertion;
|
||||
use crate::utils::ToSlice;
|
||||
|
||||
pub const USER_AGENT: &str = "BobOSM dev";
|
||||
|
@ -40,12 +39,20 @@ struct Camera {
|
|||
canvas_to_real_factor: f32
|
||||
}
|
||||
|
||||
#[fully_pub]
|
||||
#[derive(Debug)]
|
||||
struct InputBuffer {
|
||||
/// unhandled keys typed in a sequence so far
|
||||
key_sequence: Vec<KeyboardKey>,
|
||||
start_time: f64
|
||||
}
|
||||
|
||||
#[fully_pub]
|
||||
struct AppContext {
|
||||
/// snapshot of camera state
|
||||
camera: Camera,
|
||||
state: Arc<Mutex<AppState>>,
|
||||
layers: Arc<Mutex<Vec<Layer>>>
|
||||
layers: Arc<Mutex<Vec<Layer>>>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
|
@ -62,7 +69,8 @@ struct AppState {
|
|||
loading: bool,
|
||||
messages: Vec<UiMessage>,
|
||||
selected_elements_per_layer: Vec<Vec<i64>>,
|
||||
ui_mode: UiMode
|
||||
ui_mode: UiMode,
|
||||
input_buffer: InputBuffer
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -183,6 +191,10 @@ fn main() -> Result<()> {
|
|||
),
|
||||
];
|
||||
let app_state = Arc::new(Mutex::new(AppState {
|
||||
input_buffer: InputBuffer {
|
||||
start_time: 0.0,
|
||||
key_sequence: vec![]
|
||||
},
|
||||
loading: false,
|
||||
messages,
|
||||
selected_elements_per_layer: vec![],
|
||||
|
@ -229,8 +241,8 @@ fn main() -> Result<()> {
|
|||
let mut screen_def: (i32, i32) = (rl.get_screen_width(), rl.get_screen_height());
|
||||
|
||||
// let target_geo: Vector2 = Vector2::new(1.33, 49.175); // aubevoye
|
||||
let target_geo: Vector2 = Vector2::new(1.3355, 49.1761); // aubevoye boulangerie
|
||||
// let target_geo: Vector2 = Vector2::new(1.33, 49.159); // gaillon
|
||||
// let target_geo: Vector2 = Vector2::new(1.3355, 49.1761); // aubevoye boulangerie
|
||||
let target_geo: Vector2 = Vector2::new(1.33, 49.159); // gaillon
|
||||
// let target_geo: Vector2 = Vector2::new(4.86101, 45.74803); // lyon univ3 tabac
|
||||
|
||||
let target_zoom: f32 = 50.;
|
||||
|
@ -250,9 +262,8 @@ fn main() -> Result<()> {
|
|||
break;
|
||||
}
|
||||
if screen_def.0 != rl.get_screen_width() || screen_def.1 != rl.get_screen_height() {
|
||||
println!("Screen definition changed");
|
||||
screen_def = (rl.get_screen_width(), rl.get_screen_height());
|
||||
println!("New screen def: {:?}", screen_def);
|
||||
println!("Screen def changed. New def: {:?}", screen_def);
|
||||
|
||||
// compute new bounds
|
||||
camera.bounds = compute_bounds_from_center_zoom(screen_def, camera.center, target_zoom);
|
||||
|
@ -260,13 +271,11 @@ fn main() -> Result<()> {
|
|||
app_state_ref.messages.push(UiMessage::success("Screen def changed"));
|
||||
}
|
||||
|
||||
// compute mouse position
|
||||
let mouse_pos = rl.get_mouse_position();
|
||||
let top_left_bound = Vector2::new(camera.bounds.0.x, camera.bounds.1.y);
|
||||
let real_mouse_pos = top_left_bound +
|
||||
mouse_pos.invert_for_canvas().scale_by(camera.canvas_to_real_factor);
|
||||
|
||||
let actions = collect_actions_from_inputs(&mut rl, &camera, &app_state.lock().unwrap());
|
||||
let actions = collect_actions_from_inputs(&mut rl, AppContext {
|
||||
state: app_state.clone(),
|
||||
layers: layers.clone(),
|
||||
camera: camera.clone(),
|
||||
});
|
||||
// handle asked actions
|
||||
for action in actions {
|
||||
match action {
|
||||
|
@ -290,7 +299,6 @@ fn main() -> Result<()> {
|
|||
app_state_mut.messages.push(
|
||||
UiMessage::error(&format!("Zone too big to download"))
|
||||
);
|
||||
|
||||
} else {
|
||||
spawn_download_thread(
|
||||
app_state.clone(),
|
||||
|
@ -339,6 +347,10 @@ fn main() -> Result<()> {
|
|||
let res = edit_tags(&xdg_dirs, app_state.clone(), layers.clone());
|
||||
println!("{:?}", res);
|
||||
},
|
||||
UiAction::ChangesetPrepare => {
|
||||
println!("Changeset prepare");
|
||||
let _ = try_changeset(&xdg_dirs, layers.clone(), &osm_client);
|
||||
},
|
||||
_ => {
|
||||
println!("Warn: unhandled action");
|
||||
},
|
||||
|
@ -349,14 +361,14 @@ fn main() -> Result<()> {
|
|||
camera.center = camera.bounds.1.lerp(camera.bounds.0, 0.5);
|
||||
camera.real_to_canvas_factor = (rl.get_screen_width() as f32) / (camera.bounds.0.x - camera.bounds.1.x).abs();
|
||||
camera.canvas_to_real_factor = 1.0/camera.real_to_canvas_factor;
|
||||
|
||||
camera.zoom = 1.0/(camera.bounds.0.x - camera.bounds.1.x).abs();
|
||||
// TODO: remap to the real distance formula
|
||||
camera.map_zoom = 4.680_851*camera.zoom;
|
||||
|
||||
render_app(&mut rl, &thread, AppContext {
|
||||
state: app_state.clone(),
|
||||
layers: layers.clone(),
|
||||
camera: camera.clone()
|
||||
camera: camera.clone(),
|
||||
})
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
@ -2,10 +2,10 @@ use fully_pub::fully_pub;
|
|||
use raylib::{ffi::{KeyboardKey, MouseButton}, math::Vector2, RaylibHandle};
|
||||
use rstar::AABB;
|
||||
|
||||
use crate::{utils::{Invertion, ToAABB}, AppState, Camera, UiMode};
|
||||
use crate::{utils::{Invertion, ToAABB}, AppContext, AppState, Camera, UiMode};
|
||||
|
||||
/// possible ui actions
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
#[fully_pub]
|
||||
enum UiAction {
|
||||
AppExit,
|
||||
|
@ -32,6 +32,12 @@ enum UiAction {
|
|||
}
|
||||
}
|
||||
|
||||
const SEQUENCE_MAP: &'static [(&[KeyboardKey], &UiAction)] = &[
|
||||
(&[KeyboardKey::KEY_C, KeyboardKey::KEY_C], &UiAction::ChangesetPrepare),
|
||||
(&[KeyboardKey::KEY_N, KeyboardKey::KEY_N], &UiAction::CreateNode),
|
||||
(&[KeyboardKey::KEY_N, KeyboardKey::KEY_W], &UiAction::CreateWay)
|
||||
];
|
||||
|
||||
pub fn get_real_mouse_pos(
|
||||
rl: &RaylibHandle,
|
||||
camera: &Camera
|
||||
|
@ -45,18 +51,51 @@ pub fn get_real_mouse_pos(
|
|||
.scale_by(camera.canvas_to_real_factor)
|
||||
}
|
||||
|
||||
fn handle_key_sequence(
|
||||
rl: &mut RaylibHandle,
|
||||
app_state: &mut AppState
|
||||
) -> Vec<UiAction> {
|
||||
let first_keys: Vec<KeyboardKey> = SEQUENCE_MAP
|
||||
.iter()
|
||||
.map(|x| x.0.get(0).unwrap().clone())
|
||||
.collect();
|
||||
|
||||
// handle vim-like key sequence
|
||||
if let Some(key_pressed) = rl.get_key_pressed() {
|
||||
// println!("elapsed time {:?}", rl.get_time());
|
||||
if (app_state.input_buffer.start_time - rl.get_time()).abs() > 2. {
|
||||
app_state.input_buffer.key_sequence = vec![];
|
||||
}
|
||||
if app_state.input_buffer.key_sequence.is_empty() && !first_keys.contains(&key_pressed) {
|
||||
return vec![];
|
||||
}
|
||||
app_state.input_buffer.start_time = rl.get_time();
|
||||
app_state.input_buffer.key_sequence.push(key_pressed);
|
||||
println!("Sequence so far: {:?}", &app_state.input_buffer.key_sequence);
|
||||
for (sequence_keys, sequence_action) in SEQUENCE_MAP.iter() {
|
||||
if app_state.input_buffer.key_sequence == *sequence_keys {
|
||||
app_state.input_buffer.key_sequence = vec![];
|
||||
return vec![(**sequence_action).clone()];
|
||||
}
|
||||
}
|
||||
}
|
||||
return vec![];
|
||||
}
|
||||
|
||||
/// entry point to handle UI inputs
|
||||
pub fn collect_actions_from_inputs(
|
||||
rl: &mut RaylibHandle,
|
||||
camera: &Camera,
|
||||
app_state: &AppState
|
||||
context: AppContext
|
||||
) -> Vec<UiAction> {
|
||||
let mut app_state = context.state.lock().unwrap();
|
||||
let camera = context.camera;
|
||||
|
||||
let mut actions: Vec<UiAction> = vec![];
|
||||
let mut pan_f = 0.1;
|
||||
let mut zoom_f = 1.0;
|
||||
if rl.is_key_down(KeyboardKey::KEY_LEFT_SHIFT) {
|
||||
pan_f = 0.33;
|
||||
zoom_f = 3.0;
|
||||
zoom_f = 5.0;
|
||||
}
|
||||
if rl.is_key_down(KeyboardKey::KEY_LEFT_CONTROL) {
|
||||
pan_f = 0.01;
|
||||
|
@ -79,9 +118,6 @@ pub fn collect_actions_from_inputs(
|
|||
if rl.is_key_down(KeyboardKey::KEY_Q) {
|
||||
actions.push(UiAction::AppExit);
|
||||
}
|
||||
if rl.is_key_pressed(KeyboardKey::KEY_C) {
|
||||
actions.push(UiAction::ChangesetPrepare);
|
||||
}
|
||||
if rl.is_key_pressed(KeyboardKey::KEY_E) {
|
||||
actions.push(UiAction::ToggleEditMode);
|
||||
}
|
||||
|
@ -142,6 +178,8 @@ pub fn collect_actions_from_inputs(
|
|||
.invert_for_canvas();
|
||||
actions.push(UiAction::NavigatePan(mouse_move));
|
||||
}
|
||||
|
||||
actions.extend(handle_key_sequence(rl, &mut app_state));
|
||||
|
||||
actions
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue