iced_markdown_editor/
zoom.rs1use crate::widget::Command;
22use iced::keyboard::{Key, Modifiers};
23
24#[derive(Debug, Clone, PartialEq)]
31pub struct KeyBinding {
32 pub key: Key,
33 pub modifiers: Modifiers,
34}
35
36impl KeyBinding {
37 pub fn new(key: Key, modifiers: Modifiers) -> Self {
38 Self { key, modifiers }
39 }
40
41 pub fn command(character: &str) -> Self {
43 Self::new(Key::Character(character.into()), Modifiers::COMMAND)
44 }
45
46 pub fn command_shift(character: &str) -> Self {
48 Self::new(
49 Key::Character(character.into()),
50 Modifiers::COMMAND | Modifiers::SHIFT,
51 )
52 }
53
54 pub fn matches(&self, key: &Key, modified_key: &Key, modifiers: Modifiers) -> bool {
56 let same_key = |pressed: &Key| match (self.key.as_ref(), pressed.as_ref()) {
57 (Key::Character(a), Key::Character(b)) => a.eq_ignore_ascii_case(b),
58 (a, b) => a == b,
59 };
60
61 (same_key(key) || same_key(modified_key))
62 && modifiers.control() == self.modifiers.control()
63 && modifiers.alt() == self.modifiers.alt()
64 && modifiers.logo() == self.modifiers.logo()
65 && (!self.modifiers.shift() || modifiers.shift())
66 }
67}
68
69#[derive(Debug, Clone, PartialEq)]
71pub struct ZoomShortcuts {
72 pub zoom_in: Vec<KeyBinding>,
73 pub zoom_out: Vec<KeyBinding>,
74 pub reset: Vec<KeyBinding>,
75}
76
77impl Default for ZoomShortcuts {
78 fn default() -> Self {
80 Self {
81 zoom_in: vec![KeyBinding::command("+"), KeyBinding::command("=")],
82 zoom_out: vec![KeyBinding::command("-")],
83 reset: vec![KeyBinding::command("0")],
84 }
85 }
86}
87
88impl ZoomShortcuts {
89 pub fn none() -> Self {
91 Self {
92 zoom_in: Vec::new(),
93 zoom_out: Vec::new(),
94 reset: Vec::new(),
95 }
96 }
97}
98
99#[derive(Debug, Clone, PartialEq)]
101pub struct ZoomSettings {
102 pub enabled: bool,
106 pub min: f32,
107 pub max: f32,
108 pub levels: Vec<f32>,
111 pub mouse_wheel: bool,
113 pub controls: bool,
115 pub shortcuts: ZoomShortcuts,
116}
117
118impl Default for ZoomSettings {
119 fn default() -> Self {
120 Self {
121 enabled: true,
122 min: 0.25,
123 max: 5.0,
124 levels: vec![
125 0.25, 0.33, 0.5, 0.67, 0.75, 0.8, 0.9, 1.0, 1.1, 1.25, 1.5, 1.75, 2.0, 2.5, 3.0,
126 4.0, 5.0,
127 ],
128 mouse_wheel: true,
129 controls: true,
130 shortcuts: ZoomShortcuts::default(),
131 }
132 }
133}
134
135impl ZoomSettings {
136 pub fn disabled() -> Self {
138 Self {
139 enabled: false,
140 ..Self::default()
141 }
142 }
143
144 pub fn clamp(&self, zoom: f32) -> f32 {
145 let (min, max) = (self.min.min(self.max), self.max.max(self.min));
146 zoom.clamp(min.max(0.05), max.max(0.05))
147 }
148
149 pub fn step(&self, zoom: f32, direction: i32) -> f32 {
151 let next = if self.levels.is_empty() {
152 if direction > 0 {
153 zoom * 1.1
154 } else {
155 zoom / 1.1
156 }
157 } else if direction > 0 {
158 self.levels
159 .iter()
160 .copied()
161 .find(|level| *level > zoom * 1.001)
162 .unwrap_or(self.max)
163 } else {
164 self.levels
165 .iter()
166 .rev()
167 .copied()
168 .find(|level| *level < zoom * 0.999)
169 .unwrap_or(self.min)
170 };
171 self.clamp(next)
172 }
173
174 pub(crate) fn command_for(
176 &self,
177 key: &Key,
178 modified_key: &Key,
179 modifiers: Modifiers,
180 ) -> Option<Command> {
181 if !self.enabled {
182 return None;
183 }
184 let hit = |bindings: &[KeyBinding]| {
185 bindings
186 .iter()
187 .any(|binding| binding.matches(key, modified_key, modifiers))
188 };
189 if hit(&self.shortcuts.zoom_in) {
190 Some(Command::ZoomIn)
191 } else if hit(&self.shortcuts.zoom_out) {
192 Some(Command::ZoomOut)
193 } else if hit(&self.shortcuts.reset) {
194 Some(Command::ResetZoom)
195 } else {
196 None
197 }
198 }
199}