mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-23 13:38:12 +08:00
Add 'Zoom with Scroll' input navigation scheme to preferences (#1021)
* Add use_scroll_as_zoom field to preference handler
* Add {Create,Delete}Mapping variants to message
* Revert "Add {Create,Delete}Mapping variants to message"
This reverts commit 0ba74754c9fb0c78d0b590c96e1d4fe2cfdd13e7.
* Revert "Add use_scroll_as_zoom field to preference handler"
This reverts commit d30f7c9edfa6d6e156939ca07f4db81f288975fd.
* Add basic scroll_as_zoom mapping
* Create overengineered mapping patch abstraction
* Add (for now passthrough) input layout manager
* Actually handle ModifyLayout messages (untested)
* Add backend preferences <-> layout manager comms
* Add scroll-as-zoom to actual preferences UI
* Rename LayoutManager -> KeyMapping
* Add Input section to preferences and title case
* Add scrollAsZoom frontend handling code (untested)
* Handle frontend <-> preferences comms
* (broken) Move scrollAsZoom persistence into state
* Fix scrollAsZoom having no effect on node graph
* Remove debugging helpers
* Fix confusion between horizontal and vertical
* Rename feature
* Move new message handler into folder
---------
Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
committed by
Keavon Chambers
co-authored by
Keavon Chambers
parent
c2234ce3fe
commit
7a52e50a94
@@ -1,4 +1,5 @@
|
||||
use crate::consts::{BIG_NUDGE_AMOUNT, NUDGE_AMOUNT};
|
||||
use crate::messages::input_mapper::key_mapping::MappingVariant;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeyStates};
|
||||
use crate::messages::input_mapper::utility_types::macros::*;
|
||||
use crate::messages::input_mapper::utility_types::misc::MappingEntry;
|
||||
@@ -8,6 +9,15 @@ use crate::messages::prelude::*;
|
||||
|
||||
use glam::DVec2;
|
||||
|
||||
impl From<MappingVariant> for Mapping {
|
||||
fn from(value: MappingVariant) -> Self {
|
||||
match value {
|
||||
MappingVariant::Default => default_mapping(),
|
||||
MappingVariant::ZoomWithScroll => zoom_with_scroll(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_mapping() -> Mapping {
|
||||
use InputMapperMessage::*;
|
||||
use Key::*;
|
||||
@@ -337,3 +347,40 @@ pub fn default_mapping() -> Mapping {
|
||||
pointer_move,
|
||||
}
|
||||
}
|
||||
|
||||
/// Defaults except that scrolling without modifiers is bound to zooming instead of vertical panning
|
||||
pub fn zoom_with_scroll() -> Mapping {
|
||||
// TODO(multisn8): for other keymaps this patterns might be useful
|
||||
use InputMapperMessage::*;
|
||||
|
||||
let mut mapping = default_mapping();
|
||||
|
||||
let remove = [
|
||||
entry!(WheelScroll; modifiers=[Control], action_dispatch=NavigationMessage::WheelCanvasZoom),
|
||||
entry!(WheelScroll; modifiers=[Shift], action_dispatch=NavigationMessage::WheelCanvasTranslate { use_y_as_x: true }),
|
||||
entry!(WheelScroll; action_dispatch=NavigationMessage::WheelCanvasTranslate { use_y_as_x: false }),
|
||||
];
|
||||
let add = [
|
||||
entry!(WheelScroll; modifiers=[Control], action_dispatch=NavigationMessage::WheelCanvasTranslate { use_y_as_x: true }),
|
||||
entry!(WheelScroll; modifiers=[Shift], action_dispatch=NavigationMessage::WheelCanvasTranslate { use_y_as_x: false }),
|
||||
entry!(WheelScroll; action_dispatch=NavigationMessage::WheelCanvasZoom),
|
||||
];
|
||||
|
||||
apply_mapping_patch(&mut mapping, remove, add);
|
||||
|
||||
mapping
|
||||
}
|
||||
|
||||
fn apply_mapping_patch<'a, const N: usize, const M: usize, const X: usize, const Y: usize>(
|
||||
mapping: &mut Mapping,
|
||||
remove: impl IntoIterator<Item = &'a [&'a [MappingEntry; N]; M]>,
|
||||
add: impl IntoIterator<Item = &'a [&'a [MappingEntry; X]; Y]>,
|
||||
) {
|
||||
for entry in remove.into_iter().flat_map(|inner| inner.iter()).flat_map(|inner| inner.iter()) {
|
||||
mapping.remove(entry);
|
||||
}
|
||||
|
||||
for entry in add.into_iter().flat_map(|inner| inner.iter()).flat_map(|inner| inner.iter()) {
|
||||
mapping.add(entry.clone());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::messages::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[remain::sorted]
|
||||
#[impl_message(Message, InputMapper)]
|
||||
#[impl_message(Message, KeyMappingMessage, Lookup)]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize)]
|
||||
pub enum InputMapperMessage {
|
||||
// Sub-messages
|
||||
|
||||
@@ -20,6 +20,10 @@ impl MessageHandler<InputMapperMessage, (&InputPreprocessorMessageHandler, Actio
|
||||
}
|
||||
|
||||
impl InputMapperMessageHandler {
|
||||
pub fn set_mapping(&mut self, mapping: Mapping) {
|
||||
self.mapping = mapping;
|
||||
}
|
||||
|
||||
pub fn hints(&self, actions: ActionList) -> String {
|
||||
let mut output = String::new();
|
||||
let mut actions = actions
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[remain::sorted]
|
||||
#[impl_message(Message, KeyMapping)]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize)]
|
||||
pub enum KeyMappingMessage {
|
||||
#[child]
|
||||
Lookup(InputMapperMessage),
|
||||
#[child]
|
||||
ModifyMapping(MappingVariant),
|
||||
}
|
||||
|
||||
#[remain::sorted]
|
||||
#[impl_message(Message, KeyMappingMessage, ModifyMapping)]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Default, Hash, Serialize, Deserialize)]
|
||||
pub enum MappingVariant {
|
||||
#[remain::unsorted]
|
||||
#[default]
|
||||
Default,
|
||||
|
||||
ZoomWithScroll,
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct KeyMappingMessageHandler {
|
||||
mapping_handler: InputMapperMessageHandler,
|
||||
}
|
||||
|
||||
impl MessageHandler<KeyMappingMessage, (&InputPreprocessorMessageHandler, ActionList)> for KeyMappingMessageHandler {
|
||||
fn process_message(&mut self, message: KeyMappingMessage, responses: &mut VecDeque<Message>, data: (&InputPreprocessorMessageHandler, ActionList)) {
|
||||
match message {
|
||||
KeyMappingMessage::Lookup(input) => self.mapping_handler.process_message(input, responses, data),
|
||||
KeyMappingMessage::ModifyMapping(new_layout) => self.mapping_handler.set_mapping(new_layout.into()),
|
||||
}
|
||||
}
|
||||
advertise_actions!();
|
||||
}
|
||||
|
||||
impl KeyMappingMessageHandler {
|
||||
pub fn action_input_mapping(&self, action_to_find: &MessageDiscriminant) -> Vec<KeysGroup> {
|
||||
self.mapping_handler.action_input_mapping(action_to_find)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
mod key_mapping_message;
|
||||
mod key_mapping_message_handler;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use key_mapping_message::{KeyMappingMessage, KeyMappingMessageDiscriminant, MappingVariant, MappingVariantDiscriminant};
|
||||
#[doc(inline)]
|
||||
pub use key_mapping_message_handler::KeyMappingMessageHandler;
|
||||
@@ -2,6 +2,7 @@ mod input_mapper_message;
|
||||
mod input_mapper_message_handler;
|
||||
|
||||
pub mod default_mapping;
|
||||
pub mod key_mapping;
|
||||
pub mod utility_types;
|
||||
|
||||
#[doc(inline)]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::input_keyboard::{all_required_modifiers_pressed, KeysGroup, LayoutKeysGroup};
|
||||
use crate::messages::input_mapper::default_mapping::default_mapping;
|
||||
use crate::messages::input_mapper::key_mapping::MappingVariant;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{KeyStates, NUMBER_OF_KEYS};
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
@@ -14,22 +14,46 @@ pub struct Mapping {
|
||||
pub pointer_move: KeyMappingEntries,
|
||||
}
|
||||
|
||||
impl Mapping {
|
||||
pub fn match_input_message(&self, message: InputMapperMessage, keyboard_state: &KeyStates, actions: ActionList) -> Option<Message> {
|
||||
let list = match message {
|
||||
InputMapperMessage::KeyDown(key) => &self.key_down[key as usize],
|
||||
InputMapperMessage::KeyUp(key) => &self.key_up[key as usize],
|
||||
InputMapperMessage::DoubleClick => &self.double_click,
|
||||
InputMapperMessage::WheelScroll => &self.wheel_scroll,
|
||||
InputMapperMessage::PointerMove => &self.pointer_move,
|
||||
};
|
||||
list.match_mapping(keyboard_state, actions)
|
||||
impl Default for Mapping {
|
||||
fn default() -> Self {
|
||||
MappingVariant::Default.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Mapping {
|
||||
fn default() -> Self {
|
||||
default_mapping()
|
||||
impl Mapping {
|
||||
pub fn match_input_message(&self, message: InputMapperMessage, keyboard_state: &KeyStates, actions: ActionList) -> Option<Message> {
|
||||
let list = self.associated_entries(&message);
|
||||
list.match_mapping(keyboard_state, actions)
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, target_entry: &MappingEntry) {
|
||||
let list = self.associated_entries_mut(&target_entry.input);
|
||||
list.remove(target_entry);
|
||||
}
|
||||
|
||||
pub fn add(&mut self, new_entry: MappingEntry) {
|
||||
let list = self.associated_entries_mut(&new_entry.input);
|
||||
list.push(new_entry);
|
||||
}
|
||||
|
||||
fn associated_entries(&self, message: &InputMapperMessage) -> &KeyMappingEntries {
|
||||
match message {
|
||||
InputMapperMessage::KeyDown(key) => &self.key_down[*key as usize],
|
||||
InputMapperMessage::KeyUp(key) => &self.key_up[*key as usize],
|
||||
InputMapperMessage::DoubleClick => &self.double_click,
|
||||
InputMapperMessage::WheelScroll => &self.wheel_scroll,
|
||||
InputMapperMessage::PointerMove => &self.pointer_move,
|
||||
}
|
||||
}
|
||||
|
||||
fn associated_entries_mut(&mut self, message: &InputMapperMessage) -> &mut KeyMappingEntries {
|
||||
match message {
|
||||
InputMapperMessage::KeyDown(key) => &mut self.key_down[*key as usize],
|
||||
InputMapperMessage::KeyUp(key) => &mut self.key_up[*key as usize],
|
||||
InputMapperMessage::DoubleClick => &mut self.double_click,
|
||||
InputMapperMessage::WheelScroll => &mut self.wheel_scroll,
|
||||
InputMapperMessage::PointerMove => &mut self.pointer_move,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +76,11 @@ impl KeyMappingEntries {
|
||||
}
|
||||
|
||||
pub fn push(&mut self, entry: MappingEntry) {
|
||||
self.0.push(entry)
|
||||
self.0.push(entry);
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, target_entry: &MappingEntry) {
|
||||
self.0.retain(|entry| entry != target_entry);
|
||||
}
|
||||
|
||||
pub const fn new() -> Self {
|
||||
|
||||
Reference in New Issue
Block a user