KiCad PCB EDA Suite
Loading...
Searching...
No Matches
wx_view_controls.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de
5 * Copyright (C) 2013-2015 CERN
6 * Copyright (C) 2012-2022 KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * @author Tomasz Wlostowski <[email protected]>
9 * @author Maciej Suminski <[email protected]>
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, you may find one here:
23 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
24 * or you may search the http://www.gnu.org website for the version 2 license,
25 * or you may write to the Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
27 */
28
29#include <pgm_base.h>
30#include <core/profile.h>
31#include <view/view.h>
36#include <trace_helpers.h>
38#include <math/util.h> // for KiROUND
40#include <widgets/ui_common.h>
42#include <eda_draw_frame.h>
43#include <kiway.h>
44#include <kiplatform/ui.h>
45#include <wx/log.h>
46
47#ifdef __WXMSW__
48 #define USE_MOUSE_CAPTURE
49#endif
50
51using namespace KIGFX;
52
53const wxEventType WX_VIEW_CONTROLS::EVT_REFRESH_MOUSE = wxNewEventType();
54
55
56static std::unique_ptr<ZOOM_CONTROLLER> GetZoomControllerForPlatform( bool aAcceleration )
57{
58#ifdef __WXMAC__
59 // On Apple pointer devices, wheel events occur frequently and with
60 // smaller rotation values. For those devices, let's handle zoom
61 // based on the rotation amount rather than the time difference.
62 return std::make_unique<CONSTANT_ZOOM_CONTROLLER>( CONSTANT_ZOOM_CONTROLLER::MAC_SCALE );
63#elif __WXGTK3__
64 // GTK3 is similar, but the scale constant is smaller
65 return std::make_unique<CONSTANT_ZOOM_CONTROLLER>( CONSTANT_ZOOM_CONTROLLER::GTK3_SCALE );
66#else
67 if( aAcceleration )
68 return std::make_unique<ACCELERATING_ZOOM_CONTROLLER>();
69 else
70 return std::make_unique<CONSTANT_ZOOM_CONTROLLER>( CONSTANT_ZOOM_CONTROLLER::MSW_SCALE );
71#endif
72}
73
74
76 VIEW_CONTROLS( aView ),
77 m_state( IDLE ),
78 m_parentPanel( aParentPanel ),
79 m_scrollScale( 1.0, 1.0 ),
80#ifdef __WXGTK3__
81 m_lastTimestamp( 0 ),
82#endif
83 m_cursorPos( 0, 0 ),
84 m_updateCursor( true ),
85 m_infinitePanWorks( false )
86{
88
89 m_MotionEventCounter = std::make_unique<PROF_COUNTER>( "Mouse motion events" );
90
91 m_parentPanel->Connect( wxEVT_MOTION,
92 wxMouseEventHandler( WX_VIEW_CONTROLS::onMotion ), nullptr, this );
93 m_parentPanel->Connect( wxEVT_MAGNIFY,
94 wxMouseEventHandler( WX_VIEW_CONTROLS::onMagnify ), nullptr, this );
95 m_parentPanel->Connect( wxEVT_MOUSEWHEEL,
96 wxMouseEventHandler( WX_VIEW_CONTROLS::onWheel ), nullptr, this );
97 m_parentPanel->Connect( wxEVT_MIDDLE_UP,
98 wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), nullptr, this );
99 m_parentPanel->Connect( wxEVT_MIDDLE_DOWN,
100 wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), nullptr, this );
101 m_parentPanel->Connect( wxEVT_LEFT_UP,
102 wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), nullptr, this );
103 m_parentPanel->Connect( wxEVT_LEFT_DOWN,
104 wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), nullptr, this );
105 m_parentPanel->Connect( wxEVT_RIGHT_UP,
106 wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), nullptr, this );
107 m_parentPanel->Connect( wxEVT_RIGHT_DOWN,
108 wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), nullptr, this );
109#if defined __WXMSW__
110 m_parentPanel->Connect( wxEVT_ENTER_WINDOW,
111 wxMouseEventHandler( WX_VIEW_CONTROLS::onEnter ), nullptr, this );
112#endif
113 m_parentPanel->Connect( wxEVT_LEAVE_WINDOW,
114 wxMouseEventHandler( WX_VIEW_CONTROLS::onLeave ), nullptr, this );
115 m_parentPanel->Connect( wxEVT_SCROLLWIN_THUMBTRACK,
116 wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), nullptr, this );
117 m_parentPanel->Connect( wxEVT_SCROLLWIN_PAGEUP,
118 wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), nullptr, this );
119 m_parentPanel->Connect( wxEVT_SCROLLWIN_PAGEDOWN,
120 wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), nullptr, this );
121
122 m_parentPanel->Connect( wxEVT_SCROLLWIN_BOTTOM,
123 wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), nullptr, this );
124 m_parentPanel->Connect( wxEVT_SCROLLWIN_TOP,
125 wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), nullptr, this );
126 m_parentPanel->Connect( wxEVT_SCROLLWIN_LINEUP,
127 wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), nullptr, this );
128 m_parentPanel->Connect( wxEVT_SCROLLWIN_LINEDOWN,
129 wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), nullptr, this );
130#if defined USE_MOUSE_CAPTURE
131 m_parentPanel->Connect( wxEVT_MOUSE_CAPTURE_LOST,
132 wxMouseEventHandler( WX_VIEW_CONTROLS::onCaptureLost ), nullptr, this );
133#endif
134
135 m_cursorWarped = false;
136
137 m_panTimer.SetOwner( this );
138 this->Connect( wxEVT_TIMER, wxTimerEventHandler( WX_VIEW_CONTROLS::onTimer ), nullptr, this );
139
143}
144
145
147{
148#if defined USE_MOUSE_CAPTURE
149 if( m_parentPanel->HasCapture() )
150 m_parentPanel->ReleaseMouse();
151#endif
152}
153
154
156{
158
174
175 m_zoomController.reset();
176
177 if( cfg->m_Input.zoom_speed_auto )
178 {
180 }
181 else
182 {
183 if( cfg->m_Input.zoom_acceleration )
184 {
186 std::make_unique<ACCELERATING_ZOOM_CONTROLLER>( cfg->m_Input.zoom_speed );
187 }
188 else
189 {
191
192 m_zoomController = std::make_unique<CONSTANT_ZOOM_CONTROLLER>( scale );
193 }
194 }
195}
196
197
198void WX_VIEW_CONTROLS::onMotion( wxMouseEvent& aEvent )
199{
200 ( *m_MotionEventCounter )++;
201
202 // Because Weston sends a motion event to previous location after warping the pointer
203 wxPoint mouseRel = m_parentPanel->ScreenToClient( KIPLATFORM::UI::GetMousePosition() );
204
205 bool isAutoPanning = false;
206 int x = mouseRel.x;
207 int y = mouseRel.y;
208 VECTOR2D mousePos( x, y );
209
210 // Automatic focus switching between SCH and PCB windows on canvas mouse motion
212 {
214 {
215 KIWAY_PLAYER* otherFrame = nullptr;
216
217 if( frame->IsType( FRAME_PCB_EDITOR ) )
218 {
219 otherFrame = frame->Kiway().Player( FRAME_SCH, false );
220 }
221 else if( frame->IsType( FRAME_SCH ) )
222 {
223 otherFrame = frame->Kiway().Player( FRAME_PCB_EDITOR, false );
224 }
225
226 if( otherFrame && KIPLATFORM::UI::IsWindowActive( otherFrame )
227 && !KIPLATFORM::UI::IsWindowActive( frame ) )
228 {
229 frame->Raise();
230 }
231 }
232 }
233
235 handleCursorCapture( x, y );
236
238 isAutoPanning = handleAutoPanning( aEvent );
239
240 if( !isAutoPanning && aEvent.Dragging() )
241 {
242 if( m_state == DRAG_PANNING )
243 {
244 static bool justWarped = false;
245 int warpX = 0;
246 int warpY = 0;
247 wxSize parentSize = m_parentPanel->GetClientSize();
248
249 if( x < 0 )
250 {
251 warpX = parentSize.x;
252 }
253 else if(x >= parentSize.x )
254 {
255 warpX = -parentSize.x;
256 }
257
258 if( y < 0 )
259 {
260 warpY = parentSize.y;
261 }
262 else if( y >= parentSize.y )
263 {
264 warpY = -parentSize.y;
265 }
266
267 if( !justWarped )
268 {
269 VECTOR2D d = m_dragStartPoint - mousePos;
270 m_dragStartPoint = mousePos;
271 VECTOR2D delta = m_view->ToWorld( d, false );
273 aEvent.StopPropagation();
274 }
275
276 if( warpX || warpY )
277 {
278 if( !justWarped )
279 {
281 && KIPLATFORM::UI::WarpPointer( m_parentPanel, x + warpX, y + warpY ) )
282 {
283 m_dragStartPoint += VECTOR2D( warpX, warpY );
284 justWarped = true;
285 }
286 }
287 else
288 justWarped = false;
289 }
290 else
291 justWarped = false;
292 }
293 else if( m_state == DRAG_ZOOMING )
294 {
295 static bool justWarped = false;
296 int warpY = 0;
297 wxSize parentSize = m_parentPanel->GetClientSize();
298
299 if( y < 0 )
300 {
301 warpY = parentSize.y;
302 }
303 else if( y >= parentSize.y )
304 {
305 warpY = -parentSize.y;
306 }
307
308 if( !justWarped )
309 {
310 VECTOR2D d = m_dragStartPoint - mousePos;
311 m_dragStartPoint = mousePos;
312
313 double scale = exp( d.y * m_settings.m_zoomSpeed * 0.001 );
314
315 wxLogTrace( traceZoomScroll, wxString::Format( "dy: %f scale: %f", d.y, scale ) );
316
318 aEvent.StopPropagation();
319 }
320
321 if( warpY )
322 {
323 if( !justWarped )
324 {
326 m_dragStartPoint += VECTOR2D( 0, warpY );
327 justWarped = true;
328 }
329 else
330 justWarped = false;
331 }
332 else
333 justWarped = false;
334 }
335 }
336
337 if( m_updateCursor ) // do not update the cursor position if it was explicitly set
338 m_cursorPos = GetClampedCoords( m_view->ToWorld( mousePos ) );
339 else
340 m_updateCursor = true;
341
342 aEvent.Skip();
343}
344
345
346void WX_VIEW_CONTROLS::onWheel( wxMouseEvent& aEvent )
347{
348#ifdef __WXGTK3__
349 if( aEvent.GetTimestamp() == m_lastTimestamp )
350 {
351 aEvent.Skip( false );
352 return;
353 }
354
355 m_lastTimestamp = aEvent.GetTimestamp();
356#endif
357
358 const double wheelPanSpeed = 0.001;
359 const int axis = aEvent.GetWheelAxis();
360
361 if( axis == wxMOUSE_WHEEL_HORIZONTAL && !m_settings.m_horizontalPan )
362 return;
363
364 // Pick the modifier, if any. Shift beats control beats alt, we don't support more than one.
365 int modifiers =
366 aEvent.ShiftDown() ? WXK_SHIFT :
367 ( aEvent.ControlDown() ? WXK_CONTROL : ( aEvent.AltDown() ? WXK_ALT : 0 ) );
368
369 // Restrict zoom handling to the vertical axis, otherwise horizontal
370 // scrolling events (e.g. touchpads and some mice) end up interpreted
371 // as vertical scroll events and confuse the user.
372 if( modifiers == m_settings.m_scrollModifierZoom )
373 {
374 if ( axis == wxMOUSE_WHEEL_VERTICAL )
375 {
376 const int rotation = aEvent.GetWheelRotation();
377 const double zoomScale = m_zoomController->GetScaleForRotation( rotation );
378
380 {
382 m_view->SetScale( m_view->GetScale() * zoomScale );
383 }
384 else
385 {
386 const VECTOR2D anchor = m_view->ToWorld( VECTOR2D( aEvent.GetX(), aEvent.GetY() ) );
387 m_view->SetScale( m_view->GetScale() * zoomScale, anchor );
388 }
389
390 // Refresh the zoom level and mouse position on message panel
391 // (mouse position has not changed, only the zoom level has changed):
392 refreshMouse( true );
393 }
394 }
395 else
396 {
397 // Scrolling
398 VECTOR2D scrollVec = m_view->ToWorld( m_view->GetScreenPixelSize(), false ) *
399 ( (double) aEvent.GetWheelRotation() * wheelPanSpeed );
400 double scrollX = 0.0;
401 double scrollY = 0.0;
402 bool hReverse = false;
403
404 if( axis != wxMOUSE_WHEEL_HORIZONTAL )
406
407 if( axis == wxMOUSE_WHEEL_HORIZONTAL || modifiers == m_settings.m_scrollModifierPanH )
408 {
409 if( hReverse )
410 scrollX = scrollVec.x;
411 else
412 scrollX = ( axis == wxMOUSE_WHEEL_HORIZONTAL ) ? scrollVec.x : -scrollVec.x;
413 }
414 else
415 {
416 scrollY = -scrollVec.y;
417 }
418
419 VECTOR2D delta( scrollX, scrollY );
420
422 refreshMouse( true );
423 }
424
425 // Do not skip this event, otherwise wxWidgets will fire
426 // 3 wxEVT_SCROLLWIN_LINEUP or wxEVT_SCROLLWIN_LINEDOWN (normal wxWidgets behavior)
427 // and we do not want that.
428}
429
430
431void WX_VIEW_CONTROLS::onMagnify( wxMouseEvent& aEvent )
432{
433 // Scale based on the magnification from our underlying magnification event.
434 VECTOR2D anchor = m_view->ToWorld( VECTOR2D( aEvent.GetX(), aEvent.GetY() ) );
435 m_view->SetScale( m_view->GetScale() * ( aEvent.GetMagnification() + 1.0f ), anchor );
436
437 aEvent.Skip();
438}
439
440
442{
443 m_state = aNewState;
444}
445
446void WX_VIEW_CONTROLS::onButton( wxMouseEvent& aEvent )
447{
448 switch( m_state )
449 {
450 case IDLE:
451 case AUTO_PANNING:
452 if( ( aEvent.MiddleDown() && m_settings.m_dragMiddle == MOUSE_DRAG_ACTION::PAN ) ||
453 ( aEvent.RightDown() && m_settings.m_dragRight == MOUSE_DRAG_ACTION::PAN ) )
454 {
455 m_dragStartPoint = VECTOR2D( aEvent.GetX(), aEvent.GetY() );
458
459#if defined USE_MOUSE_CAPTURE
460 if( !m_parentPanel->HasCapture() )
461 m_parentPanel->CaptureMouse();
462#endif
463 }
464 else if( ( aEvent.MiddleDown() && m_settings.m_dragMiddle == MOUSE_DRAG_ACTION::ZOOM ) ||
465 ( aEvent.RightDown() && m_settings.m_dragRight == MOUSE_DRAG_ACTION::ZOOM ) )
466 {
467 m_dragStartPoint = VECTOR2D( aEvent.GetX(), aEvent.GetY() );
470
471#if defined USE_MOUSE_CAPTURE
472 if( !m_parentPanel->HasCapture() )
473 m_parentPanel->CaptureMouse();
474#endif
475 }
476
477 if( aEvent.LeftUp() )
478 setState( IDLE ); // Stop autopanning when user release left mouse button
479
480 break;
481
482 case DRAG_ZOOMING:
483 case DRAG_PANNING:
484 if( aEvent.MiddleUp() || aEvent.LeftUp() || aEvent.RightUp() )
485 {
486 setState( IDLE );
488
489#if defined USE_MOUSE_CAPTURE
490 if( !m_settings.m_cursorCaptured && m_parentPanel->HasCapture() )
491 m_parentPanel->ReleaseMouse();
492#endif
493 }
494
495 break;
496 }
497
498 aEvent.Skip();
499}
500
501
502void WX_VIEW_CONTROLS::onEnter( wxMouseEvent& aEvent )
503{
504 // Avoid stealing focus from text controls
505 // This is particularly important for users using On-Screen-Keyboards
506 // They may move the mouse over the canvas to reach the keyboard
508 {
509 return;
510 }
511
512#if defined( _WIN32 ) || defined( __WXGTK__ )
513 // Win32 and some *nix WMs transmit mouse move and wheel events to all controls below the mouse regardless
514 // of focus. Forcing the focus here will cause the EDA FRAMES to immediately become the
515 // top level active window.
516 if( m_parentPanel->GetParent() != nullptr )
517 {
518 // this assumes the parent panel's parent is the eda window
520 {
522 }
523 }
524#else
526#endif
527}
528
529
530void WX_VIEW_CONTROLS::onLeave( wxMouseEvent& aEvent )
531{
532#if !defined USE_MOUSE_CAPTURE
533 onMotion( aEvent );
534#endif
535}
536
537void WX_VIEW_CONTROLS::onCaptureLost( wxMouseEvent& aEvent )
538{
539 // This method must be present to suppress the capture-lost assertion
540
541 // Set the flag to allow calling m_parentPanel->CaptureMouse()
542 // Note: One cannot call m_parentPanel->CaptureMouse() twice, this is not accepted
543 // by wxWidgets (MSW specific) so we need this guard
545}
546
547
548void WX_VIEW_CONTROLS::onTimer( wxTimerEvent& aEvent )
549{
550 switch( m_state )
551 {
552 case AUTO_PANNING:
553 {
555 {
556 setState( IDLE );
557 return;
558 }
559
560 #ifdef __WXMSW__
561 // Hackfix: It's possible for the mouse to leave the canvas
562 // without triggering any leave events on windows
563 // Use a MSW only wx function
564 if( !m_parentPanel->IsMouseInWindow() )
565 {
566 m_panTimer.Stop();
567 setState( IDLE );
568 return;
569 }
570 #endif
571
572 if( !m_parentPanel->HasFocus() && !m_parentPanel->StatusPopupHasFocus() )
573 {
574 setState( IDLE );
575 return;
576 }
577
578 double borderSize = std::min( m_settings.m_autoPanMargin * m_view->GetScreenPixelSize().x,
580
581 // When the mouse cursor is outside the area with no pan,
582 // m_panDirection is the dist to this area limit ( in pixels )
583 // It will be used also as pan value (the pan speed depends on this dist).
585
586 // When the mouse cursor is outside the area with no pan, the pan value
587 // is accelerated depending on the dist between the area and the cursor
588 float accel = 0.5f + ( m_settings.m_autoPanAcceleration / 5.0f );
589
590 // For a small mouse cursor dist to area, just use the distance.
591 // But for a dist > borderSize / 2, use an accelerated pan value
592
593 if( dir.EuclideanNorm() >= borderSize ) // far from area limits
594 dir = dir.Resize( borderSize * accel );
595 else if( dir.EuclideanNorm() > borderSize / 2 ) // Near from area limits
596 dir = dir.Resize( borderSize );
597
598 dir = m_view->ToWorld( dir, false );
599 m_view->SetCenter( m_view->GetCenter() + dir );
600
601 refreshMouse( true );
602
603 m_panTimer.Start();
604 }
605 break;
606
607 case IDLE: // Just remove unnecessary warnings
608 case DRAG_PANNING:
609 case DRAG_ZOOMING:
610 break;
611 }
612}
613
614
615void WX_VIEW_CONTROLS::onScroll( wxScrollWinEvent& aEvent )
616{
617 const double linePanDelta = 0.05;
618 const double pagePanDelta = 0.5;
619
620 int type = aEvent.GetEventType();
621 int dir = aEvent.GetOrientation();
622
623 if( type == wxEVT_SCROLLWIN_THUMBTRACK )
624 {
625 auto center = m_view->GetCenter();
626 const auto& boundary = m_view->GetBoundary();
627
628 // Flip scroll direction in flipped view
629 const double xstart = ( m_view->IsMirroredX() ?
630 boundary.GetRight() : boundary.GetLeft() );
631 const double xdelta = ( m_view->IsMirroredX() ? -1 : 1 );
632
633 if( dir == wxHORIZONTAL )
634 center.x = xstart + xdelta * ( aEvent.GetPosition() / m_scrollScale.x );
635 else
636 center.y = boundary.GetTop() + aEvent.GetPosition() / m_scrollScale.y;
637
638 m_view->SetCenter( center );
639 }
640 else if( type == wxEVT_SCROLLWIN_THUMBRELEASE ||
641 type == wxEVT_SCROLLWIN_TOP ||
642 type == wxEVT_SCROLLWIN_BOTTOM )
643 {
644 // Do nothing on thumb release, we don't care about it.
645 // We don't have a concept of top or bottom in our viewport, so ignore those events.
646 }
647 else
648 {
649 double dist = 0;
650
651 if( type == wxEVT_SCROLLWIN_PAGEUP )
652 {
653 dist = pagePanDelta;
654 }
655 else if( type == wxEVT_SCROLLWIN_PAGEDOWN )
656 {
657 dist = -pagePanDelta;
658 }
659 else if( type == wxEVT_SCROLLWIN_LINEUP )
660 {
661 dist = linePanDelta;
662 }
663 else if( type == wxEVT_SCROLLWIN_LINEDOWN )
664 {
665 dist = -linePanDelta;
666 }
667 else
668 {
669 wxCHECK_MSG( false, /* void */, wxT( "Unhandled event type" ) );
670 }
671
672 VECTOR2D scroll = m_view->ToWorld( m_view->GetScreenPixelSize(), false ) * dist;
673
674 double scrollX = 0.0;
675 double scrollY = 0.0;
676
677 if ( dir == wxHORIZONTAL )
678 scrollX = -scroll.x;
679 else
680 scrollY = -scroll.y;
681
682 VECTOR2D delta( scrollX, scrollY );
683
685 }
686
688}
689
690
692{
693#if defined USE_MOUSE_CAPTURE
694 // Note: for some reason, m_parentPanel->HasCapture() can be false even if CaptureMouse()
695 // was called (i.e. mouse was captured, so when need to test m_MouseCapturedLost to be
696 // sure a wxEVT_MOUSE_CAPTURE_LOST event was fired before. Otherwise wxMSW complains
697 if( aEnabled && !m_parentPanel->HasCapture() && m_parentPanel->m_MouseCapturedLost )
698 {
699 m_parentPanel->CaptureMouse();
700
701 // Clear the flag to allow calling m_parentPanel->CaptureMouse()
702 // Calling it without calling ReleaseMouse() is not accepted by wxWidgets (MSW specific)
704 }
705 else if( !aEnabled && m_parentPanel->HasCapture()
707 {
708 m_parentPanel->ReleaseMouse();
709
710 // Mouse is released, calling CaptureMouse() is allowed now:
712 }
713#endif
715}
716
717
719{
721 {
722 setState( IDLE );
723#if defined USE_MOUSE_CAPTURE
724 if( !m_settings.m_cursorCaptured && m_parentPanel->HasCapture() )
725 m_parentPanel->ReleaseMouse();
726#endif
727 }
728}
729
730
731VECTOR2D WX_VIEW_CONTROLS::GetMousePosition( bool aWorldCoordinates ) const
732{
733 wxPoint msp = getMouseScreenPosition();
734 VECTOR2D screenPos( msp.x, msp.y );
735
736 return aWorldCoordinates ? GetClampedCoords( m_view->ToWorld( screenPos ) ) : screenPos;
737}
738
739
741{
742 GAL* gal = m_view->GetGAL();
743
744 if( aEnableSnapping && gal->GetGridSnapping() )
745 {
746 return gal->GetGridPoint( m_cursorPos );
747 }
748 else
749 {
750 return m_cursorPos;
751 }
752}
753
754
756{
758 {
760 }
761 else
762 {
763 return GetClampedCoords( GetRawCursorPosition( aEnableSnapping ) );
764 }
765}
766
767
768void WX_VIEW_CONTROLS::SetCursorPosition( const VECTOR2D& aPosition, bool aWarpView,
769 bool aTriggeredByArrows, long aArrowCommand )
770{
771 m_updateCursor = false;
772
773 VECTOR2D clampedPosition = GetClampedCoords( aPosition );
774
775 if( aTriggeredByArrows )
776 {
780 m_cursorWarped = false;
781 }
782 else
783 {
787 m_cursorWarped = true;
788 }
789
790 WarpMouseCursor( clampedPosition, true, aWarpView );
791 m_cursorPos = clampedPosition;
792}
793
794
796 bool aWarpView = true )
797{
798 m_updateCursor = false;
799
800 VECTOR2D clampedPosition = GetClampedCoords( aPosition );
801
802 const VECTOR2I& screenSize = m_view->GetGAL()->GetScreenPixelSize();
803 BOX2I screen( VECTOR2I( 0, 0 ), screenSize );
804 VECTOR2D screenPos = m_view->ToScreen( clampedPosition );
805
806 if( aWarpView && !screen.Contains( screenPos ) )
807 m_view->SetCenter( clampedPosition );
808
809 m_cursorPos = clampedPosition;
810}
811
812
813void WX_VIEW_CONTROLS::WarpMouseCursor( const VECTOR2D& aPosition, bool aWorldCoordinates,
814 bool aWarpView )
815{
816 if( aWorldCoordinates )
817 {
818 const VECTOR2I& screenSize = m_view->GetGAL()->GetScreenPixelSize();
819 BOX2I screen( VECTOR2I( 0, 0 ), screenSize );
820 VECTOR2D clampedPosition = GetClampedCoords( aPosition );
821 VECTOR2D screenPos = m_view->ToScreen( clampedPosition );
822
823 if( !screen.Contains( screenPos ) )
824 {
825 if( aWarpView )
826 {
827 m_view->SetCenter( clampedPosition );
828 KIPLATFORM::UI::WarpPointer( m_parentPanel, screenSize.x / 2, screenSize.y / 2 );
829 }
830 }
831 else
832 {
833 KIPLATFORM::UI::WarpPointer( m_parentPanel, screenPos.x, screenPos.y );
834 }
835 }
836 else
837 {
838 KIPLATFORM::UI::WarpPointer( m_parentPanel, aPosition.x, aPosition.y );
839 }
840
841 // If we are not refreshing because of mouse movement, don't set the modifiers
842 // because we are refreshing for keyboard movement, which uses the same modifiers for other actions
844}
845
846
848{
849 const VECTOR2I& screenSize = m_view->GetGAL()->GetScreenPixelSize();
850 VECTOR2I screenCenter( screenSize / 2 );
851
852 if( GetMousePosition( false ) != screenCenter )
853 {
854 VECTOR2D newCenter = GetCursorPosition();
855
856 if( KIPLATFORM::UI::WarpPointer( m_parentPanel, screenCenter.x, screenCenter.y ) )
857 {
858 m_view->SetCenter( newCenter );
859 m_dragStartPoint = screenCenter;
860 }
861 }
862}
863
864
866{
867 int border = std::min( m_settings.m_autoPanMargin * m_view->GetScreenPixelSize().x,
869 border += 2;
870
871 VECTOR2D topLeft( border, border );
872 VECTOR2D botRight( m_view->GetScreenPixelSize().x - border,
873 m_view->GetScreenPixelSize().y - border );
874
875 topLeft = m_view->ToWorld( topLeft );
876 botRight = m_view->ToWorld( botRight );
877
878 VECTOR2D pos = GetMousePosition( true );
879
880 if( pos.x < topLeft.x )
881 pos.x = topLeft.x;
882 else if( pos.x > botRight.x )
883 pos.x = botRight.x;
884
885 if( pos.y < topLeft.y )
886 pos.y = topLeft.y;
887 else if( pos.y > botRight.y )
888 pos.y = botRight.y;
889
890 SetCursorPosition( pos, false, false, 0 );
891
892 if( aWarpMouseCursor )
893 WarpMouseCursor( pos, true );
894}
895
896
897bool WX_VIEW_CONTROLS::handleAutoPanning( const wxMouseEvent& aEvent )
898{
899 VECTOR2I p( aEvent.GetX(), aEvent.GetY() );
901
903 {
904 // last cursor move event came from keyboard cursor control. If auto-panning is enabled
905 // and the next position is inside the autopan zone, check if it really came from a mouse
906 // event, otherwise disable autopan temporarily. Also temporarily disable autopan if the
907 // cursor is in the autopan zone because the application warped the cursor.
908
909 m_cursorWarped = false;
910 return true;
911 }
912
913 m_cursorWarped = false;
914
915 // Compute areas where autopanning is active
916 int borderStart = std::min( m_settings.m_autoPanMargin * m_view->GetScreenPixelSize().x,
918 borderStart = std::max( borderStart, 2 );
919 int borderEndX = m_view->GetScreenPixelSize().x - borderStart;
920 int borderEndY = m_view->GetScreenPixelSize().y - borderStart;
921
922 if( p.x < borderStart )
923 m_panDirection.x = -( borderStart - p.x );
924 else if( p.x > borderEndX )
925 m_panDirection.x = ( p.x - borderEndX );
926 else
927 m_panDirection.x = 0;
928
929 if( p.y < borderStart )
930 m_panDirection.y = -( borderStart - p.y );
931 else if( p.y > borderEndY )
932 m_panDirection.y = ( p.y - borderEndY );
933 else
934 m_panDirection.y = 0;
935
936 bool borderHit = ( m_panDirection.x != 0 || m_panDirection.y != 0 );
937
938 switch( m_state )
939 {
940 case AUTO_PANNING:
941 if( !borderHit )
942 {
943 m_panTimer.Stop();
944 setState( IDLE );
945
946 return false;
947 }
948
949 return true;
950
951 case IDLE:
952 if( borderHit )
953 {
955 m_panTimer.Start( (int) ( 250.0 / 60.0 ), true );
956
957 return true;
958 }
959
960 return false;
961
962 case DRAG_PANNING:
963 case DRAG_ZOOMING:
964 return false;
965 }
966
967 wxCHECK_MSG( false, false, wxT( "This line should never be reached" ) );
968
969 return false;
970}
971
972
974{
976 {
977 bool warp = false;
978 wxSize parentSize = m_parentPanel->GetClientSize();
979
980 if( x < 0 )
981 {
982 x = 0;
983 warp = true;
984 }
985 else if( x >= parentSize.x )
986 {
987 x = parentSize.x - 1;
988 warp = true;
989 }
990
991 if( y < 0 )
992 {
993 y = 0;
994 warp = true;
995 }
996 else if( y >= parentSize.y )
997 {
998 y = parentSize.y - 1;
999 warp = true;
1000 }
1001
1002 if( warp )
1004 }
1005}
1006
1007
1008void WX_VIEW_CONTROLS::refreshMouse( bool aSetModifiers )
1009{
1010 // Notify tools that the cursor position has changed in the world coordinates
1011 wxMouseEvent moveEvent( EVT_REFRESH_MOUSE );
1012 wxPoint msp = getMouseScreenPosition();
1013 moveEvent.SetX( msp.x );
1014 moveEvent.SetY( msp.y );
1015
1016 if( aSetModifiers )
1017 {
1018 // Set the modifiers state
1019 moveEvent.SetControlDown( wxGetKeyState( WXK_CONTROL ) );
1020 moveEvent.SetShiftDown( wxGetKeyState( WXK_SHIFT ) );
1021 moveEvent.SetAltDown( wxGetKeyState( WXK_ALT ) );
1022 }
1023
1024 m_cursorPos = GetClampedCoords( m_view->ToWorld( VECTOR2D( msp.x, msp.y ) ) );
1025 wxPostEvent( m_parentPanel, moveEvent );
1026}
1027
1028
1030{
1031 wxPoint msp = KIPLATFORM::UI::GetMousePosition();
1032 m_parentPanel->ScreenToClient( &msp.x, &msp.y );
1033 return msp;
1034}
1035
1036
1038{
1039 const BOX2D viewport = m_view->GetViewport();
1040 const BOX2D& boundary = m_view->GetBoundary();
1041
1042 m_scrollScale.x = 2e3 / viewport.GetWidth(); // TODO it does not have to be updated so often
1043 m_scrollScale.y = 2e3 / viewport.GetHeight();
1044 VECTOR2I newScroll( ( viewport.Centre().x - boundary.GetLeft() ) * m_scrollScale.x,
1045 ( viewport.Centre().y - boundary.GetTop() ) * m_scrollScale.y );
1046
1047 // We add the width of the scroll bar thumb to the range because the scroll range is given by
1048 // the full bar while the position is given by the left/top position of the thumb
1049 VECTOR2I newRange( m_scrollScale.x * boundary.GetWidth() +
1050 m_parentPanel->GetScrollThumb( wxSB_HORIZONTAL ),
1051 m_scrollScale.y * boundary.GetHeight() +
1052 m_parentPanel->GetScrollThumb( wxSB_VERTICAL ) );
1053
1054 // Flip scroll direction in flipped view
1055 if( m_view->IsMirroredX() )
1056 newScroll.x = ( boundary.GetRight() - viewport.Centre().x ) * m_scrollScale.x;
1057
1058 // Adjust scrollbars only if it is needed. Otherwise there are cases when canvas is continuously
1059 // refreshed (Windows)
1060 if( m_scrollPos != newScroll || newRange.x != m_parentPanel->GetScrollRange( wxSB_HORIZONTAL )
1061 || newRange.y != m_parentPanel->GetScrollRange( wxSB_VERTICAL ) )
1062 {
1063 m_parentPanel->SetScrollbars( 1, 1, newRange.x, newRange.y, newScroll.x, newScroll.y,
1064 true );
1065 m_scrollPos = newScroll;
1066
1067#if !defined( __APPLE__ ) && !defined( WIN32 )
1068 // Trigger a mouse refresh to get the canvas update in GTK (re-draws the scrollbars).
1069 // Note that this causes an infinite loop on OSX and Windows (in certain cases) as it
1070 // generates a paint event.
1071 refreshMouse( false );
1072#endif
1073 }
1074}
1075
1076void WX_VIEW_CONTROLS::ForceCursorPosition( bool aEnabled, const VECTOR2D& aPosition )
1077{
1078 VECTOR2D clampedPosition = GetClampedCoords( aPosition );
1079
1081 m_settings.m_forcedPosition = clampedPosition;
1082}
coord_type GetTop() const
Definition: box2.h:195
coord_type GetHeight() const
Definition: box2.h:189
coord_type GetWidth() const
Definition: box2.h:188
bool Contains(const Vec &aPoint) const
Definition: box2.h:142
Vec Centre() const
Definition: box2.h:71
coord_type GetRight() const
Definition: box2.h:190
coord_type GetLeft() const
Definition: box2.h:194
The base class for create windows for drawing purpose.
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
bool m_MouseCapturedLost
used on wxMSW: true after a wxEVT_MOUSE_CAPTURE_LOST was received false after the mouse is recaptured...
void SetFocus() override
EDA_DRAW_FRAME * GetParentEDAFrame() const
Returns parent EDA_DRAW_FRAME, if available or NULL otherwise.
static constexpr double MAC_SCALE
static constexpr double MSW_SCALE
A suitable (magic) scale factor for Windows systems.
static constexpr double GTK3_SCALE
A suitable (magic) scale factor for Mac systems.
static constexpr double MANUAL_SCALE_FACTOR
Abstract interface for drawing on a 2D-surface.
VECTOR2D GetGridPoint(const VECTOR2D &aPoint) const
For a given point it returns the nearest point belonging to the grid in world coordinates.
bool GetGridSnapping() const
const VECTOR2I & GetScreenPixelSize() const
Return GAL canvas size in pixels.
An interface for classes handling user events controlling the view behavior such as zooming,...
virtual void CaptureCursor(bool aEnabled)
Force the cursor to stay within the drawing panel area.
bool m_cursorWarped
Current VIEW_CONTROLS settings.
bool IsCursorWarpingEnabled() const
VC_SETTINGS m_settings
VIEW * m_view
< Pointer to controlled VIEW.
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition: view.h:68
double GetScale() const
Definition: view.h:271
BOX2D GetViewport() const
Return the current viewport visible area rectangle.
Definition: view.cpp:512
const VECTOR2D & GetCenter() const
Return the center point of this VIEW (in world space coordinates).
Definition: view.h:341
virtual void SetScale(double aScale, VECTOR2D aAnchor={ 0, 0 })
Set the scaling factor, zooming around a given anchor point.
Definition: view.cpp:552
VECTOR2D ToScreen(const VECTOR2D &aCoord, bool aAbsolute=true) const
Convert a world space point/vector to a point/vector in screen space coordinates.
Definition: view.cpp:468
const VECTOR2I & GetScreenPixelSize() const
Return the size of the our rendering area in pixels.
Definition: view.cpp:1191
GAL * GetGAL() const
Return the #GAL this view is using to draw graphical primitives.
Definition: view.h:197
VECTOR2D ToWorld(const VECTOR2D &aCoord, bool aAbsolute=true) const
Converts a screen space point/vector to a point/vector in world space coordinates.
Definition: view.cpp:449
bool IsMirroredX() const
Return true if view is flipped across the X axis.
Definition: view.h:245
const BOX2D & GetBoundary() const
Definition: view.h:300
void SetCenter(const VECTOR2D &aCenter)
Set the center point of the VIEW (i.e.
Definition: view.cpp:578
bool m_updateCursor
Flag to indicate if infinite panning works on this platform.
bool m_infinitePanWorks
A #ZOOM_CONTROLLER that determines zoom steps. This is platform-specific.
void SetCrossHairCursorPosition(const VECTOR2D &aPosition, bool aWarpView) override
void LoadSettings() override
Event that forces mouse move event in the dispatcher (eg.
void setState(STATE aNewState)
Sets the interaction state, simply a internal setter to make it easier to debug changes.
STATE m_state
Panel that is affected by VIEW_CONTROLS.
void CaptureCursor(bool aEnabled) override
Force the cursor to stay within the drawing panel area.
void onScroll(wxScrollWinEvent &aEvent)
wxTimer m_panTimer
Ratio used for scaling world coordinates to scrollbar position.
void onButton(wxMouseEvent &aEvent)
void onEnter(wxMouseEvent &WXUNUSED(aEvent))
void onWheel(wxMouseEvent &aEvent)
Handler functions.
VECTOR2I m_scrollPos
The mouse position when a drag zoom started.
void refreshMouse(bool aSetModifiers)
Send an event to refresh mouse position.
WX_VIEW_CONTROLS(VIEW *aView, EDA_DRAW_PANEL_GAL *aParentPanel)
void PinCursorInsideNonAutoscrollArea(bool aWarpMouseCursor) override
Return the current mouse pointer position.
void SetCursorPosition(const VECTOR2D &aPosition, bool warpView, bool aTriggeredByArrows, long aArrowCommand) override
Move the graphic crosshair cursor to the requested position expressed in world coordinates.
VECTOR2D GetMousePosition(bool aWorldCoordinates=true) const override
Return the current mouse pointer position.
void WarpMouseCursor(const VECTOR2D &aPosition, bool aWorldCoordinates=false, bool aWarpView=false) override
Set the viewport center to the current cursor position and warps the cursor to the screen center.
void onMagnify(wxMouseEvent &aEvent)
VECTOR2D GetRawCursorPosition(bool aSnappingEnabled=true) const override
Return the current cursor position in world coordinates ignoring the cursorUp position force mode.
VECTOR2D m_scrollScale
Current scrollbar position.
bool handleAutoPanning(const wxMouseEvent &aEvent)
Compute new viewport settings while in autopanning mode.
wxPoint getMouseScreenPosition() const
Get the cursor position in the screen coordinates.
void onTimer(wxTimerEvent &WXUNUSED(aEvent))
std::unique_ptr< ZOOM_CONTROLLER > m_zoomController
STATE
< Possible states for WX_VIEW_CONTROLS.
@ DRAG_PANNING
Panning with mouse button pressed.
@ AUTO_PANNING
Panning on approaching borders of the frame.
@ DRAG_ZOOMING
Zooming with mouse button pressed.
@ IDLE
Nothing is happening.
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
void CenterOnCursor() override
Adjusts the scrollbars position to match the current viewport.
VECTOR2D m_cursorPos
Flag deciding whether the cursor position should be calculated using the mouse position.
void onCaptureLost(wxMouseEvent &WXUNUSED(aEvent))
void onMotion(wxMouseEvent &aEvent)
std::unique_ptr< PROF_COUNTER > m_MotionEventCounter
EDA_DRAW_PANEL_GAL * m_parentPanel
Store information about point where dragging has started.
void UpdateScrollbars()
End any mouse drag action still in progress.
void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0)) override
Applies VIEW_CONTROLS settings from the program COMMON_SETTINGS.
void handleCursorCapture(int x, int y)
Limit the cursor position to within the canvas by warping it.
void onLeave(wxMouseEvent &WXUNUSED(aEvent))
VECTOR2D m_zoomStartPoint
Current cursor position (world coordinates).
static const wxEventType EVT_REFRESH_MOUSE
VECTOR2D m_panDirection
Timer responsible for handling autopanning.
VECTOR2D m_dragStartPoint
Current direction of panning (only autopanning mode).
KIWAY & Kiway() const
Return a reference to the KIWAY that this object has an opportunity to participate in.
Definition: kiway_holder.h:55
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
Definition: kiway_player.h:67
virtual KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=nullptr)
Return the KIWAY_PLAYER* given a FRAME_T.
Definition: kiway.cpp:432
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition: pgm_base.cpp:650
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:265
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition: vector2d.h:350
@ FRAME_PCB_EDITOR
Definition: frame_type.h:42
@ FRAME_SCH
Definition: frame_type.h:34
a few functions useful in geometry calculations.
VECTOR2< ret_type > GetClampedCoords(const VECTOR2< in_type > &aCoords, pad_type aPadding=1u)
Clamps a vector to values that can be negated, respecting numeric limits of coordinates data type wit...
const wxChar *const traceZoomScroll
Flag to enable debug output of zoom-scrolling calculations in KIGFX::ZOOM_CONTROLLER and derivatives.
The Cairo implementation of the graphics abstraction layer.
Definition: color4d.cpp:247
wxPoint GetMousePosition()
Returns the mouse position in screen coordinates.
Definition: gtk/ui.cpp:601
void InfiniteDragReleaseWindow()
On Wayland, allows the cursor to freely move again after a drag (see InfiniteDragPrepareWindow).
Definition: gtk/ui.cpp:595
bool WarpPointer(wxWindow *aWindow, int aX, int aY)
Move the mouse cursor to a specific position relative to the window.
Definition: gtk/ui.cpp:253
bool IsWindowActive(wxWindow *aWindow)
Check to see if the given window is the currently active window (e.g.
Definition: gtk/ui.cpp:73
bool InfiniteDragPrepareWindow(wxWindow *aWindow)
On Wayland, restricts the pointer movement to a rectangle slightly bigger than the given wxWindow.
Definition: gtk/ui.cpp:588
KICOMMON_API bool IsInputControlFocused(wxWindow *aFocus=nullptr)
Check if a input control has focus.
Definition: ui_common.cpp:260
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: pgm_base.cpp:1031
see class PGM_BASE
const int scale
MOUSE_DRAG_ACTION drag_right
MOUSE_DRAG_ACTION drag_middle
MOUSE_DRAG_ACTION drag_left
float m_autoPanMargin
How fast is panning when in auto mode.
Definition: view_controls.h:80
VECTOR2D m_forcedPosition
Is the forced cursor position enabled.
Definition: view_controls.h:56
MOUSE_DRAG_ACTION m_dragLeft
bool m_horizontalPan
Enable the accelerating zoom controller.
Definition: view_controls.h:92
bool m_autoPanSettingEnabled
Distance from cursor to VIEW edge when panning is active.
Definition: view_controls.h:77
bool m_focusFollowSchPcb
Flag for turning on autopanning.
Definition: view_controls.h:71
float m_autoPanAcceleration
If the cursor is allowed to be warped.
Definition: view_controls.h:86
MOUSE_DRAG_ACTION m_dragMiddle
bool m_cursorCaptured
Should the cursor snap to grid or move freely.
Definition: view_controls.h:62
int m_zoomSpeed
When true, ignore zoom_speed and pick a platform-specific default.
Definition: view_controls.h:98
int m_scrollModifierZoom
What modifier key to enable horizontal pan with the (vertical) scroll wheel.
int m_scrollModifierPanH
What modifier key to enable vertical with the (vertical) scroll wheel.
VECTOR2D m_lastKeyboardCursorPosition
Wether to invert the scroll wheel movement for horizontal pan.
bool m_warpCursor
Enable horizontal panning with the horizontal scroll/trackpad input.
Definition: view_controls.h:89
MOUSE_DRAG_ACTION m_dragRight
Is last cursor motion event coming from keyboard arrow cursor motion action.
bool m_zoomAcceleration
Zoom speed for the non-accelerating zoom controller.
Definition: view_controls.h:95
bool m_lastKeyboardCursorPositionValid
ACTIONS::CURSOR_UP, ACTIONS::CURSOR_DOWN, etc.
bool m_zoomSpeedAuto
What modifier key to enable zoom with the (vertical) scroll wheel.
bool m_autoPanEnabled
Flag for turning on autopanning.
Definition: view_controls.h:74
long m_lastKeyboardCursorCommand
Position of the above event.
bool m_forceCursorPosition
Should the cursor be locked within the parent window area.
Definition: view_controls.h:59
constexpr int delta
wxLogTrace helper definitions.
Functions to provide common constants and other functions to assist in making a consistent UI.
VECTOR2< double > VECTOR2D
Definition: vector2d.h:587
VECTOR2< int > VECTOR2I
Definition: vector2d.h:588
static std::unique_ptr< ZOOM_CONTROLLER > GetZoomControllerForPlatform(bool aAcceleration)
WX_VIEW_CONTROLS class definition.
ZOOM_CONTROLLER class definition.