11use crate :: {
22 image:: grammar:: Image ,
33 renderer:: {
4+ camera:: Camera ,
45 draw_uniform:: DrawUniform ,
56 effect_pipeline:: EffectPipeline ,
67 feature_uniform:: { FeatureUniform , TransformAction } ,
@@ -14,7 +15,7 @@ use crate::{
1415use anyhow:: Result ;
1516use winit:: {
1617 dpi:: PhysicalSize ,
17- event:: { ElementState , Event , KeyEvent , Modifiers , MouseButton , WindowEvent } ,
18+ event:: { ElementState , Event , KeyEvent , Modifiers , MouseButton , MouseScrollDelta , WindowEvent } ,
1819 event_loop:: EventLoop ,
1920 keyboard:: { KeyCode , PhysicalKey } ,
2021 window:: { CursorIcon , Window , WindowBuilder } ,
@@ -30,6 +31,7 @@ pub struct AppState<'a> {
3031
3132 pub feature_uniform : FeatureUniform ,
3233 pub draw_uniform : DrawUniform ,
34+ pub camera : Camera ,
3335 pub mouse_state : MouseState ,
3436 pub editor_state : EditorState ,
3537 pub modifiers : Modifiers ,
@@ -61,6 +63,8 @@ impl<'a> AppState<'a> {
6163 let draw_uniform_resource =
6264 gpu_allocator. create_uniform_resource ( "draw_uniform" , draw_uniform) ?;
6365
66+ let camera = Camera :: new ( ) ;
67+
6468 let shape_render_texture =
6569 gpu_allocator. create_render_texture ( "shape_texture" , size. width , size. height ) ;
6670
@@ -147,6 +151,7 @@ impl<'a> AppState<'a> {
147151 size,
148152 feature_uniform,
149153 draw_uniform,
154+ camera,
150155 mouse_state,
151156 editor_state,
152157 modifiers,
@@ -179,14 +184,48 @@ impl<'a> AppState<'a> {
179184 let feature_uniform = & mut self . feature_uniform ;
180185 let draw_uniform = & mut self . draw_uniform ;
181186
187+ // the state
188+ // note to future self: we are probably due for a refactor
189+ // right now, application state, inputs, windowing are all coupled together
190+ // it would be good to be able to reuse certain commands for instance, think about cut (cmd + x)
191+ let mut super_key_pressed = if cfg ! ( target_os = "macos" ) {
192+ self . modifiers . state ( ) . super_key ( )
193+ } else {
194+ self . modifiers . state ( ) . control_key ( )
195+ } ;
196+
197+ let mut draw_mode = draw_uniform. crosshair ( ) ;
198+
199+ // some global rules
200+ // maybe keep a stack of actions?
201+ draw_mode = if super_key_pressed { false } else { draw_mode } ;
202+
182203 match event {
183204 WindowEvent :: MouseInput { state, button, .. } => {
184205 if * button == MouseButton :: Left {
185206 let prev_state = self . mouse_state . pressed ( ) ;
186207 self . mouse_state
187208 . set_pressed ( matches ! ( state, ElementState :: Pressed ) ) ;
188209
189- if draw_uniform. crosshair ( ) {
210+ // camera panning
211+ if super_key_pressed && !draw_mode {
212+ self . window . set_cursor_icon ( CursorIcon :: Grab ) ;
213+
214+ match ( prev_state, self . mouse_state . pressed ( ) ) {
215+ ( false , true ) => {
216+ let ( x, y) = self . mouse_state . position ( ) ;
217+ self . mouse_state . set_camera_pan_start ( Some ( ( x, y) ) ) ;
218+ }
219+ ( true , false ) => {
220+ self . mouse_state . set_camera_pan_start ( None ) ;
221+ self . window . set_cursor_icon ( CursorIcon :: Default ) ;
222+ }
223+ _ => { }
224+ }
225+ return true ;
226+ }
227+
228+ if draw_mode {
190229 // Crosshair mode: Draw new circles
191230 match ( prev_state, self . mouse_state . pressed ( ) ) {
192231 ( false , true ) => {
@@ -265,10 +304,49 @@ impl<'a> AppState<'a> {
265304 }
266305 }
267306 }
307+ WindowEvent :: MouseWheel { delta, .. } => {
308+ match delta {
309+ MouseScrollDelta :: LineDelta ( _, y) => {
310+ let zoom_speed = 0.1 ;
311+ let zoom_factor = if * y > 0.0 {
312+ 1.0 + zoom_speed
313+ } else if * y < 0.0 {
314+ 1.0 - zoom_speed
315+ } else {
316+ 1.0
317+ } ;
318+ self . camera . zoom ( zoom_factor) ;
319+ }
320+ MouseScrollDelta :: PixelDelta ( pos) => {
321+ // Zoom with two-finger scroll
322+ let zoom_speed = 0.1 ;
323+ let y = pos. y as f32 ;
324+ let zoom_factor = if y > 0.0 {
325+ 1.0 + zoom_speed * 0.01 * y
326+ } else if y < 0.0 {
327+ 1.0 + zoom_speed * 0.01 * y
328+ } else {
329+ 1.0
330+ } ;
331+ self . camera . zoom ( zoom_factor) ;
332+ }
333+ } ;
334+ }
268335 WindowEvent :: CursorMoved { position, .. } => {
269336 let ( x, y) = ( position. x as f32 , position. y as f32 ) ;
270337
271- if draw_uniform. crosshair ( ) {
338+ if super_key_pressed && self . mouse_state . pressed ( ) {
339+ if let Some ( ( start_x, start_y) ) = self . mouse_state . camera_pan_start ( ) {
340+ let delta_x = ( x - start_x) / ( self . size . width as f32 ) * 2.0 ;
341+ let delta_y = -( y - start_y) / ( self . size . height as f32 ) * 2.0 ; // Invert Y
342+
343+ self . camera . pan ( delta_x, delta_y) ;
344+
345+ self . mouse_state . set_camera_pan_start ( Some ( ( x, y) ) ) ;
346+ }
347+ }
348+
349+ if draw_mode {
272350 // Crosshair mode: Update the preview circle radius
273351 if let Some ( center) = self . mouse_state . start_drag ( ) {
274352 let radius = compute_distance ( center, ( x, y) ) ;
@@ -404,6 +482,9 @@ impl<'a> AppState<'a> {
404482 }
405483 }
406484 }
485+ ( KeyCode :: KeyR , ElementState :: Pressed ) => {
486+ self . camera . reset ( ) ;
487+ }
407488 _ => return false ,
408489 }
409490 }
@@ -421,6 +502,10 @@ impl<'a> AppState<'a> {
421502 self . feature_uniform . gamma ( ) ,
422503 ) ;
423504
505+ // Update camera in draw uniform
506+ self . draw_uniform
507+ . update_camera ( self . camera . view_projection_matrix ( ) ) ;
508+
424509 // Update image shader uniforms
425510 let uniform_resources = & self . image_shader . uniform_resources ;
426511 self . gpu_allocator
0 commit comments